1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2017-2023, STMicroelectronics - All Rights Reserved 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <errno.h> 8*91f16700Schasinglulu 9*91f16700Schasinglulu #include <arch_helpers.h> 10*91f16700Schasinglulu #include <common/fdt_wrappers.h> 11*91f16700Schasinglulu #include <drivers/clk.h> 12*91f16700Schasinglulu #include <drivers/generic_delay_timer.h> 13*91f16700Schasinglulu #include <drivers/st/stm32_gpio.h> 14*91f16700Schasinglulu #include <drivers/st/stm32mp_clkfunc.h> 15*91f16700Schasinglulu #include <lib/mmio.h> 16*91f16700Schasinglulu #include <libfdt.h> 17*91f16700Schasinglulu 18*91f16700Schasinglulu #include <platform_def.h> 19*91f16700Schasinglulu 20*91f16700Schasinglulu /* 21*91f16700Schasinglulu * Get the frequency of an oscillator from its name in device tree. 22*91f16700Schasinglulu * @param name: oscillator name 23*91f16700Schasinglulu * @param freq: stores the frequency of the oscillator 24*91f16700Schasinglulu * @return: 0 on success, and a negative FDT/ERRNO error code on failure. 25*91f16700Schasinglulu */ 26*91f16700Schasinglulu int fdt_osc_read_freq(const char *name, uint32_t *freq) 27*91f16700Schasinglulu { 28*91f16700Schasinglulu int node, subnode; 29*91f16700Schasinglulu void *fdt; 30*91f16700Schasinglulu 31*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 32*91f16700Schasinglulu return -ENOENT; 33*91f16700Schasinglulu } 34*91f16700Schasinglulu 35*91f16700Schasinglulu node = fdt_path_offset(fdt, "/clocks"); 36*91f16700Schasinglulu if (node < 0) { 37*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 38*91f16700Schasinglulu } 39*91f16700Schasinglulu 40*91f16700Schasinglulu fdt_for_each_subnode(subnode, fdt, node) { 41*91f16700Schasinglulu const char *cchar; 42*91f16700Schasinglulu int ret; 43*91f16700Schasinglulu 44*91f16700Schasinglulu cchar = fdt_get_name(fdt, subnode, &ret); 45*91f16700Schasinglulu if (cchar == NULL) { 46*91f16700Schasinglulu return ret; 47*91f16700Schasinglulu } 48*91f16700Schasinglulu 49*91f16700Schasinglulu if ((strncmp(cchar, name, (size_t)ret) == 0) && 50*91f16700Schasinglulu (fdt_get_status(subnode) != DT_DISABLED)) { 51*91f16700Schasinglulu const fdt32_t *cuint; 52*91f16700Schasinglulu 53*91f16700Schasinglulu cuint = fdt_getprop(fdt, subnode, "clock-frequency", 54*91f16700Schasinglulu &ret); 55*91f16700Schasinglulu if (cuint == NULL) { 56*91f16700Schasinglulu return ret; 57*91f16700Schasinglulu } 58*91f16700Schasinglulu 59*91f16700Schasinglulu *freq = fdt32_to_cpu(*cuint); 60*91f16700Schasinglulu 61*91f16700Schasinglulu return 0; 62*91f16700Schasinglulu } 63*91f16700Schasinglulu } 64*91f16700Schasinglulu 65*91f16700Schasinglulu /* Oscillator not found, freq=0 */ 66*91f16700Schasinglulu *freq = 0; 67*91f16700Schasinglulu return 0; 68*91f16700Schasinglulu } 69*91f16700Schasinglulu 70*91f16700Schasinglulu /* 71*91f16700Schasinglulu * Check the presence of an oscillator property from its id. 72*91f16700Schasinglulu * @param node_label: clock node name 73*91f16700Schasinglulu * @param prop_name: property name 74*91f16700Schasinglulu * @return: true/false regarding search result. 75*91f16700Schasinglulu */ 76*91f16700Schasinglulu bool fdt_clk_read_bool(const char *node_label, const char *prop_name) 77*91f16700Schasinglulu { 78*91f16700Schasinglulu int node, subnode; 79*91f16700Schasinglulu void *fdt; 80*91f16700Schasinglulu 81*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 82*91f16700Schasinglulu return false; 83*91f16700Schasinglulu } 84*91f16700Schasinglulu 85*91f16700Schasinglulu node = fdt_path_offset(fdt, "/clocks"); 86*91f16700Schasinglulu if (node < 0) { 87*91f16700Schasinglulu return false; 88*91f16700Schasinglulu } 89*91f16700Schasinglulu 90*91f16700Schasinglulu fdt_for_each_subnode(subnode, fdt, node) { 91*91f16700Schasinglulu const char *cchar; 92*91f16700Schasinglulu int ret; 93*91f16700Schasinglulu 94*91f16700Schasinglulu cchar = fdt_get_name(fdt, subnode, &ret); 95*91f16700Schasinglulu if (cchar == NULL) { 96*91f16700Schasinglulu return false; 97*91f16700Schasinglulu } 98*91f16700Schasinglulu 99*91f16700Schasinglulu if (strncmp(cchar, node_label, (size_t)ret) != 0) { 100*91f16700Schasinglulu continue; 101*91f16700Schasinglulu } 102*91f16700Schasinglulu 103*91f16700Schasinglulu if (fdt_getprop(fdt, subnode, prop_name, NULL) != NULL) { 104*91f16700Schasinglulu return true; 105*91f16700Schasinglulu } 106*91f16700Schasinglulu } 107*91f16700Schasinglulu 108*91f16700Schasinglulu return false; 109*91f16700Schasinglulu } 110*91f16700Schasinglulu 111*91f16700Schasinglulu /* 112*91f16700Schasinglulu * Get the value of a oscillator property from its name. 113*91f16700Schasinglulu * @param node_label: oscillator name 114*91f16700Schasinglulu * @param prop_name: property name 115*91f16700Schasinglulu * @param dflt_value: default value 116*91f16700Schasinglulu * @return oscillator value on success, default value if property not found. 117*91f16700Schasinglulu */ 118*91f16700Schasinglulu uint32_t fdt_clk_read_uint32_default(const char *node_label, 119*91f16700Schasinglulu const char *prop_name, uint32_t dflt_value) 120*91f16700Schasinglulu { 121*91f16700Schasinglulu int node, subnode; 122*91f16700Schasinglulu void *fdt; 123*91f16700Schasinglulu 124*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 125*91f16700Schasinglulu return dflt_value; 126*91f16700Schasinglulu } 127*91f16700Schasinglulu 128*91f16700Schasinglulu node = fdt_path_offset(fdt, "/clocks"); 129*91f16700Schasinglulu if (node < 0) { 130*91f16700Schasinglulu return dflt_value; 131*91f16700Schasinglulu } 132*91f16700Schasinglulu 133*91f16700Schasinglulu fdt_for_each_subnode(subnode, fdt, node) { 134*91f16700Schasinglulu const char *cchar; 135*91f16700Schasinglulu int ret; 136*91f16700Schasinglulu 137*91f16700Schasinglulu cchar = fdt_get_name(fdt, subnode, &ret); 138*91f16700Schasinglulu if (cchar == NULL) { 139*91f16700Schasinglulu return dflt_value; 140*91f16700Schasinglulu } 141*91f16700Schasinglulu 142*91f16700Schasinglulu if (strncmp(cchar, node_label, (size_t)ret) != 0) { 143*91f16700Schasinglulu continue; 144*91f16700Schasinglulu } 145*91f16700Schasinglulu 146*91f16700Schasinglulu return fdt_read_uint32_default(fdt, subnode, prop_name, 147*91f16700Schasinglulu dflt_value); 148*91f16700Schasinglulu } 149*91f16700Schasinglulu 150*91f16700Schasinglulu return dflt_value; 151*91f16700Schasinglulu } 152*91f16700Schasinglulu 153*91f16700Schasinglulu /* 154*91f16700Schasinglulu * Get the RCC node offset from the device tree 155*91f16700Schasinglulu * @param fdt: Device tree reference 156*91f16700Schasinglulu * @return: Node offset or a negative value on error 157*91f16700Schasinglulu */ 158*91f16700Schasinglulu static int fdt_get_rcc_node(void *fdt) 159*91f16700Schasinglulu { 160*91f16700Schasinglulu static int node; 161*91f16700Schasinglulu 162*91f16700Schasinglulu if (node <= 0) { 163*91f16700Schasinglulu node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); 164*91f16700Schasinglulu } 165*91f16700Schasinglulu 166*91f16700Schasinglulu return node; 167*91f16700Schasinglulu } 168*91f16700Schasinglulu 169*91f16700Schasinglulu /* 170*91f16700Schasinglulu * Read a series of parameters in rcc-clk section in device tree 171*91f16700Schasinglulu * @param prop_name: Name of the RCC property to be read 172*91f16700Schasinglulu * @param array: the array to store the property parameters 173*91f16700Schasinglulu * @param count: number of parameters to be read 174*91f16700Schasinglulu * @return: 0 on succes or a negative value on error 175*91f16700Schasinglulu */ 176*91f16700Schasinglulu int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count, 177*91f16700Schasinglulu uint32_t *array) 178*91f16700Schasinglulu { 179*91f16700Schasinglulu int node; 180*91f16700Schasinglulu void *fdt; 181*91f16700Schasinglulu 182*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 183*91f16700Schasinglulu return -ENOENT; 184*91f16700Schasinglulu } 185*91f16700Schasinglulu 186*91f16700Schasinglulu node = fdt_get_rcc_node(fdt); 187*91f16700Schasinglulu if (node < 0) { 188*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 189*91f16700Schasinglulu } 190*91f16700Schasinglulu 191*91f16700Schasinglulu return fdt_read_uint32_array(fdt, node, prop_name, count, array); 192*91f16700Schasinglulu } 193*91f16700Schasinglulu 194*91f16700Schasinglulu /* 195*91f16700Schasinglulu * Get the subnode offset in rcc-clk section from its name in device tree 196*91f16700Schasinglulu * @param name: name of the RCC property 197*91f16700Schasinglulu * @return: offset on success, and a negative FDT/ERRNO error code on failure. 198*91f16700Schasinglulu */ 199*91f16700Schasinglulu int fdt_rcc_subnode_offset(const char *name) 200*91f16700Schasinglulu { 201*91f16700Schasinglulu int node, subnode; 202*91f16700Schasinglulu void *fdt; 203*91f16700Schasinglulu 204*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 205*91f16700Schasinglulu return -ENOENT; 206*91f16700Schasinglulu } 207*91f16700Schasinglulu 208*91f16700Schasinglulu node = fdt_get_rcc_node(fdt); 209*91f16700Schasinglulu if (node < 0) { 210*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 211*91f16700Schasinglulu } 212*91f16700Schasinglulu 213*91f16700Schasinglulu subnode = fdt_subnode_offset(fdt, node, name); 214*91f16700Schasinglulu if (subnode <= 0) { 215*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 216*91f16700Schasinglulu } 217*91f16700Schasinglulu 218*91f16700Schasinglulu return subnode; 219*91f16700Schasinglulu } 220*91f16700Schasinglulu 221*91f16700Schasinglulu /* 222*91f16700Schasinglulu * Get the pointer to a rcc-clk property from its name. 223*91f16700Schasinglulu * @param name: name of the RCC property 224*91f16700Schasinglulu * @param lenp: stores the length of the property. 225*91f16700Schasinglulu * @return: pointer to the property on success, and NULL value on failure. 226*91f16700Schasinglulu */ 227*91f16700Schasinglulu const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp) 228*91f16700Schasinglulu { 229*91f16700Schasinglulu const fdt32_t *cuint; 230*91f16700Schasinglulu int node, len; 231*91f16700Schasinglulu void *fdt; 232*91f16700Schasinglulu 233*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 234*91f16700Schasinglulu return NULL; 235*91f16700Schasinglulu } 236*91f16700Schasinglulu 237*91f16700Schasinglulu node = fdt_get_rcc_node(fdt); 238*91f16700Schasinglulu if (node < 0) { 239*91f16700Schasinglulu return NULL; 240*91f16700Schasinglulu } 241*91f16700Schasinglulu 242*91f16700Schasinglulu cuint = fdt_getprop(fdt, node, prop_name, &len); 243*91f16700Schasinglulu if (cuint == NULL) { 244*91f16700Schasinglulu return NULL; 245*91f16700Schasinglulu } 246*91f16700Schasinglulu 247*91f16700Schasinglulu *lenp = len; 248*91f16700Schasinglulu return cuint; 249*91f16700Schasinglulu } 250*91f16700Schasinglulu 251*91f16700Schasinglulu #if defined(IMAGE_BL32) 252*91f16700Schasinglulu /* 253*91f16700Schasinglulu * Get the secure state for rcc node in device tree. 254*91f16700Schasinglulu * @return: true if rcc is configured for secure world access, false if not. 255*91f16700Schasinglulu */ 256*91f16700Schasinglulu bool fdt_get_rcc_secure_state(void) 257*91f16700Schasinglulu { 258*91f16700Schasinglulu void *fdt; 259*91f16700Schasinglulu 260*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 261*91f16700Schasinglulu return false; 262*91f16700Schasinglulu } 263*91f16700Schasinglulu 264*91f16700Schasinglulu if (fdt_node_offset_by_compatible(fdt, -1, DT_RCC_SEC_CLK_COMPAT) < 0) { 265*91f16700Schasinglulu return false; 266*91f16700Schasinglulu } 267*91f16700Schasinglulu 268*91f16700Schasinglulu return true; 269*91f16700Schasinglulu } 270*91f16700Schasinglulu #endif 271*91f16700Schasinglulu 272*91f16700Schasinglulu /* 273*91f16700Schasinglulu * Get the clock ID of the given node in device tree. 274*91f16700Schasinglulu * @param node: node offset 275*91f16700Schasinglulu * @return: Clock ID on success, and a negative FDT/ERRNO error code on failure. 276*91f16700Schasinglulu */ 277*91f16700Schasinglulu int fdt_get_clock_id(int node) 278*91f16700Schasinglulu { 279*91f16700Schasinglulu const fdt32_t *cuint; 280*91f16700Schasinglulu void *fdt; 281*91f16700Schasinglulu 282*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 283*91f16700Schasinglulu return -ENOENT; 284*91f16700Schasinglulu } 285*91f16700Schasinglulu 286*91f16700Schasinglulu cuint = fdt_getprop(fdt, node, "clocks", NULL); 287*91f16700Schasinglulu if (cuint == NULL) { 288*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 289*91f16700Schasinglulu } 290*91f16700Schasinglulu 291*91f16700Schasinglulu cuint++; 292*91f16700Schasinglulu return (int)fdt32_to_cpu(*cuint); 293*91f16700Schasinglulu } 294*91f16700Schasinglulu 295*91f16700Schasinglulu /* 296*91f16700Schasinglulu * Get the frequency of the specified UART instance. 297*91f16700Schasinglulu * @param instance: UART interface registers base address. 298*91f16700Schasinglulu * @return: clock frequency on success, 0 value on failure. 299*91f16700Schasinglulu */ 300*91f16700Schasinglulu unsigned long fdt_get_uart_clock_freq(uintptr_t instance) 301*91f16700Schasinglulu { 302*91f16700Schasinglulu void *fdt; 303*91f16700Schasinglulu int node; 304*91f16700Schasinglulu int clk_id; 305*91f16700Schasinglulu 306*91f16700Schasinglulu if (fdt_get_address(&fdt) == 0) { 307*91f16700Schasinglulu return 0UL; 308*91f16700Schasinglulu } 309*91f16700Schasinglulu 310*91f16700Schasinglulu /* Check for UART nodes */ 311*91f16700Schasinglulu node = dt_match_instance_by_compatible(DT_UART_COMPAT, instance); 312*91f16700Schasinglulu if (node < 0) { 313*91f16700Schasinglulu return 0UL; 314*91f16700Schasinglulu } 315*91f16700Schasinglulu 316*91f16700Schasinglulu clk_id = fdt_get_clock_id(node); 317*91f16700Schasinglulu if (clk_id < 0) { 318*91f16700Schasinglulu return 0UL; 319*91f16700Schasinglulu } 320*91f16700Schasinglulu 321*91f16700Schasinglulu return clk_get_rate((unsigned long)clk_id); 322*91f16700Schasinglulu } 323*91f16700Schasinglulu 324*91f16700Schasinglulu /******************************************************************************* 325*91f16700Schasinglulu * This function sets the STGEN counter value. 326*91f16700Schasinglulu ******************************************************************************/ 327*91f16700Schasinglulu static void stgen_set_counter(unsigned long long counter) 328*91f16700Schasinglulu { 329*91f16700Schasinglulu #ifdef __aarch64__ 330*91f16700Schasinglulu mmio_write_64(STGEN_BASE + CNTCV_OFF, counter); 331*91f16700Schasinglulu #else 332*91f16700Schasinglulu mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter); 333*91f16700Schasinglulu mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32)); 334*91f16700Schasinglulu #endif 335*91f16700Schasinglulu } 336*91f16700Schasinglulu 337*91f16700Schasinglulu /******************************************************************************* 338*91f16700Schasinglulu * This function configures and restores the STGEN counter depending on the 339*91f16700Schasinglulu * connected clock. 340*91f16700Schasinglulu ******************************************************************************/ 341*91f16700Schasinglulu void stm32mp_stgen_config(unsigned long rate) 342*91f16700Schasinglulu { 343*91f16700Schasinglulu uint32_t cntfid0; 344*91f16700Schasinglulu unsigned long long counter; 345*91f16700Schasinglulu 346*91f16700Schasinglulu cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF); 347*91f16700Schasinglulu 348*91f16700Schasinglulu if (cntfid0 == rate) { 349*91f16700Schasinglulu return; 350*91f16700Schasinglulu } 351*91f16700Schasinglulu 352*91f16700Schasinglulu mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 353*91f16700Schasinglulu counter = stm32mp_stgen_get_counter() * rate / cntfid0; 354*91f16700Schasinglulu 355*91f16700Schasinglulu stgen_set_counter(counter); 356*91f16700Schasinglulu mmio_write_32(STGEN_BASE + CNTFID_OFF, rate); 357*91f16700Schasinglulu mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 358*91f16700Schasinglulu 359*91f16700Schasinglulu write_cntfrq_el0(rate); 360*91f16700Schasinglulu 361*91f16700Schasinglulu /* Need to update timer with new frequency */ 362*91f16700Schasinglulu generic_delay_timer_init(); 363*91f16700Schasinglulu } 364*91f16700Schasinglulu 365*91f16700Schasinglulu /******************************************************************************* 366*91f16700Schasinglulu * This function returns the STGEN counter value. 367*91f16700Schasinglulu ******************************************************************************/ 368*91f16700Schasinglulu unsigned long long stm32mp_stgen_get_counter(void) 369*91f16700Schasinglulu { 370*91f16700Schasinglulu #ifdef __aarch64__ 371*91f16700Schasinglulu return mmio_read_64(STGEN_BASE + CNTCV_OFF); 372*91f16700Schasinglulu #else 373*91f16700Schasinglulu return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) | 374*91f16700Schasinglulu mmio_read_32(STGEN_BASE + CNTCVL_OFF)); 375*91f16700Schasinglulu #endif 376*91f16700Schasinglulu } 377*91f16700Schasinglulu 378*91f16700Schasinglulu /******************************************************************************* 379*91f16700Schasinglulu * This function restores the STGEN counter value. 380*91f16700Schasinglulu * It takes a first input value as a counter backup value to be restored and a 381*91f16700Schasinglulu * offset in ms to be added. 382*91f16700Schasinglulu ******************************************************************************/ 383*91f16700Schasinglulu void stm32mp_stgen_restore_counter(unsigned long long value, 384*91f16700Schasinglulu unsigned long long offset_in_ms) 385*91f16700Schasinglulu { 386*91f16700Schasinglulu unsigned long long cnt; 387*91f16700Schasinglulu 388*91f16700Schasinglulu cnt = value + ((offset_in_ms * 389*91f16700Schasinglulu mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U); 390*91f16700Schasinglulu 391*91f16700Schasinglulu mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 392*91f16700Schasinglulu stgen_set_counter(cnt); 393*91f16700Schasinglulu mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 394*91f16700Schasinglulu } 395