1447b2b13SYann Gautier /* 2bcccdaccSPatrick Delaunay * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved 3447b2b13SYann Gautier * 4447b2b13SYann Gautier * SPDX-License-Identifier: BSD-3-Clause 5447b2b13SYann Gautier */ 6447b2b13SYann Gautier 7447b2b13SYann Gautier #include <errno.h> 8447b2b13SYann Gautier 9591d80c8SLionel Debieve #include <arch_helpers.h> 1052a616b4SAndre Przywara #include <common/fdt_wrappers.h> 1133667d29SYann Gautier #include <drivers/clk.h> 12591d80c8SLionel Debieve #include <drivers/generic_delay_timer.h> 13447b2b13SYann Gautier #include <drivers/st/stm32_gpio.h> 14447b2b13SYann Gautier #include <drivers/st/stm32mp_clkfunc.h> 15591d80c8SLionel Debieve #include <lib/mmio.h> 16bcccdaccSPatrick Delaunay #include <libfdt.h> 17bcccdaccSPatrick Delaunay 18bcccdaccSPatrick Delaunay #include <platform_def.h> 19447b2b13SYann Gautier 20165ad556SNicolas Le Bayon #define DT_UART_COMPAT "st,stm32h7-uart" 21447b2b13SYann Gautier /* 22f66358afSYann Gautier * Get the frequency of an oscillator from its name in device tree. 23f66358afSYann Gautier * @param name: oscillator name 24f66358afSYann Gautier * @param freq: stores the frequency of the oscillator 25f66358afSYann Gautier * @return: 0 on success, and a negative FDT/ERRNO error code on failure. 26f66358afSYann Gautier */ 27f66358afSYann Gautier int fdt_osc_read_freq(const char *name, uint32_t *freq) 28f66358afSYann Gautier { 29f66358afSYann Gautier int node, subnode; 30f66358afSYann Gautier void *fdt; 31f66358afSYann Gautier 32f66358afSYann Gautier if (fdt_get_address(&fdt) == 0) { 33f66358afSYann Gautier return -ENOENT; 34f66358afSYann Gautier } 35f66358afSYann Gautier 36f66358afSYann Gautier node = fdt_path_offset(fdt, "/clocks"); 37f66358afSYann Gautier if (node < 0) { 38f66358afSYann Gautier return -FDT_ERR_NOTFOUND; 39f66358afSYann Gautier } 40f66358afSYann Gautier 41f66358afSYann Gautier fdt_for_each_subnode(subnode, fdt, node) { 42f66358afSYann Gautier const char *cchar; 43f66358afSYann Gautier int ret; 44f66358afSYann Gautier 45f66358afSYann Gautier cchar = fdt_get_name(fdt, subnode, &ret); 46f66358afSYann Gautier if (cchar == NULL) { 47f66358afSYann Gautier return ret; 48f66358afSYann Gautier } 49f66358afSYann Gautier 50bcccdaccSPatrick Delaunay if ((strncmp(cchar, name, (size_t)ret) == 0) && 51bcccdaccSPatrick Delaunay (fdt_get_status(subnode) != DT_DISABLED)) { 52f66358afSYann Gautier const fdt32_t *cuint; 53f66358afSYann Gautier 54f66358afSYann Gautier cuint = fdt_getprop(fdt, subnode, "clock-frequency", 55f66358afSYann Gautier &ret); 56f66358afSYann Gautier if (cuint == NULL) { 57f66358afSYann Gautier return ret; 58f66358afSYann Gautier } 59f66358afSYann Gautier 60f66358afSYann Gautier *freq = fdt32_to_cpu(*cuint); 61f66358afSYann Gautier 62f66358afSYann Gautier return 0; 63f66358afSYann Gautier } 64f66358afSYann Gautier } 65f66358afSYann Gautier 66f66358afSYann Gautier /* Oscillator not found, freq=0 */ 67f66358afSYann Gautier *freq = 0; 68f66358afSYann Gautier return 0; 69f66358afSYann Gautier } 70f66358afSYann Gautier 71f66358afSYann Gautier /* 72f66358afSYann Gautier * Check the presence of an oscillator property from its id. 73b208e3daSGabriel Fernandez * @param node_label: clock node name 74f66358afSYann Gautier * @param prop_name: property name 75f66358afSYann Gautier * @return: true/false regarding search result. 76f66358afSYann Gautier */ 77b208e3daSGabriel Fernandez bool fdt_clk_read_bool(const char *node_label, const char *prop_name) 78f66358afSYann Gautier { 79f66358afSYann Gautier int node, subnode; 80f66358afSYann Gautier void *fdt; 81f66358afSYann Gautier 82f66358afSYann Gautier if (fdt_get_address(&fdt) == 0) { 83f66358afSYann Gautier return false; 84f66358afSYann Gautier } 85f66358afSYann Gautier 86f66358afSYann Gautier node = fdt_path_offset(fdt, "/clocks"); 87f66358afSYann Gautier if (node < 0) { 88f66358afSYann Gautier return false; 89f66358afSYann Gautier } 90f66358afSYann Gautier 91f66358afSYann Gautier fdt_for_each_subnode(subnode, fdt, node) { 92f66358afSYann Gautier const char *cchar; 93f66358afSYann Gautier int ret; 94f66358afSYann Gautier 95f66358afSYann Gautier cchar = fdt_get_name(fdt, subnode, &ret); 96f66358afSYann Gautier if (cchar == NULL) { 97f66358afSYann Gautier return false; 98f66358afSYann Gautier } 99f66358afSYann Gautier 100b208e3daSGabriel Fernandez if (strncmp(cchar, node_label, (size_t)ret) != 0) { 101f66358afSYann Gautier continue; 102f66358afSYann Gautier } 103f66358afSYann Gautier 104f66358afSYann Gautier if (fdt_getprop(fdt, subnode, prop_name, NULL) != NULL) { 105f66358afSYann Gautier return true; 106f66358afSYann Gautier } 107f66358afSYann Gautier } 108f66358afSYann Gautier 109f66358afSYann Gautier return false; 110f66358afSYann Gautier } 111f66358afSYann Gautier 112f66358afSYann Gautier /* 113b208e3daSGabriel Fernandez * Get the value of a oscillator property from its name. 114b208e3daSGabriel Fernandez * @param node_label: oscillator name 115f66358afSYann Gautier * @param prop_name: property name 116f66358afSYann Gautier * @param dflt_value: default value 117f66358afSYann Gautier * @return oscillator value on success, default value if property not found. 118f66358afSYann Gautier */ 119b208e3daSGabriel Fernandez uint32_t fdt_clk_read_uint32_default(const char *node_label, 120f66358afSYann Gautier const char *prop_name, uint32_t dflt_value) 121f66358afSYann Gautier { 122f66358afSYann Gautier int node, subnode; 123f66358afSYann Gautier void *fdt; 124f66358afSYann Gautier 125f66358afSYann Gautier if (fdt_get_address(&fdt) == 0) { 126f66358afSYann Gautier return dflt_value; 127f66358afSYann Gautier } 128f66358afSYann Gautier 129f66358afSYann Gautier node = fdt_path_offset(fdt, "/clocks"); 130f66358afSYann Gautier if (node < 0) { 131f66358afSYann Gautier return dflt_value; 132f66358afSYann Gautier } 133f66358afSYann Gautier 134f66358afSYann Gautier fdt_for_each_subnode(subnode, fdt, node) { 135f66358afSYann Gautier const char *cchar; 136f66358afSYann Gautier int ret; 137f66358afSYann Gautier 138f66358afSYann Gautier cchar = fdt_get_name(fdt, subnode, &ret); 139f66358afSYann Gautier if (cchar == NULL) { 140f66358afSYann Gautier return dflt_value; 141f66358afSYann Gautier } 142f66358afSYann Gautier 143b208e3daSGabriel Fernandez if (strncmp(cchar, node_label, (size_t)ret) != 0) { 144f66358afSYann Gautier continue; 145f66358afSYann Gautier } 146f66358afSYann Gautier 147be858cffSAndre Przywara return fdt_read_uint32_default(fdt, subnode, prop_name, 148be858cffSAndre Przywara dflt_value); 149f66358afSYann Gautier } 150f66358afSYann Gautier 151f66358afSYann Gautier return dflt_value; 152f66358afSYann Gautier } 153f66358afSYann Gautier 154f66358afSYann Gautier /* 155447b2b13SYann Gautier * Get the RCC node offset from the device tree 156447b2b13SYann Gautier * @param fdt: Device tree reference 157447b2b13SYann Gautier * @return: Node offset or a negative value on error 158447b2b13SYann Gautier */ 159ff18c4cdSPatrick Delaunay static int fdt_get_rcc_node(void *fdt) 160447b2b13SYann Gautier { 161ba57711cSYann Gautier static int node; 162ba57711cSYann Gautier 163ba57711cSYann Gautier if (node <= 0) { 164ba57711cSYann Gautier node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); 165ba57711cSYann Gautier } 166ba57711cSYann Gautier 167ba57711cSYann Gautier return node; 168447b2b13SYann Gautier } 169447b2b13SYann Gautier 170447b2b13SYann Gautier /* 171447b2b13SYann Gautier * Read a series of parameters in rcc-clk section in device tree 172447b2b13SYann Gautier * @param prop_name: Name of the RCC property to be read 173447b2b13SYann Gautier * @param array: the array to store the property parameters 174447b2b13SYann Gautier * @param count: number of parameters to be read 175447b2b13SYann Gautier * @return: 0 on succes or a negative value on error 176447b2b13SYann Gautier */ 17752a616b4SAndre Przywara int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count, 17852a616b4SAndre Przywara uint32_t *array) 179447b2b13SYann Gautier { 180447b2b13SYann Gautier int node; 181447b2b13SYann Gautier void *fdt; 182447b2b13SYann Gautier 183447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) { 184447b2b13SYann Gautier return -ENOENT; 185447b2b13SYann Gautier } 186447b2b13SYann Gautier 187447b2b13SYann Gautier node = fdt_get_rcc_node(fdt); 188447b2b13SYann Gautier if (node < 0) { 189447b2b13SYann Gautier return -FDT_ERR_NOTFOUND; 190447b2b13SYann Gautier } 191447b2b13SYann Gautier 19252a616b4SAndre Przywara return fdt_read_uint32_array(fdt, node, prop_name, count, array); 193447b2b13SYann Gautier } 194447b2b13SYann Gautier 195447b2b13SYann Gautier /* 196447b2b13SYann Gautier * Get the subnode offset in rcc-clk section from its name in device tree 197447b2b13SYann Gautier * @param name: name of the RCC property 198447b2b13SYann Gautier * @return: offset on success, and a negative FDT/ERRNO error code on failure. 199447b2b13SYann Gautier */ 200447b2b13SYann Gautier int fdt_rcc_subnode_offset(const char *name) 201447b2b13SYann Gautier { 202447b2b13SYann Gautier int node, subnode; 203447b2b13SYann Gautier void *fdt; 204447b2b13SYann Gautier 205447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) { 206447b2b13SYann Gautier return -ENOENT; 207447b2b13SYann Gautier } 208447b2b13SYann Gautier 209447b2b13SYann Gautier node = fdt_get_rcc_node(fdt); 210447b2b13SYann Gautier if (node < 0) { 211447b2b13SYann Gautier return -FDT_ERR_NOTFOUND; 212447b2b13SYann Gautier } 213447b2b13SYann Gautier 214447b2b13SYann Gautier subnode = fdt_subnode_offset(fdt, node, name); 215447b2b13SYann Gautier if (subnode <= 0) { 216447b2b13SYann Gautier return -FDT_ERR_NOTFOUND; 217447b2b13SYann Gautier } 218447b2b13SYann Gautier 219447b2b13SYann Gautier return subnode; 220447b2b13SYann Gautier } 221447b2b13SYann Gautier 222447b2b13SYann Gautier /* 223447b2b13SYann Gautier * Get the pointer to a rcc-clk property from its name. 224447b2b13SYann Gautier * @param name: name of the RCC property 225447b2b13SYann Gautier * @param lenp: stores the length of the property. 226447b2b13SYann Gautier * @return: pointer to the property on success, and NULL value on failure. 227447b2b13SYann Gautier */ 228447b2b13SYann Gautier const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp) 229447b2b13SYann Gautier { 230447b2b13SYann Gautier const fdt32_t *cuint; 231447b2b13SYann Gautier int node, len; 232447b2b13SYann Gautier void *fdt; 233447b2b13SYann Gautier 234447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) { 235447b2b13SYann Gautier return NULL; 236447b2b13SYann Gautier } 237447b2b13SYann Gautier 238447b2b13SYann Gautier node = fdt_get_rcc_node(fdt); 239447b2b13SYann Gautier if (node < 0) { 240447b2b13SYann Gautier return NULL; 241447b2b13SYann Gautier } 242447b2b13SYann Gautier 243447b2b13SYann Gautier cuint = fdt_getprop(fdt, node, prop_name, &len); 244447b2b13SYann Gautier if (cuint == NULL) { 245447b2b13SYann Gautier return NULL; 246447b2b13SYann Gautier } 247447b2b13SYann Gautier 248447b2b13SYann Gautier *lenp = len; 249447b2b13SYann Gautier return cuint; 250447b2b13SYann Gautier } 251447b2b13SYann Gautier 252447b2b13SYann Gautier /* 253*812daf91SLionel Debieve * Get the secure state for rcc node in device tree. 254*812daf91SLionel Debieve * @return: true if rcc is configured for secure world access, false if not. 255447b2b13SYann Gautier */ 256*812daf91SLionel Debieve bool fdt_get_rcc_secure_state(void) 257447b2b13SYann Gautier { 258447b2b13SYann Gautier void *fdt; 259447b2b13SYann Gautier 260447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) { 261447b2b13SYann Gautier return false; 262447b2b13SYann Gautier } 263447b2b13SYann Gautier 264*812daf91SLionel Debieve if (fdt_node_offset_by_compatible(fdt, -1, DT_RCC_SEC_CLK_COMPAT) < 0) { 265447b2b13SYann Gautier return false; 266447b2b13SYann Gautier } 267447b2b13SYann Gautier 268*812daf91SLionel Debieve return true; 269447b2b13SYann Gautier } 270447b2b13SYann Gautier 271447b2b13SYann Gautier /* 272447b2b13SYann Gautier * Get the clock ID of the given node in device tree. 273447b2b13SYann Gautier * @param node: node offset 274447b2b13SYann Gautier * @return: Clock ID on success, and a negative FDT/ERRNO error code on failure. 275447b2b13SYann Gautier */ 276447b2b13SYann Gautier int fdt_get_clock_id(int node) 277447b2b13SYann Gautier { 278447b2b13SYann Gautier const fdt32_t *cuint; 279447b2b13SYann Gautier void *fdt; 280447b2b13SYann Gautier 281447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) { 282447b2b13SYann Gautier return -ENOENT; 283447b2b13SYann Gautier } 284447b2b13SYann Gautier 285447b2b13SYann Gautier cuint = fdt_getprop(fdt, node, "clocks", NULL); 286447b2b13SYann Gautier if (cuint == NULL) { 287447b2b13SYann Gautier return -FDT_ERR_NOTFOUND; 288447b2b13SYann Gautier } 289447b2b13SYann Gautier 290447b2b13SYann Gautier cuint++; 291447b2b13SYann Gautier return (int)fdt32_to_cpu(*cuint); 292447b2b13SYann Gautier } 293165ad556SNicolas Le Bayon 294165ad556SNicolas Le Bayon /* 295165ad556SNicolas Le Bayon * Get the frequency of the specified UART instance. 296165ad556SNicolas Le Bayon * @param instance: UART interface registers base address. 297165ad556SNicolas Le Bayon * @return: clock frequency on success, 0 value on failure. 298165ad556SNicolas Le Bayon */ 299165ad556SNicolas Le Bayon unsigned long fdt_get_uart_clock_freq(uintptr_t instance) 300165ad556SNicolas Le Bayon { 301165ad556SNicolas Le Bayon void *fdt; 302165ad556SNicolas Le Bayon int node; 303165ad556SNicolas Le Bayon int clk_id; 304165ad556SNicolas Le Bayon 305165ad556SNicolas Le Bayon if (fdt_get_address(&fdt) == 0) { 306165ad556SNicolas Le Bayon return 0UL; 307165ad556SNicolas Le Bayon } 308165ad556SNicolas Le Bayon 309165ad556SNicolas Le Bayon /* Check for UART nodes */ 310165ad556SNicolas Le Bayon node = dt_match_instance_by_compatible(DT_UART_COMPAT, instance); 311165ad556SNicolas Le Bayon if (node < 0) { 312165ad556SNicolas Le Bayon return 0UL; 313165ad556SNicolas Le Bayon } 314165ad556SNicolas Le Bayon 315165ad556SNicolas Le Bayon clk_id = fdt_get_clock_id(node); 316165ad556SNicolas Le Bayon if (clk_id < 0) { 317165ad556SNicolas Le Bayon return 0UL; 318165ad556SNicolas Le Bayon } 319165ad556SNicolas Le Bayon 32033667d29SYann Gautier return clk_get_rate((unsigned long)clk_id); 321165ad556SNicolas Le Bayon } 322591d80c8SLionel Debieve 323591d80c8SLionel Debieve /******************************************************************************* 324591d80c8SLionel Debieve * This function configures and restores the STGEN counter depending on the 325591d80c8SLionel Debieve * connected clock. 326591d80c8SLionel Debieve ******************************************************************************/ 327591d80c8SLionel Debieve void stm32mp_stgen_config(unsigned long rate) 328591d80c8SLionel Debieve { 329591d80c8SLionel Debieve uint32_t cntfid0; 330591d80c8SLionel Debieve unsigned long long counter; 331591d80c8SLionel Debieve 332591d80c8SLionel Debieve cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF); 333591d80c8SLionel Debieve 334591d80c8SLionel Debieve if (cntfid0 == rate) { 335591d80c8SLionel Debieve return; 336591d80c8SLionel Debieve } 337591d80c8SLionel Debieve 338591d80c8SLionel Debieve mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 339591d80c8SLionel Debieve counter = stm32mp_stgen_get_counter() * rate / cntfid0; 340591d80c8SLionel Debieve 341591d80c8SLionel Debieve mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter); 342591d80c8SLionel Debieve mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32)); 343591d80c8SLionel Debieve mmio_write_32(STGEN_BASE + CNTFID_OFF, rate); 344591d80c8SLionel Debieve mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 345591d80c8SLionel Debieve 346591d80c8SLionel Debieve write_cntfrq_el0(rate); 347591d80c8SLionel Debieve 348591d80c8SLionel Debieve /* Need to update timer with new frequency */ 349591d80c8SLionel Debieve generic_delay_timer_init(); 350591d80c8SLionel Debieve } 351591d80c8SLionel Debieve 352591d80c8SLionel Debieve /******************************************************************************* 353591d80c8SLionel Debieve * This function returns the STGEN counter value. 354591d80c8SLionel Debieve ******************************************************************************/ 355591d80c8SLionel Debieve unsigned long long stm32mp_stgen_get_counter(void) 356591d80c8SLionel Debieve { 357591d80c8SLionel Debieve return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) | 358591d80c8SLionel Debieve mmio_read_32(STGEN_BASE + CNTCVL_OFF)); 359591d80c8SLionel Debieve } 360591d80c8SLionel Debieve 361591d80c8SLionel Debieve /******************************************************************************* 362591d80c8SLionel Debieve * This function restores the STGEN counter value. 363591d80c8SLionel Debieve * It takes a first input value as a counter backup value to be restored and a 364591d80c8SLionel Debieve * offset in ms to be added. 365591d80c8SLionel Debieve ******************************************************************************/ 366591d80c8SLionel Debieve void stm32mp_stgen_restore_counter(unsigned long long value, 367591d80c8SLionel Debieve unsigned long long offset_in_ms) 368591d80c8SLionel Debieve { 369591d80c8SLionel Debieve unsigned long long cnt; 370591d80c8SLionel Debieve 371591d80c8SLionel Debieve cnt = value + ((offset_in_ms * 372591d80c8SLionel Debieve mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U); 373591d80c8SLionel Debieve 374591d80c8SLionel Debieve mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 375591d80c8SLionel Debieve mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)cnt); 376591d80c8SLionel Debieve mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(cnt >> 32)); 377591d80c8SLionel Debieve mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 378591d80c8SLionel Debieve } 379