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