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