Skip to content

Commit 1c4b87c

Browse files
scosudlezcano
authored andcommitted
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
1 parent 21b8a63 commit 1c4b87c

2 files changed

Lines changed: 41 additions & 7 deletions

File tree

drivers/clocksource/Kconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,7 @@ config ARM_GLOBAL_TIMER
395395

396396
config ARM_GT_INITIAL_PRESCALER_VAL
397397
int "ARM global timer initial prescaler value"
398-
default 2 if ARCH_ZYNQ
399-
default 1
398+
default 0
400399
depends on ARM_GLOBAL_TIMER
401400
help
402401
When the ARM global timer initializes, its current rate is declared
@@ -406,6 +405,7 @@ config ARM_GT_INITIAL_PRESCALER_VAL
406405
bounds about how much the parent clock is allowed to decrease or
407406
increase wrt the initial clock value.
408407
This affects CPU_FREQ max delta from the initial frequency.
408+
Use 0 to use auto-detection in the driver.
409409

410410
config ARM_TIMER_SP804
411411
bool "Support for Dual Timer SP804 module"

drivers/clocksource/arm_global_timer.c

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,14 +263,13 @@ static void __init gt_delay_timer_init(void)
263263
register_current_timer_delay(&gt_delay_timer);
264264
}
265265

266-
static int __init gt_clocksource_init(void)
266+
static int __init gt_clocksource_init(unsigned int psv)
267267
{
268268
writel(0, gt_base + GT_CONTROL);
269269
writel(0, gt_base + GT_COUNTER0);
270270
writel(0, gt_base + GT_COUNTER1);
271271
/* 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) |
274273
GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
275274

276275
#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
@@ -338,11 +337,45 @@ static int gt_clk_rate_change_cb(struct notifier_block *nb,
338337
return NOTIFY_DONE;
339338
}
340339

340+
struct gt_prescaler_config {
341+
const char *compatible;
342+
unsigned long prescaler;
343+
};
344+
345+
static const struct gt_prescaler_config gt_prescaler_configs[] = {
346+
/*
347+
* On am43 the global timer clock is a child of the clock used for CPU
348+
* OPPs, so the initial prescaler has to be compatible with all OPPs
349+
* which are 300, 600, 720, 800 and 1000 with a fixed divider of 2, this
350+
* gives us a GCD of 10. Initial frequency is 1000, so the prescaler is
351+
* 50.
352+
*/
353+
{ .compatible = "ti,am43", .prescaler = 50 },
354+
{ .compatible = "xlnx,zynq-7000", .prescaler = 2 },
355+
{ .compatible = NULL }
356+
};
357+
358+
static unsigned long gt_get_initial_prescaler_value(struct device_node *np)
359+
{
360+
const struct gt_prescaler_config *config;
361+
362+
if (CONFIG_ARM_GT_INITIAL_PRESCALER_VAL != 0)
363+
return CONFIG_ARM_GT_INITIAL_PRESCALER_VAL;
364+
365+
for (config = gt_prescaler_configs; config->compatible; config++) {
366+
if (of_machine_is_compatible(config->compatible))
367+
return config->prescaler;
368+
}
369+
370+
return 1;
371+
}
372+
341373
static int __init global_timer_of_register(struct device_node *np)
342374
{
343375
struct clk *gt_clk;
344376
static unsigned long gt_clk_rate;
345377
int err;
378+
unsigned long psv;
346379

347380
/*
348381
* In A9 r2p0 the comparators for each processor with the global timer
@@ -378,8 +411,9 @@ static int __init global_timer_of_register(struct device_node *np)
378411
goto out_unmap;
379412
}
380413

414+
psv = gt_get_initial_prescaler_value(np);
381415
gt_clk_rate = clk_get_rate(gt_clk);
382-
gt_target_rate = gt_clk_rate / CONFIG_ARM_GT_INITIAL_PRESCALER_VAL;
416+
gt_target_rate = gt_clk_rate / psv;
383417
gt_clk_rate_change_nb.notifier_call =
384418
gt_clk_rate_change_cb;
385419
err = clk_notifier_register(gt_clk, &gt_clk_rate_change_nb);
@@ -404,7 +438,7 @@ static int __init global_timer_of_register(struct device_node *np)
404438
}
405439

406440
/* Register and immediately configure the timer on the boot CPU */
407-
err = gt_clocksource_init();
441+
err = gt_clocksource_init(psv);
408442
if (err)
409443
goto out_irq;
410444

0 commit comments

Comments
 (0)