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 status for rcc node in device tree. 254 * @return: true if rcc is available from secure world, false if not. 255 */ 256 bool fdt_get_rcc_secure_status(void) 257 { 258 int node; 259 void *fdt; 260 261 if (fdt_get_address(&fdt) == 0) { 262 return false; 263 } 264 265 node = fdt_get_rcc_node(fdt); 266 if (node < 0) { 267 return false; 268 } 269 270 return !!(fdt_get_status(node) & DT_SECURE); 271 } 272 273 /* 274 * Get the clock ID of the given node in device tree. 275 * @param node: node offset 276 * @return: Clock ID on success, and a negative FDT/ERRNO error code on failure. 277 */ 278 int fdt_get_clock_id(int node) 279 { 280 const fdt32_t *cuint; 281 void *fdt; 282 283 if (fdt_get_address(&fdt) == 0) { 284 return -ENOENT; 285 } 286 287 cuint = fdt_getprop(fdt, node, "clocks", NULL); 288 if (cuint == NULL) { 289 return -FDT_ERR_NOTFOUND; 290 } 291 292 cuint++; 293 return (int)fdt32_to_cpu(*cuint); 294 } 295 296 /* 297 * Get the frequency of the specified UART instance. 298 * @param instance: UART interface registers base address. 299 * @return: clock frequency on success, 0 value on failure. 300 */ 301 unsigned long fdt_get_uart_clock_freq(uintptr_t instance) 302 { 303 void *fdt; 304 int node; 305 int clk_id; 306 307 if (fdt_get_address(&fdt) == 0) { 308 return 0UL; 309 } 310 311 /* Check for UART nodes */ 312 node = dt_match_instance_by_compatible(DT_UART_COMPAT, instance); 313 if (node < 0) { 314 return 0UL; 315 } 316 317 clk_id = fdt_get_clock_id(node); 318 if (clk_id < 0) { 319 return 0UL; 320 } 321 322 return clk_get_rate((unsigned long)clk_id); 323 } 324 325 /******************************************************************************* 326 * This function configures and restores the STGEN counter depending on the 327 * connected clock. 328 ******************************************************************************/ 329 void stm32mp_stgen_config(unsigned long rate) 330 { 331 uint32_t cntfid0; 332 unsigned long long counter; 333 334 cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF); 335 336 if (cntfid0 == rate) { 337 return; 338 } 339 340 mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 341 counter = stm32mp_stgen_get_counter() * rate / cntfid0; 342 343 mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter); 344 mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32)); 345 mmio_write_32(STGEN_BASE + CNTFID_OFF, rate); 346 mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 347 348 write_cntfrq_el0(rate); 349 350 /* Need to update timer with new frequency */ 351 generic_delay_timer_init(); 352 } 353 354 /******************************************************************************* 355 * This function returns the STGEN counter value. 356 ******************************************************************************/ 357 unsigned long long stm32mp_stgen_get_counter(void) 358 { 359 return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) | 360 mmio_read_32(STGEN_BASE + CNTCVL_OFF)); 361 } 362 363 /******************************************************************************* 364 * This function restores the STGEN counter value. 365 * It takes a first input value as a counter backup value to be restored and a 366 * offset in ms to be added. 367 ******************************************************************************/ 368 void stm32mp_stgen_restore_counter(unsigned long long value, 369 unsigned long long offset_in_ms) 370 { 371 unsigned long long cnt; 372 373 cnt = value + ((offset_in_ms * 374 mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U); 375 376 mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 377 mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)cnt); 378 mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(cnt >> 32)); 379 mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); 380 } 381