Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clocksource/drivers/arm_global_timer: Add auto-detection for initial prescaler values

am43xx has a clock tree where the global timer clock is an indirect child
of the CPU clock used for frequency scaling:

dpll_mpu_ck -- CPU/cpufreq
|
v
dpll_mpu_m2_ck -- divider
|
v
mpu_periphclk -- fixed divider by 2 used for global timer

When CPU frequency changes, the global timer's clock notifier rejects
the change because the hardcoded prescaler (1 or 2) cannot accommodate
the frequency range across all CPU OPPs (300, 600, 720, 800, 1000 MHz).

Add platform-specific prescaler auto-detection to solve this issue:

- am43xx: prescaler = 50 (calculated as initial_freq/GCD of all OPP
freqs) This allows the timer to work across all CPU frequencies after
the fixed divider by 2. Tested on am4372-idk-evm.

- zynq-7000: prescaler = 2 (preserves previous Kconfig default)

- Other platforms: prescaler = 1 (previous default)

The Kconfig option now defaults to 0 (auto-detection) but can still
override the auto-detected value when set to a non-zero value,
preserving existing customization workflows.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Tested-by: Kevin Hilman <khilman@baylibre.com>
Tested-by: Patrice Chotard <patrice.chotard@foss.st.com>
Tested-by: Judith Mendez <jm@ti.com>
Reviewed-by: Kevin Hilman <khilman@baylibre.com>
Link: https://lore.kernel.org/r/20250819-topic-am43-arm-global-timer-v6-16-v2-1-6d082e2a5161@baylibre.com

authored by

Markus Schneider-Pargmann and committed by
Daniel Lezcano
1c4b87c9 21b8a635

+41 -7
+2 -2
drivers/clocksource/Kconfig
··· 395 395 396 396 config ARM_GT_INITIAL_PRESCALER_VAL 397 397 int "ARM global timer initial prescaler value" 398 - default 2 if ARCH_ZYNQ 399 - default 1 398 + default 0 400 399 depends on ARM_GLOBAL_TIMER 401 400 help 402 401 When the ARM global timer initializes, its current rate is declared ··· 405 406 bounds about how much the parent clock is allowed to decrease or 406 407 increase wrt the initial clock value. 407 408 This affects CPU_FREQ max delta from the initial frequency. 409 + Use 0 to use auto-detection in the driver. 408 410 409 411 config ARM_TIMER_SP804 410 412 bool "Support for Dual Timer SP804 module"
+39 -5
drivers/clocksource/arm_global_timer.c
··· 263 263 register_current_timer_delay(&gt_delay_timer); 264 264 } 265 265 266 - static int __init gt_clocksource_init(void) 266 + static int __init gt_clocksource_init(unsigned int psv) 267 267 { 268 268 writel(0, gt_base + GT_CONTROL); 269 269 writel(0, gt_base + GT_COUNTER0); 270 270 writel(0, gt_base + GT_COUNTER1); 271 271 /* set prescaler and enable timer on all the cores */ 272 - writel(FIELD_PREP(GT_CONTROL_PRESCALER_MASK, 273 - CONFIG_ARM_GT_INITIAL_PRESCALER_VAL - 1) | 272 + writel(FIELD_PREP(GT_CONTROL_PRESCALER_MASK, psv - 1) | 274 273 GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); 275 274 276 275 #ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK ··· 337 338 return NOTIFY_DONE; 338 339 } 339 340 341 + struct gt_prescaler_config { 342 + const char *compatible; 343 + unsigned long prescaler; 344 + }; 345 + 346 + static const struct gt_prescaler_config gt_prescaler_configs[] = { 347 + /* 348 + * On am43 the global timer clock is a child of the clock used for CPU 349 + * OPPs, so the initial prescaler has to be compatible with all OPPs 350 + * which are 300, 600, 720, 800 and 1000 with a fixed divider of 2, this 351 + * gives us a GCD of 10. Initial frequency is 1000, so the prescaler is 352 + * 50. 353 + */ 354 + { .compatible = "ti,am43", .prescaler = 50 }, 355 + { .compatible = "xlnx,zynq-7000", .prescaler = 2 }, 356 + { .compatible = NULL } 357 + }; 358 + 359 + static unsigned long gt_get_initial_prescaler_value(struct device_node *np) 360 + { 361 + const struct gt_prescaler_config *config; 362 + 363 + if (CONFIG_ARM_GT_INITIAL_PRESCALER_VAL != 0) 364 + return CONFIG_ARM_GT_INITIAL_PRESCALER_VAL; 365 + 366 + for (config = gt_prescaler_configs; config->compatible; config++) { 367 + if (of_machine_is_compatible(config->compatible)) 368 + return config->prescaler; 369 + } 370 + 371 + return 1; 372 + } 373 + 340 374 static int __init global_timer_of_register(struct device_node *np) 341 375 { 342 376 struct clk *gt_clk; 343 377 static unsigned long gt_clk_rate; 344 378 int err; 379 + unsigned long psv; 345 380 346 381 /* 347 382 * In A9 r2p0 the comparators for each processor with the global timer ··· 411 378 goto out_unmap; 412 379 } 413 380 381 + psv = gt_get_initial_prescaler_value(np); 414 382 gt_clk_rate = clk_get_rate(gt_clk); 415 - gt_target_rate = gt_clk_rate / CONFIG_ARM_GT_INITIAL_PRESCALER_VAL; 383 + gt_target_rate = gt_clk_rate / psv; 416 384 gt_clk_rate_change_nb.notifier_call = 417 385 gt_clk_rate_change_cb; 418 386 err = clk_notifier_register(gt_clk, &gt_clk_rate_change_nb); ··· 438 404 } 439 405 440 406 /* Register and immediately configure the timer on the boot CPU */ 441 - err = gt_clocksource_init(); 407 + err = gt_clocksource_init(psv); 442 408 if (err) 443 409 goto out_irq; 444 410