From 4f9f4f0f6261e4b162dfcaf91e08824a7c93da07 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 11 May 2021 15:48:56 +0800 Subject: [PATCH 01/14] clocksource/drivers/arm_arch_timer: Remove arch_timer_rate1 This variable is added by my mistake, it's not used at all. Fixes: e2bf384d4329 ("clocksource/drivers/arm_arch_timer: Add __ro_after_init and __init") Signed-off-by: Jisheng Zhang Reported-by: Hulk Robot Acked-by: Marc Zyngier Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210511154856.6afbcb65@xhacker.debian --- drivers/clocksource/arm_arch_timer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index fe1a82627d57..89a9e0524555 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -64,7 +64,6 @@ struct arch_timer { #define to_arch_timer(e) container_of(e, struct arch_timer, evt) static u32 arch_timer_rate __ro_after_init; -u32 arch_timer_rate1 __ro_after_init; static int arch_timer_ppi[ARCH_TIMER_MAX_TIMER_PPI] __ro_after_init; static const char *arch_timer_ppi_names[ARCH_TIMER_MAX_TIMER_PPI] = { From a0143f5ac0594d73ef91c2336d8172217ff9cd72 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 6 May 2021 16:27:25 -0400 Subject: [PATCH 02/14] clocksource/drivers/samsung_pwm: Minor whitespace cleanup Cleanup the code to be slightly more readable and follow coding convention - only whitespace. This fixes checkpatch warnings: WARNING: Block comments should align the * on each line WARNING: please, no space before tabs WARNING: Missing a blank line after declarations CHECK: Alignment should match open parenthesis Signed-off-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210506202729.157260-1-krzysztof.kozlowski@canonical.com --- drivers/clocksource/samsung_pwm_timer.c | 19 +++++++++++-------- include/clocksource/samsung_pwm.h | 3 ++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index f760229d0c7f..69bf79c7f462 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -4,7 +4,7 @@ * http://www.samsung.com/ * * samsung - Common hr-timer support (s3c and s5p) -*/ + */ #include #include @@ -22,7 +22,6 @@ #include - /* * Clocksource driver */ @@ -38,8 +37,8 @@ #define TCFG0_PRESCALER_MASK 0xff #define TCFG0_PRESCALER1_SHIFT 8 -#define TCFG1_SHIFT(x) ((x) * 4) -#define TCFG1_MUX_MASK 0xf +#define TCFG1_SHIFT(x) ((x) * 4) +#define TCFG1_MUX_MASK 0xf /* * Each channel occupies 4 bits in TCON register, but there is a gap of 4 @@ -183,7 +182,7 @@ static void samsung_time_start(unsigned int channel, bool periodic) } static int samsung_set_next_event(unsigned long cycles, - struct clock_event_device *evt) + struct clock_event_device *evt) { /* * This check is needed to account for internal rounding @@ -225,6 +224,7 @@ static void samsung_clockevent_resume(struct clock_event_device *cev) if (pwm.variant.has_tint_cstat) { u32 mask = (1 << pwm.event_id); + writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); } } @@ -248,6 +248,7 @@ static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id) if (pwm.variant.has_tint_cstat) { u32 mask = (1 << pwm.event_id); + writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); } @@ -272,7 +273,7 @@ static void __init samsung_clockevent_init(void) time_event_device.cpumask = cpumask_of(0); clockevents_config_and_register(&time_event_device, - clock_rate, 1, pwm.tcnt_max); + clock_rate, 1, pwm.tcnt_max); irq_number = pwm.irq[pwm.event_id]; if (request_irq(irq_number, samsung_clock_event_isr, @@ -282,6 +283,7 @@ static void __init samsung_clockevent_init(void) if (pwm.variant.has_tint_cstat) { u32 mask = (1 << pwm.event_id); + writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); } } @@ -347,7 +349,7 @@ static int __init samsung_clocksource_init(void) pwm.source_reg = pwm.base + pwm.source_id * 0x0c + 0x14; sched_clock_register(samsung_read_sched_clock, - pwm.variant.bits, clock_rate); + pwm.variant.bits, clock_rate); samsung_clocksource.mask = CLOCKSOURCE_MASK(pwm.variant.bits); return clocksource_register_hz(&samsung_clocksource, clock_rate); @@ -398,7 +400,8 @@ static int __init _samsung_pwm_clocksource_init(void) } void __init samsung_pwm_clocksource_init(void __iomem *base, - unsigned int *irqs, struct samsung_pwm_variant *variant) + unsigned int *irqs, + struct samsung_pwm_variant *variant) { pwm.base = base; memcpy(&pwm.variant, variant, sizeof(pwm.variant)); diff --git a/include/clocksource/samsung_pwm.h b/include/clocksource/samsung_pwm.h index c395238d0922..76341988fb4f 100644 --- a/include/clocksource/samsung_pwm.h +++ b/include/clocksource/samsung_pwm.h @@ -27,6 +27,7 @@ struct samsung_pwm_variant { }; void samsung_pwm_clocksource_init(void __iomem *base, - unsigned int *irqs, struct samsung_pwm_variant *variant); + unsigned int *irqs, + struct samsung_pwm_variant *variant); #endif /* __CLOCKSOURCE_SAMSUNG_PWM_H */ From bb08e96575dbbd49acb49999dd0d7ffedb5c1608 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 6 May 2021 16:27:26 -0400 Subject: [PATCH 03/14] clocksource/drivers/samsung_pwm: Constify passed structure The 'struct samsung_pwm_variant' argument passed to initialization functions is not modified, so it can be made const for safety. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210506202729.157260-2-krzysztof.kozlowski@canonical.com --- drivers/clocksource/samsung_pwm_timer.c | 2 +- include/clocksource/samsung_pwm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 69bf79c7f462..bfad61b509f9 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -401,7 +401,7 @@ static int __init _samsung_pwm_clocksource_init(void) void __init samsung_pwm_clocksource_init(void __iomem *base, unsigned int *irqs, - struct samsung_pwm_variant *variant) + const struct samsung_pwm_variant *variant) { pwm.base = base; memcpy(&pwm.variant, variant, sizeof(pwm.variant)); diff --git a/include/clocksource/samsung_pwm.h b/include/clocksource/samsung_pwm.h index 76341988fb4f..9b435caa95fe 100644 --- a/include/clocksource/samsung_pwm.h +++ b/include/clocksource/samsung_pwm.h @@ -28,6 +28,6 @@ struct samsung_pwm_variant { void samsung_pwm_clocksource_init(void __iomem *base, unsigned int *irqs, - struct samsung_pwm_variant *variant); + const struct samsung_pwm_variant *variant); #endif /* __CLOCKSOURCE_SAMSUNG_PWM_H */ From 63e83bd8cd848a3d1b4777d90635a309fa9cb2c7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 6 May 2021 16:27:27 -0400 Subject: [PATCH 04/14] clocksource/drivers/samsung_pwm: Cleanup on init error Failure of timer initialization is likely to be fatal for the system, so cleanup in such case is not strictly necessary. However the code might be refactored or reused, so better not to rely on such assumption that system won't continue init failure. Unmap the IO memory and put the clock on initialization failures from devicetree. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210506202729.157260-3-krzysztof.kozlowski@canonical.com --- drivers/clocksource/samsung_pwm_timer.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index bfad61b509f9..55e2f9fa2a15 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -421,7 +421,7 @@ static int __init samsung_pwm_alloc(struct device_node *np, struct property *prop; const __be32 *cur; u32 val; - int i; + int i, ret; memcpy(&pwm.variant, variant, sizeof(pwm.variant)); for (i = 0; i < SAMSUNG_PWM_NUM; ++i) @@ -444,10 +444,24 @@ static int __init samsung_pwm_alloc(struct device_node *np, pwm.timerclk = of_clk_get_by_name(np, "timers"); if (IS_ERR(pwm.timerclk)) { pr_crit("failed to get timers clock for timer\n"); - return PTR_ERR(pwm.timerclk); + ret = PTR_ERR(pwm.timerclk); + goto err_clk; } - return _samsung_pwm_clocksource_init(); + ret = _samsung_pwm_clocksource_init(); + if (ret) + goto err_clocksource; + + return 0; + +err_clocksource: + clk_put(pwm.timerclk); + pwm.timerclk = NULL; +err_clk: + iounmap(pwm.base); + pwm.base = NULL; + + return ret; } static const struct samsung_pwm_variant s3c24xx_variant = { From b4318ce203db8f8b7004e7ab82a957f894660c88 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 6 May 2021 16:27:28 -0400 Subject: [PATCH 05/14] clocksource/drivers/samsung_pwm: Constify source IO memory The 'source_reg' IO memory is only read, so the pointer can point to const for safety. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210506202729.157260-4-krzysztof.kozlowski@canonical.com --- drivers/clocksource/samsung_pwm_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 55e2f9fa2a15..6e46781bc9ac 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -61,7 +61,7 @@ EXPORT_SYMBOL(samsung_pwm_lock); struct samsung_pwm_clocksource { void __iomem *base; - void __iomem *source_reg; + const void __iomem *source_reg; unsigned int irq[SAMSUNG_PWM_NUM]; struct samsung_pwm_variant variant; From 75ac5cc2ee6b499bc0225ad67302271772929f19 Mon Sep 17 00:00:00 2001 From: Evan Benn Date: Wed, 12 May 2021 12:25:44 +1000 Subject: [PATCH 06/14] clocksource/drivers/mediatek: Ack and disable interrupts on suspend Interrupts are disabled during suspend before this driver disables its timers. ARM trusted firmware will abort suspend if the timer irq is pending, so ack and disable the timer interrupt during suspend. Signed-off-by: Evan Benn Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210512122528.v4.1.I1d9917047de06715da16e1620759f703fcfdcbcb@changeid --- drivers/clocksource/timer-mediatek.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/clocksource/timer-mediatek.c b/drivers/clocksource/timer-mediatek.c index 9318edcd8963..ab63b95e414f 100644 --- a/drivers/clocksource/timer-mediatek.c +++ b/drivers/clocksource/timer-mediatek.c @@ -241,6 +241,28 @@ static void mtk_gpt_enable_irq(struct timer_of *to, u8 timer) timer_of_base(to) + GPT_IRQ_EN_REG); } +static void mtk_gpt_resume(struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + + mtk_gpt_enable_irq(to, TIMER_CLK_EVT); +} + +static void mtk_gpt_suspend(struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + + /* Disable all interrupts */ + writel(0x0, timer_of_base(to) + GPT_IRQ_EN_REG); + + /* + * This is called with interrupts disabled, + * so we need to ack any interrupt that is pending + * or for example ATF will prevent a suspend from completing. + */ + writel(0x3f, timer_of_base(to) + GPT_IRQ_ACK_REG); +} + static struct timer_of to = { .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, @@ -286,6 +308,8 @@ static int __init mtk_gpt_init(struct device_node *node) to.clkevt.set_state_oneshot = mtk_gpt_clkevt_shutdown; to.clkevt.tick_resume = mtk_gpt_clkevt_shutdown; to.clkevt.set_next_event = mtk_gpt_clkevt_next_event; + to.clkevt.suspend = mtk_gpt_suspend; + to.clkevt.resume = mtk_gpt_resume; to.of_irq.handler = mtk_gpt_interrupt; ret = timer_of_init(node, &to); From 9517c577f9f722270584cfb1a7b4e1354e408658 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 15 Apr 2021 11:55:06 +0300 Subject: [PATCH 07/14] clocksource/drivers/timer-ti-dm: Save and restore timer TIOCP_CFG As we are using cpu_pm to save and restore context, we must also save and restore the timer sysconfig register TIOCP_CFG. This is needed because we are not calling PM runtime functions at all with cpu_pm. Fixes: b34677b0999a ("clocksource/drivers/timer-ti-dm: Implement cpu_pm notifier for context save and restore") Cc: Aaro Koskinen Cc: Adam Ford Cc: Andreas Kemnade Cc: Lokesh Vutla Cc: Peter Ujfalusi Signed-off-by: Tony Lindgren Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210415085506.56828-1-tony@atomide.com --- drivers/clocksource/timer-ti-dm.c | 6 ++++++ include/clocksource/timer-ti-dm.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 33eeabf9c3d1..e5c631f1b5cb 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -78,6 +78,9 @@ static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, static void omap_timer_restore_context(struct omap_dm_timer *timer) { + __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, + timer->context.ocp_cfg, 0); + omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, timer->context.twer); omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, @@ -95,6 +98,9 @@ static void omap_timer_restore_context(struct omap_dm_timer *timer) static void omap_timer_save_context(struct omap_dm_timer *timer) { + timer->context.ocp_cfg = + __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0); + timer->context.tclr = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); timer->context.twer = diff --git a/include/clocksource/timer-ti-dm.h b/include/clocksource/timer-ti-dm.h index 4c61dade8835..f6da8a132639 100644 --- a/include/clocksource/timer-ti-dm.h +++ b/include/clocksource/timer-ti-dm.h @@ -74,6 +74,7 @@ #define OMAP_TIMER_ERRATA_I103_I767 0x80000000 struct timer_regs { + u32 ocp_cfg; u32 tidr; u32 tier; u32 twer; From 870a6e1539829356baf70b57c933d0b309cfac21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=90=B0=E6=9D=B0=20=28Zhou=20Yanjie=29?= Date: Sat, 5 Jun 2021 00:31:45 +0800 Subject: [PATCH 08/14] clocksource/drivers/ingenic: Rename unreasonable array names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.Rename the "ingenic_ost_clk_info[]" to "x1000_ost_clk_info[]" to facilitate the addition of OST support for X2000 SoC in a later commit 2.When the OST support for X2000 SoC is added, there will be two compatible strings, so renaming "ingenic_ost_of_match[]" to "ingenic_ost_of_matches[]" is more reasonable 3.Remove the unnecessary comma in "ingenic_ost_of_matches[]" to reduce code size as much as possible. Signed-off-by: 周琰杰 (Zhou Yanjie) Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1622824306-30987-2-git-send-email-zhouyanjie@wanyeetech.com --- drivers/clocksource/ingenic-sysost.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/clocksource/ingenic-sysost.c b/drivers/clocksource/ingenic-sysost.c index e77d58449005..a129840f14f9 100644 --- a/drivers/clocksource/ingenic-sysost.c +++ b/drivers/clocksource/ingenic-sysost.c @@ -186,7 +186,7 @@ static const struct clk_ops ingenic_ost_global_timer_ops = { static const char * const ingenic_ost_clk_parents[] = { "ext" }; -static const struct ingenic_ost_clk_info ingenic_ost_clk_info[] = { +static const struct ingenic_ost_clk_info x1000_ost_clk_info[] = { [OST_CLK_PERCPU_TIMER] = { .init_data = { .name = "percpu timer", @@ -414,14 +414,14 @@ static const struct ingenic_soc_info x1000_soc_info = { .num_channels = 2, }; -static const struct of_device_id __maybe_unused ingenic_ost_of_match[] __initconst = { - { .compatible = "ingenic,x1000-ost", .data = &x1000_soc_info, }, +static const struct of_device_id __maybe_unused ingenic_ost_of_matches[] __initconst = { + { .compatible = "ingenic,x1000-ost", .data = &x1000_soc_info }, { /* sentinel */ } }; static int __init ingenic_ost_probe(struct device_node *np) { - const struct of_device_id *id = of_match_node(ingenic_ost_of_match, np); + const struct of_device_id *id = of_match_node(ingenic_ost_of_matches, np); struct ingenic_ost *ost; unsigned int i; int ret; @@ -462,7 +462,7 @@ static int __init ingenic_ost_probe(struct device_node *np) ost->clocks->num = ost->soc_info->num_channels; for (i = 0; i < ost->clocks->num; i++) { - ret = ingenic_ost_register_clock(ost, i, &ingenic_ost_clk_info[i], ost->clocks); + ret = ingenic_ost_register_clock(ost, i, &x1000_ost_clk_info[i], ost->clocks); if (ret) { pr_crit("%s: Cannot register clock %d\n", __func__, i); goto err_unregister_ost_clocks; From 171b45a4a70eef2fd36bb794ce4f5a48c440361e Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 6 Apr 2021 15:00:44 +0200 Subject: [PATCH 09/14] clocksource/drivers/arm_global_timer: Implement rate compensation whenever source clock changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds rate change notification support for the parent clock; should that clock change, then we try to adjust the our prescaler in order to compensate (i.e. we adjust to still get the same timer frequency). This is loosely based on what it's done in timer-cadence-ttc. timer-sun51, mips-gic-timer and smp_twd.c also seem to look at their parent clock rate and to perform some kind of adjustment whenever needed. In this particular case we have only one single counter and prescaler for all clocksource, clockevent and timer_delay, and we just update it for all (i.e. we don't let it go and call clockevents_update_freq() to notify to the kernel that our rate has changed). Note that, there is apparently no other way to fixup things, because once we call register_current_timer_delay(), specifying the timer rate, it seems that that rate is not supposed to change ever. In order for this mechanism to work, we have to make assumptions about how much the initial clock is supposed to eventually decrease from the initial one, and set our initial prescaler to a value that we can eventually decrease enough to compensate. We provide an option in KConfig for this. In case we end up in a situation in which we are not able to compensate the parent clock change, we fail returning NOTIFY_BAD. This fixes a real-world problem with Zynq arch not being able to use this driver and CPU_FREQ at the same time (because ARM global timer is fed by the CPU clock, which may keep changing when CPU_FREQ is enabled). Signed-off-by: Andrea Merello Cc: Patrice Chotard Cc: linux-kernel@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: Michal Simek Cc: Sören Brinkmann Reviewed-by: Patrice Chotard Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210406130045.15491-2-andrea.merello@gmail.com --- drivers/clocksource/Kconfig | 13 +++ drivers/clocksource/arm_global_timer.c | 122 +++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 10 deletions(-) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 39aa21d01e05..19fc5f8883e0 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -358,6 +358,19 @@ config ARM_GLOBAL_TIMER help This option enables support for the ARM global timer unit. +config ARM_GT_INITIAL_PRESCALER_VAL + int "ARM global timer initial prescaler value" + default 1 + depends on ARM_GLOBAL_TIMER + help + When the ARM global timer initializes, its current rate is declared + to the kernel and maintained forever. Should it's parent clock + change, the driver tries to fix the timer's internal prescaler. + On some machs (i.e. Zynq) the initial prescaler value thus poses + bounds about how much the parent clock is allowed to decrease or + increase wrt the initial clock value. + This affects CPU_FREQ max delta from the initial frequency. + config ARM_TIMER_SP804 bool "Support for Dual Timer SP804 module" if COMPILE_TEST depends on GENERIC_SCHED_CLOCK && CLKDEV_LOOKUP diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c index 88b2d38a7a61..60a8047fd32e 100644 --- a/drivers/clocksource/arm_global_timer.c +++ b/drivers/clocksource/arm_global_timer.c @@ -31,6 +31,10 @@ #define GT_CONTROL_COMP_ENABLE BIT(1) /* banked */ #define GT_CONTROL_IRQ_ENABLE BIT(2) /* banked */ #define GT_CONTROL_AUTO_INC BIT(3) /* banked */ +#define GT_CONTROL_PRESCALER_SHIFT 8 +#define GT_CONTROL_PRESCALER_MAX 0xF +#define GT_CONTROL_PRESCALER_MASK (GT_CONTROL_PRESCALER_MAX << \ + GT_CONTROL_PRESCALER_SHIFT) #define GT_INT_STATUS 0x0c #define GT_INT_STATUS_EVENT_FLAG BIT(0) @@ -39,6 +43,7 @@ #define GT_COMP1 0x14 #define GT_AUTO_INC 0x18 +#define MAX_F_ERR 50 /* * We are expecting to be clocked by the ARM peripheral clock. * @@ -46,7 +51,8 @@ * the units for all operations. */ static void __iomem *gt_base; -static unsigned long gt_clk_rate; +struct notifier_block gt_clk_rate_change_nb; +static u32 gt_psv_new, gt_psv_bck, gt_target_rate; static int gt_ppi; static struct clock_event_device __percpu *gt_evt; @@ -96,7 +102,10 @@ static void gt_compare_set(unsigned long delta, int periodic) unsigned long ctrl; counter += delta; - ctrl = GT_CONTROL_TIMER_ENABLE; + ctrl = readl(gt_base + GT_CONTROL); + ctrl &= ~(GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE | + GT_CONTROL_AUTO_INC | GT_CONTROL_AUTO_INC); + ctrl |= GT_CONTROL_TIMER_ENABLE; writel_relaxed(ctrl, gt_base + GT_CONTROL); writel_relaxed(lower_32_bits(counter), gt_base + GT_COMP0); writel_relaxed(upper_32_bits(counter), gt_base + GT_COMP1); @@ -123,7 +132,7 @@ static int gt_clockevent_shutdown(struct clock_event_device *evt) static int gt_clockevent_set_periodic(struct clock_event_device *evt) { - gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1); + gt_compare_set(DIV_ROUND_CLOSEST(gt_target_rate, HZ), 1); return 0; } @@ -177,7 +186,7 @@ static int gt_starting_cpu(unsigned int cpu) clk->cpumask = cpumask_of(cpu); clk->rating = 300; clk->irq = gt_ppi; - clockevents_config_and_register(clk, gt_clk_rate, + clockevents_config_and_register(clk, gt_target_rate, 1, 0xffffffff); enable_percpu_irq(clk->irq, IRQ_TYPE_NONE); return 0; @@ -232,9 +241,28 @@ static struct delay_timer gt_delay_timer = { .read_current_timer = gt_read_long, }; +static void gt_write_presc(u32 psv) +{ + u32 reg; + + reg = readl(gt_base + GT_CONTROL); + reg &= ~GT_CONTROL_PRESCALER_MASK; + reg |= psv << GT_CONTROL_PRESCALER_SHIFT; + writel(reg, gt_base + GT_CONTROL); +} + +static u32 gt_read_presc(void) +{ + u32 reg; + + reg = readl(gt_base + GT_CONTROL); + reg &= GT_CONTROL_PRESCALER_MASK; + return reg >> GT_CONTROL_PRESCALER_SHIFT; +} + static void __init gt_delay_timer_init(void) { - gt_delay_timer.freq = gt_clk_rate; + gt_delay_timer.freq = gt_target_rate; register_current_timer_delay(>_delay_timer); } @@ -243,18 +271,81 @@ static int __init gt_clocksource_init(void) writel(0, gt_base + GT_CONTROL); writel(0, gt_base + GT_COUNTER0); writel(0, gt_base + GT_COUNTER1); - /* enables timer on all the cores */ - writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); + /* set prescaler and enable timer on all the cores */ + writel(((CONFIG_ARM_GT_INITIAL_PRESCALER_VAL - 1) << + GT_CONTROL_PRESCALER_SHIFT) + | GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); #ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK - sched_clock_register(gt_sched_clock_read, 64, gt_clk_rate); + sched_clock_register(gt_sched_clock_read, 64, gt_target_rate); #endif - return clocksource_register_hz(>_clocksource, gt_clk_rate); + return clocksource_register_hz(>_clocksource, gt_target_rate); +} + +static int gt_clk_rate_change_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + + switch (event) { + case PRE_RATE_CHANGE: + { + int psv; + + psv = DIV_ROUND_CLOSEST(ndata->new_rate, + gt_target_rate); + + if (abs(gt_target_rate - (ndata->new_rate / psv)) > MAX_F_ERR) + return NOTIFY_BAD; + + psv--; + + /* prescaler within legal range? */ + if (psv < 0 || psv > GT_CONTROL_PRESCALER_MAX) + return NOTIFY_BAD; + + /* + * store timer clock ctrl register so we can restore it in case + * of an abort. + */ + gt_psv_bck = gt_read_presc(); + gt_psv_new = psv; + /* scale down: adjust divider in post-change notification */ + if (ndata->new_rate < ndata->old_rate) + return NOTIFY_DONE; + + /* scale up: adjust divider now - before frequency change */ + gt_write_presc(psv); + break; + } + case POST_RATE_CHANGE: + /* scale up: pre-change notification did the adjustment */ + if (ndata->new_rate > ndata->old_rate) + return NOTIFY_OK; + + /* scale down: adjust divider now - after frequency change */ + gt_write_presc(gt_psv_new); + break; + + case ABORT_RATE_CHANGE: + /* we have to undo the adjustment in case we scale up */ + if (ndata->new_rate < ndata->old_rate) + return NOTIFY_OK; + + /* restore original register value */ + gt_write_presc(gt_psv_bck); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_DONE; } static int __init global_timer_of_register(struct device_node *np) { struct clk *gt_clk; + static unsigned long gt_clk_rate; int err = 0; /* @@ -292,11 +383,20 @@ static int __init global_timer_of_register(struct device_node *np) } gt_clk_rate = clk_get_rate(gt_clk); + gt_target_rate = gt_clk_rate / CONFIG_ARM_GT_INITIAL_PRESCALER_VAL; + gt_clk_rate_change_nb.notifier_call = + gt_clk_rate_change_cb; + err = clk_notifier_register(gt_clk, >_clk_rate_change_nb); + if (err) { + pr_warn("Unable to register clock notifier\n"); + goto out_clk; + } + gt_evt = alloc_percpu(struct clock_event_device); if (!gt_evt) { pr_warn("global-timer: can't allocate memory\n"); err = -ENOMEM; - goto out_clk; + goto out_clk_nb; } err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt, @@ -326,6 +426,8 @@ out_irq: free_percpu_irq(gt_ppi, gt_evt); out_free: free_percpu(gt_evt); +out_clk_nb: + clk_notifier_unregister(gt_clk, >_clk_rate_change_nb); out_clk: clk_disable_unprepare(gt_clk); out_unmap: From 68e2215e9d5f5ec8e5ba0158683742932519cad9 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 6 Apr 2021 15:00:45 +0200 Subject: [PATCH 10/14] arm: zynq: don't disable CONFIG_ARM_GLOBAL_TIMER due to CONFIG_CPU_FREQ anymore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now ARM global timer driver could work even if it's source clock rate changes, so we don't need to disable that driver when cpu frequency scaling is in use. This cause Zynq arch to get support for timer delay and get_cycles(). Signed-off-by: Andrea Merello Cc: Patrice Chotard Cc: linux-kernel@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: Michal Simek Cc: Sören Brinkmann Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210406130045.15491-3-andrea.merello@gmail.com --- arch/arm/mach-zynq/Kconfig | 2 +- drivers/clocksource/Kconfig | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig index 43fb941dcd07..a56748d671c4 100644 --- a/arch/arm/mach-zynq/Kconfig +++ b/arch/arm/mach-zynq/Kconfig @@ -6,7 +6,7 @@ config ARCH_ZYNQ select ARCH_SUPPORTS_BIG_ENDIAN select ARM_AMBA select ARM_GIC - select ARM_GLOBAL_TIMER if !CPU_FREQ + select ARM_GLOBAL_TIMER select CADENCE_TTC_TIMER select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD if SMP diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 19fc5f8883e0..9fa28237715a 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -360,6 +360,7 @@ config ARM_GLOBAL_TIMER config ARM_GT_INITIAL_PRESCALER_VAL int "ARM global timer initial prescaler value" + default 2 if ARCH_ZYNQ default 1 depends on ARM_GLOBAL_TIMER help From be534f8ee137b95046d7c53c8200ffdcf05781a7 Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Sat, 12 Jun 2021 17:27:26 +0800 Subject: [PATCH 11/14] clocksource/drivers/arm_global_timer: Make symbol 'gt_clk_rate_change_nb' static The sparse tool complains as follows: drivers/clocksource/arm_global_timer.c:54:23: warning: symbol 'gt_clk_rate_change_nb' was not declared. Should it be static? This symbol is not used outside of arm_global_timer.c, so mark it static. Reported-by: Hulk Robot Signed-off-by: Zou Wei Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1623490046-37972-1-git-send-email-zou_wei@huawei.com --- drivers/clocksource/arm_global_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c index 60a8047fd32e..68b1d144a412 100644 --- a/drivers/clocksource/arm_global_timer.c +++ b/drivers/clocksource/arm_global_timer.c @@ -51,7 +51,7 @@ * the units for all operations. */ static void __iomem *gt_base; -struct notifier_block gt_clk_rate_change_nb; +static struct notifier_block gt_clk_rate_change_nb; static u32 gt_psv_new, gt_psv_bck, gt_target_rate; static int gt_ppi; static struct clock_event_device __percpu *gt_evt; From f94bc2667fb204d7c131ac39d9ea342bd16116dc Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Tue, 15 Jun 2021 19:54:40 +0800 Subject: [PATCH 12/14] clocksource/drivers/arm_global_timer: Remove duplicated argument in arm_global_timer Fix the following coccicheck warning: drivers/clocksource/arm_global_timer.c:107:4-23: duplicated argument to & or | Signed-off-by: Wan Jiabing Reviewed-by: Patrice Chotard Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210615115440.8881-1-wanjiabing@vivo.com --- drivers/clocksource/arm_global_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c index 68b1d144a412..44a61dc6f932 100644 --- a/drivers/clocksource/arm_global_timer.c +++ b/drivers/clocksource/arm_global_timer.c @@ -104,7 +104,7 @@ static void gt_compare_set(unsigned long delta, int periodic) counter += delta; ctrl = readl(gt_base + GT_CONTROL); ctrl &= ~(GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE | - GT_CONTROL_AUTO_INC | GT_CONTROL_AUTO_INC); + GT_CONTROL_AUTO_INC); ctrl |= GT_CONTROL_TIMER_ENABLE; writel_relaxed(ctrl, gt_base + GT_CONTROL); writel_relaxed(lower_32_bits(counter), gt_base + GT_COMP0); From 8b33dfe0ba1c84c1aab2456590b38195837f1e6e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 14 May 2021 21:14:39 -0500 Subject: [PATCH 13/14] clocksource/arm_arch_timer: Improve Allwinner A64 timer workaround Bad counter reads are experienced sometimes when bit 10 or greater rolls over. Originally, testing showed that at least 10 lower bits would be set to the same value during these bad reads. However, some users still reported time skips. Wider testing revealed that on some chips, occasionally only the lowest 9 bits would read as the anomalous value. During these reads (which still happen only when bit 10), bit 9 would read as the correct value. Reduce the mask by one bit to cover these cases as well. Cc: stable@vger.kernel.org Fixes: c950ca8c35ee ("clocksource/drivers/arch_timer: Workaround for Allwinner A64 timer instability") Reported-by: Roman Stratiienko Signed-off-by: Samuel Holland Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210515021439.55316-1-samuel@sholland.org --- drivers/clocksource/arm_arch_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 89a9e0524555..be6d741d404c 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -364,7 +364,7 @@ static u64 notrace arm64_858921_read_cntvct_el0(void) do { \ _val = read_sysreg(reg); \ _retries--; \ - } while (((_val + 1) & GENMASK(9, 0)) <= 1 && _retries); \ + } while (((_val + 1) & GENMASK(8, 0)) <= 1 && _retries); \ \ WARN_ON_ONCE(!_retries); \ _val; \ From 3d41fff3ae3980c055f3c7861264c46c924f3e4c Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 18 May 2021 10:53:06 +0300 Subject: [PATCH 14/14] clocksource/drivers/timer-ti-dm: Drop unnecessary restore The device is not losing context on CPU_CLUSTER_PM_ERROR. As we are only saving and restoring context with cpu_pm, there is no need to restore the context in case of an error. Note that the unnecessary restoring of context does not cause issues, it's just not needed. Cc: Lokesh Vutla Signed-off-by: Tony Lindgren Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210518075306.35532-1-tony@atomide.com --- drivers/clocksource/timer-ti-dm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index e5c631f1b5cb..3e52c5226c4d 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -128,7 +128,8 @@ static int omap_timer_context_notifier(struct notifier_block *nb, break; omap_timer_save_context(timer); break; - case CPU_CLUSTER_PM_ENTER_FAILED: + case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */ + break; case CPU_CLUSTER_PM_EXIT: if ((timer->capability & OMAP_TIMER_ALWON) || !atomic_read(&timer->enabled))