From d6bfdeb4fde74c9a0e227cc1af9dda69c57489f4 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Jun 2025 17:18:45 +0200 Subject: [PATCH 01/52] clocksource/drivers/scx200: Add module owner The conversion to modules requires a correct handling of the module refcount in order to prevent to unload it if it is in use. That is especially true with the clockevents where there is no function to unregister them. The core time framework correctly handles the module refcount with the different clocksource and clockevents if the module owner is set. Add the module owner to make sure the core framework will prevent stupid things happening when the driver will be converted into a module. Signed-off-by: Daniel Lezcano Reviewed-by: Will McVicker Link: https://lore.kernel.org/r/20250602151853.1942521-2-daniel.lezcano@linaro.org --- drivers/clocksource/scx200_hrt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clocksource/scx200_hrt.c b/drivers/clocksource/scx200_hrt.c index c3536fffbe9a..5a99801a1657 100644 --- a/drivers/clocksource/scx200_hrt.c +++ b/drivers/clocksource/scx200_hrt.c @@ -52,6 +52,7 @@ static struct clocksource cs_hrt = { .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, /* mult, shift are set based on mhz27 flag */ + .owner = THIS_MODULE, }; static int __init init_hrt_clocksource(void) From 2cf51ab7f5c52f11dc12a11055f4b628121802d9 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Jun 2025 17:18:46 +0200 Subject: [PATCH 02/52] clocksource/drivers/stm32-lp: Add module owner The conversion to modules requires a correct handling of the module refcount in order to prevent to unload it if it is in use. That is especially true with the clockevents where there is no function to unregister them. The core time framework correctly handles the module refcount with the different clocksource and clockevents if the module owner is set. Add the module owner to make sure the core framework will prevent stupid things happening when the driver will be converted into a module. Signed-off-by: Daniel Lezcano Reviewed-by: Will McVicker Link: https://lore.kernel.org/r/20250602151853.1942521-3-daniel.lezcano@linaro.org --- drivers/clocksource/timer-stm32-lp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clocksource/timer-stm32-lp.c b/drivers/clocksource/timer-stm32-lp.c index 6e7944ffd7c0..c2a699f5c1dd 100644 --- a/drivers/clocksource/timer-stm32-lp.c +++ b/drivers/clocksource/timer-stm32-lp.c @@ -211,6 +211,7 @@ static void stm32_clkevent_lp_init(struct stm32_lp_private *priv, priv->clkevt.rating = STM32_LP_RATING; priv->clkevt.suspend = stm32_clkevent_lp_suspend; priv->clkevt.resume = stm32_clkevent_lp_resume; + priv->clkevt.owner = THIS_MODULE; clockevents_config_and_register(&priv->clkevt, rate, 0x1, STM32_LPTIM_MAX_ARR); From 376d11d32718c4d965714278ac6e6605d944fca2 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Jun 2025 17:18:47 +0200 Subject: [PATCH 03/52] clocksource/drivers/sun5i: Add module owner The conversion to modules requires a correct handling of the module refcount in order to prevent to unload it if it is in use. That is especially true with the clockevents where there is no function to unregister them. The core time framework correctly handles the module refcount with the different clocksource and clockevents if the module owner is set. Add the module owner to make sure the core framework will prevent stupid things happening when the driver will be converted into a module. Signed-off-by: Daniel Lezcano Reviewed-by: Will McVicker Acked-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20250602151853.1942521-4-daniel.lezcano@linaro.org --- drivers/clocksource/timer-sun5i.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index 6b48a9006444..f827d3f98f60 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -185,6 +185,7 @@ static int sun5i_setup_clocksource(struct platform_device *pdev, cs->clksrc.read = sun5i_clksrc_read; cs->clksrc.mask = CLOCKSOURCE_MASK(32); cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; + cs->clksrc.owner = THIS_MODULE; ret = clocksource_register_hz(&cs->clksrc, rate); if (ret) { @@ -214,6 +215,7 @@ static int sun5i_setup_clockevent(struct platform_device *pdev, ce->clkevt.rating = 340; ce->clkevt.irq = irq; ce->clkevt.cpumask = cpu_possible_mask; + ce->clkevt.owner = THIS_MODULE; /* Enable timer0 interrupt */ val = readl(base + TIMER_IRQ_EN_REG); From afe904f5091e2ceaa95b451f61a115a0224e8e38 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Jun 2025 17:18:48 +0200 Subject: [PATCH 04/52] clocksource/drivers/tegra186: Add module owner The conversion to modules requires a correct handling of the module refcount in order to prevent to unload it if it is in use. That is especially true with the clockevents where there is no function to unregister them. The core time framework correctly handles the module refcount with the different clocksource and clockevents if the module owner is set. Add the module owner to make sure the core framework will prevent stupid things happening when the driver will be converted into a module. Signed-off-by: Daniel Lezcano Reviewed-by: Will McVicker Link: https://lore.kernel.org/r/20250602151853.1942521-5-daniel.lezcano@linaro.org --- drivers/clocksource/timer-tegra186.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index e5394f98a02e..56a5342bcf78 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -373,6 +373,7 @@ static int tegra186_timer_tsc_init(struct tegra186_timer *tegra) tegra->tsc.read = tegra186_timer_tsc_read; tegra->tsc.mask = CLOCKSOURCE_MASK(56); tegra->tsc.flags = CLOCK_SOURCE_IS_CONTINUOUS; + tegra->tsc.owner = THIS_MODULE; return clocksource_register_hz(&tegra->tsc, 31250000); } @@ -392,6 +393,7 @@ static int tegra186_timer_osc_init(struct tegra186_timer *tegra) tegra->osc.read = tegra186_timer_osc_read; tegra->osc.mask = CLOCKSOURCE_MASK(32); tegra->osc.flags = CLOCK_SOURCE_IS_CONTINUOUS; + tegra->osc.owner = THIS_MODULE; return clocksource_register_hz(&tegra->osc, 38400000); } @@ -411,6 +413,7 @@ static int tegra186_timer_usec_init(struct tegra186_timer *tegra) tegra->usec.read = tegra186_timer_usec_read; tegra->usec.mask = CLOCKSOURCE_MASK(32); tegra->usec.flags = CLOCK_SOURCE_IS_CONTINUOUS; + tegra->usec.owner = THIS_MODULE; return clocksource_register_hz(&tegra->usec, USEC_PER_SEC); } From edef59887b5c5a527ad4b287a51f8f9fc239d867 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Jun 2025 17:18:49 +0200 Subject: [PATCH 05/52] clocksource/drivers/stm: Add module owner The conversion to modules requires a correct handling of the module refcount in order to prevent to unload it if it is in use. That is especially true with the clockevents where there is no function to unregister them. The core time framework correctly handles the module refcount with the different clocksource and clockevents if the module owner is set. Add the module owner to make sure the core framework will prevent stupid things happening when the driver will be converted into a module. Signed-off-by: Daniel Lezcano Reviewed-by: Will McVicker Link: https://lore.kernel.org/r/20250602151853.1942521-6-daniel.lezcano@linaro.org --- drivers/clocksource/timer-nxp-stm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clocksource/timer-nxp-stm.c b/drivers/clocksource/timer-nxp-stm.c index d7ccf9001729..bbc40623728f 100644 --- a/drivers/clocksource/timer-nxp-stm.c +++ b/drivers/clocksource/timer-nxp-stm.c @@ -201,6 +201,7 @@ static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer stm_timer->cs.resume = nxp_stm_clocksource_resume; stm_timer->cs.mask = CLOCKSOURCE_MASK(32); stm_timer->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; + stm_timer->cs.owner = THIS_MODULE; ret = clocksource_register_hz(&stm_timer->cs, stm_timer->rate); if (ret) @@ -314,6 +315,7 @@ static int __init nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm stm_timer->ced.cpumask = cpumask_of(cpu); stm_timer->ced.rating = 460; stm_timer->ced.irq = irq; + stm_timer->ced.owner = THIS_MODULE; per_cpu(stm_timers, cpu) = stm_timer; From eea65574e259f812e84080d56eca51f1a1889f8c Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Jun 2025 17:18:50 +0200 Subject: [PATCH 06/52] clocksource/drivers/cs5535: Add module owner The conversion to modules requires a correct handling of the module refcount in order to prevent to unload it if it is in use. That is especially true with the clockevents where there is no function to unregister them. The core time framework correctly handles the module refcount with the different clocksource and clockevents if the module owner is set. Add the module owner to make sure the core framework will prevent stupid things happening when the driver will be converted into a module. Signed-off-by: Daniel Lezcano Reviewed-by: Will McVicker Link: https://lore.kernel.org/r/20250602151853.1942521-7-daniel.lezcano@linaro.org --- drivers/clocksource/timer-cs5535.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clocksource/timer-cs5535.c b/drivers/clocksource/timer-cs5535.c index d47acfe848ae..8af666c39890 100644 --- a/drivers/clocksource/timer-cs5535.c +++ b/drivers/clocksource/timer-cs5535.c @@ -101,6 +101,7 @@ static struct clock_event_device cs5535_clockevent = { .tick_resume = mfgpt_shutdown, .set_next_event = mfgpt_next_event, .rating = 250, + .owner = THIS_MODULE, }; static irqreturn_t mfgpt_tick(int irq, void *dev_id) From 84b1a903aed876a3fd6bb04786947f640d4d8e62 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Jun 2025 17:18:51 +0200 Subject: [PATCH 07/52] time/sched_clock: Export symbol for sched_clock register function The timer drivers could be converted into modules. The different functions to register the clocksource or the clockevent are already exporting their symbols for modules but the sched_clock_register() function is missing. Export the symbols so the drivers using this function can be converted into modules. Signed-off-by: Daniel Lezcano Reviewed-by: Thomas Gleixner Reviewed-by: Carlos Llamas Reviewed-by: Will McVicker Acked-by: John Stultz Link: https://lore.kernel.org/r/20250602151853.1942521-8-daniel.lezcano@linaro.org --- kernel/time/sched_clock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c index cc15fe293719..cc1afec306b3 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -174,8 +174,7 @@ static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt) return HRTIMER_RESTART; } -void __init -sched_clock_register(u64 (*read)(void), int bits, unsigned long rate) +void sched_clock_register(u64 (*read)(void), int bits, unsigned long rate) { u64 res, wrap, new_mask, new_epoch, cyc, ns; u32 new_mult, new_shift; @@ -247,6 +246,7 @@ sched_clock_register(u64 (*read)(void), int bits, unsigned long rate) pr_debug("Registered %pS as sched_clock source\n", read); } +EXPORT_SYMBOL_GPL(sched_clock_register); void __init generic_sched_clock_init(void) { From ef0e000cd162a58dba10608ef6959f172d1bc5f4 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 11 Jun 2025 18:26:20 -0500 Subject: [PATCH 08/52] dt-bindings: timer: Convert faraday,fttmr010 to DT schema Convert the Faraday fttmr010 Timer binding to DT schema format. Adjust the compatible string values to match what's in use. The number of interrupts can also be anywhere from 1 to 8. The clock-names order was reversed compared to what's used. Signed-off-by: Rob Herring (Arm) Signed-off-by: Daniel Lezcano Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250611232621.1508116-1-robh@kernel.org --- .../bindings/timer/faraday,fttmr010.txt | 38 -------- .../bindings/timer/faraday,fttmr010.yaml | 89 +++++++++++++++++++ 2 files changed, 89 insertions(+), 38 deletions(-) delete mode 100644 Documentation/devicetree/bindings/timer/faraday,fttmr010.txt create mode 100644 Documentation/devicetree/bindings/timer/faraday,fttmr010.yaml diff --git a/Documentation/devicetree/bindings/timer/faraday,fttmr010.txt b/Documentation/devicetree/bindings/timer/faraday,fttmr010.txt deleted file mode 100644 index 3cb2f4c98d64..000000000000 --- a/Documentation/devicetree/bindings/timer/faraday,fttmr010.txt +++ /dev/null @@ -1,38 +0,0 @@ -Faraday Technology timer - -This timer is a generic IP block from Faraday Technology, embedded in the -Cortina Systems Gemini SoCs and other designs. - -Required properties: - -- compatible : Must be one of - "faraday,fttmr010" - "cortina,gemini-timer", "faraday,fttmr010" - "moxa,moxart-timer", "faraday,fttmr010" - "aspeed,ast2400-timer" - "aspeed,ast2500-timer" - "aspeed,ast2600-timer" - -- reg : Should contain registers location and length -- interrupts : Should contain the three timer interrupts usually with - flags for falling edge - -Optionally required properties: - -- clocks : a clock to provide the tick rate for "faraday,fttmr010" -- clock-names : should be "EXTCLK" and "PCLK" for the external tick timer - and peripheral clock respectively, for "faraday,fttmr010" -- syscon : a phandle to the global Gemini system controller if the compatible - type is "cortina,gemini-timer" - -Example: - -timer@43000000 { - compatible = "faraday,fttmr010"; - reg = <0x43000000 0x1000>; - interrupts = <14 IRQ_TYPE_EDGE_FALLING>, /* Timer 1 */ - <15 IRQ_TYPE_EDGE_FALLING>, /* Timer 2 */ - <16 IRQ_TYPE_EDGE_FALLING>; /* Timer 3 */ - clocks = <&extclk>, <&pclk>; - clock-names = "EXTCLK", "PCLK"; -}; diff --git a/Documentation/devicetree/bindings/timer/faraday,fttmr010.yaml b/Documentation/devicetree/bindings/timer/faraday,fttmr010.yaml new file mode 100644 index 000000000000..39506323556c --- /dev/null +++ b/Documentation/devicetree/bindings/timer/faraday,fttmr010.yaml @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/faraday,fttmr010.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Faraday FTTMR010 timer + +maintainers: + - Joel Stanley + - Linus Walleij + +description: + This timer is a generic IP block from Faraday Technology, embedded in the + Cortina Systems Gemini SoCs and other designs. + +properties: + compatible: + oneOf: + - items: + - const: moxa,moxart-timer + - const: faraday,fttmr010 + - enum: + - aspeed,ast2400-timer + - aspeed,ast2500-timer + - aspeed,ast2600-timer + - cortina,gemini-timer + - faraday,fttmr010 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 8 + description: One interrupt per timer + + clocks: + minItems: 1 + items: + - description: Peripheral clock + - description: External tick clock + + clock-names: + minItems: 1 + items: + - const: PCLK + - const: EXTCLK + + resets: + maxItems: 1 + + syscon: + description: System controller phandle for Gemini systems + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - reg + - interrupts + +allOf: + - if: + properties: + compatible: + contains: + const: cortina,gemini-timer + then: + required: + - syscon + else: + properties: + syscon: false + +additionalProperties: false + +examples: + - | + #include + + timer@43000000 { + compatible = "faraday,fttmr010"; + reg = <0x43000000 0x1000>; + interrupts = <14 IRQ_TYPE_EDGE_FALLING>, /* Timer 1 */ + <15 IRQ_TYPE_EDGE_FALLING>, /* Timer 2 */ + <16 IRQ_TYPE_EDGE_FALLING>; /* Timer 3 */ + clocks = <&pclk>, <&extclk>; + clock-names = "PCLK", "EXTCLK"; + }; From bb7bf8b44de1b13b8b7b10ca166b545ff22edac9 Mon Sep 17 00:00:00 2001 From: Max Shevchenko Date: Wed, 2 Jul 2025 13:50:40 +0300 Subject: [PATCH 09/52] dt-bindings: timer: mediatek: add MT6572 Add a compatible string for timer on the MT6572 SoC. Signed-off-by: Max Shevchenko Signed-off-by: Daniel Lezcano Reviewed-by: AngeloGioacchino Del Regno Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250702-mt6572-v4-3-bde75b7ed445@proton.me --- Documentation/devicetree/bindings/timer/mediatek,timer.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/timer/mediatek,timer.yaml b/Documentation/devicetree/bindings/timer/mediatek,timer.yaml index f68fc7050c56..d5b574bfd2ca 100644 --- a/Documentation/devicetree/bindings/timer/mediatek,timer.yaml +++ b/Documentation/devicetree/bindings/timer/mediatek,timer.yaml @@ -26,6 +26,7 @@ properties: - items: - enum: - mediatek,mt2701-timer + - mediatek,mt6572-timer - mediatek,mt6580-timer - mediatek,mt6582-timer - mediatek,mt6589-timer From c1ff9e919addb8cf0414b08bd996f11a4a2e7297 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Fri, 23 May 2025 10:14:37 -0400 Subject: [PATCH 10/52] dt-bindings: timer: fsl,ftm-timer: use items for reg The original txt binding doc is: reg : Specifies base physical address and size of the register sets for the clock event device and clock source device. And existed dts provide two reg MMIO spaces. So change to use items to descript reg property. Update examples. Fixes: 8fc30d8f8e86 ("dt-bindings: timer: fsl,ftm-timer: Convert to dtschema") Signed-off-by: Frank Li Signed-off-by: Daniel Lezcano Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250523141437.533643-1-Frank.Li@nxp.com --- Documentation/devicetree/bindings/timer/fsl,ftm-timer.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/timer/fsl,ftm-timer.yaml b/Documentation/devicetree/bindings/timer/fsl,ftm-timer.yaml index 0e4a8ddc3de3..e3b61b62521e 100644 --- a/Documentation/devicetree/bindings/timer/fsl,ftm-timer.yaml +++ b/Documentation/devicetree/bindings/timer/fsl,ftm-timer.yaml @@ -14,7 +14,9 @@ properties: const: fsl,ftm-timer reg: - maxItems: 1 + items: + - description: clock event device + - description: clock source device interrupts: maxItems: 1 @@ -50,7 +52,8 @@ examples: ftm@400b8000 { compatible = "fsl,ftm-timer"; - reg = <0x400b8000 0x1000>; + reg = <0x400b8000 0x1000>, + <0x400b9000 0x1000>; interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>; clock-names = "ftm-evt", "ftm-src", "ftm-evt-counter-en", "ftm-src-counter-en"; clocks = <&clks VF610_CLK_FTM2>, <&clks VF610_CLK_FTM3>, From be26ec8b1479a0bb1888ed93d7bb5ce4d8eaee1e Mon Sep 17 00:00:00 2001 From: Will McVicker Date: Fri, 20 Jun 2025 11:17:04 -0700 Subject: [PATCH 11/52] of/irq: Export of_irq_count for modules Need to export `of_irq_count` in preparation for modularizing the Exynos MCT driver which uses this API for setting up the timer IRQs. Signed-off-by: Will McVicker Signed-off-by: Daniel Lezcano Tested-by: Youngmin Nam Reviewed-by: Linus Walleij Reviewed-by: Youngmin Nam Acked-by: Rob Herring (Arm) Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250620181719.1399856-2-willmcvicker@google.com --- drivers/of/irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 74aaea61de13..d2b690857e58 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -519,6 +519,7 @@ int of_irq_count(struct device_node *dev) return nr; } +EXPORT_SYMBOL_GPL(of_irq_count); /** * of_irq_to_resource_table - Fill in resource table with node's IRQ info From 916aa36042db8ee230543ffe0d192f900e8b8c9f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 14 Jun 2025 10:55:55 -0700 Subject: [PATCH 12/52] clocksource/drivers/timer-tegra186: Avoid 64-bit divide operation Building the driver on xtensa fails with tensa-linux-ld: drivers/clocksource/timer-tegra186.o: in function `tegra186_timer_remove': timer-tegra186.c:(.text+0x350): undefined reference to `__udivdi3' Avoid the problem by rearranging the offending code to avoid the 64-bit divide operation. Fixes: 28c842c8b0f5 ("clocksource/drivers/timer-tegra186: Add WDIOC_GETTIMELEFT support") Signed-off-by: Guenter Roeck Signed-off-by: Daniel Lezcano Reviewed-by: Jon Hunter Cc: Pohsun Su Cc: Robert Lin Link: https://lore.kernel.org/r/20250614175556.922159-1-linux@roeck-us.net --- drivers/clocksource/timer-tegra186.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index 56a5342bcf78..76a5481c8eb6 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -267,7 +267,7 @@ static unsigned int tegra186_wdt_get_timeleft(struct watchdog_device *wdd) * counter value to the time of the counter expirations that * remain. */ - timeleft += (((u64)wdt->base.timeout * USEC_PER_SEC) / 5) * (4 - expiration); + timeleft += ((u64)wdt->base.timeout * (USEC_PER_SEC / 5)) * (4 - expiration); /* * Convert the current counter value to seconds, From 7f3abae5b447a7f8458a0f58a003c11c46aade99 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 14 Jun 2025 10:55:56 -0700 Subject: [PATCH 13/52] clocksource/drivers/timer-tegra186: Simplify calculating timeleft It is not necessary to use 64-bit operations to calculate the remaining watchdog timeout. Simplify to use 32-bit operations, and add comments explaining why there will be no overflow. Signed-off-by: Guenter Roeck Signed-off-by: Daniel Lezcano Reviewed-by: Jon Hunter Cc: Pohsun Su Cc: Robert Lin Link: https://lore.kernel.org/r/20250614175556.922159-2-linux@roeck-us.net --- drivers/clocksource/timer-tegra186.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index 76a5481c8eb6..d403a3fe25e7 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -231,7 +231,7 @@ static unsigned int tegra186_wdt_get_timeleft(struct watchdog_device *wdd) { struct tegra186_wdt *wdt = to_tegra186_wdt(wdd); u32 expiration, val; - u64 timeleft; + u32 timeleft; if (!watchdog_active(&wdt->base)) { /* return zero if the watchdog timer is not activated. */ @@ -266,21 +266,26 @@ static unsigned int tegra186_wdt_get_timeleft(struct watchdog_device *wdd) * Calculate the time remaining by adding the time for the * counter value to the time of the counter expirations that * remain. + * Note: Since wdt->base.timeout is bound to 255, the maximum + * value added to timeleft is + * 255 * (1,000,000 / 5) * 4 + * = 255 * 200,000 * 4 + * = 204,000,000 + * TMRSR_PCV is a 29-bit field. + * Its maximum value is 0x1fffffff = 536,870,911. + * 204,000,000 + 536,870,911 = 740,870,911 = 0x2C28CAFF. + * timeleft can therefore not overflow, and 64-bit calculations + * are not necessary. */ - timeleft += ((u64)wdt->base.timeout * (USEC_PER_SEC / 5)) * (4 - expiration); + timeleft += (wdt->base.timeout * (USEC_PER_SEC / 5)) * (4 - expiration); /* * Convert the current counter value to seconds, - * rounding up to the nearest second. Cast u64 to - * u32 under the assumption that no overflow happens - * when coverting to seconds. + * rounding to the nearest second. */ - timeleft = DIV_ROUND_CLOSEST_ULL(timeleft, USEC_PER_SEC); + timeleft = DIV_ROUND_CLOSEST(timeleft, USEC_PER_SEC); - if (WARN_ON_ONCE(timeleft > U32_MAX)) - return U32_MAX; - - return lower_32_bits(timeleft); + return timeleft; } static const struct watchdog_ops tegra186_wdt_ops = { From 409f8fe03e08f92bf5be96cedbcd7a3e8fb2eeaf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 20 Jun 2025 13:19:35 +0200 Subject: [PATCH 14/52] clocksource/drivers/tegra186: Avoid 64-bit division The newly added function causes a build failure on 32-bit targets with older compiler version such as gcc-10: arm-linux-gnueabi-ld: drivers/clocksource/timer-tegra186.o: in function `tegra186_wdt_get_timeleft': timer-tegra186.c:(.text+0x3c2): undefined reference to `__aeabi_uldivmod' The calculation can trivially be changed to avoid the division entirely, as USEC_PER_SEC is a multiple of 5. Change both such calculation for consistency, even though gcc apparently managed to optimize the other one properly already. [dlezcano : Fixed conflict with 20250614175556.922159-2-linux@roeck-us.net ] Fixes: 28c842c8b0f5 ("clocksource/drivers/timer-tegra186: Add WDIOC_GETTIMELEFT support") Signed-off-by: Arnd Bergmann Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250620111939.3395525-1-arnd@kernel.org --- drivers/clocksource/timer-tegra186.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index d403a3fe25e7..1ec7b38ff8c6 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -159,7 +159,7 @@ static void tegra186_wdt_enable(struct tegra186_wdt *wdt) tmr_writel(wdt->tmr, TMRCSSR_SRC_USEC, TMRCSSR); /* configure timer (system reset happens on the fifth expiration) */ - value = TMRCR_PTV(wdt->base.timeout * USEC_PER_SEC / 5) | + value = TMRCR_PTV(wdt->base.timeout * (USEC_PER_SEC / 5)) | TMRCR_PERIODIC | TMRCR_ENABLE; tmr_writel(wdt->tmr, value, TMRCR); From ffc5870fc4e0ea72bf73ad5ab1d648aa0b4f7cbf Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 28 May 2025 12:53:50 -0400 Subject: [PATCH 15/52] dt-bindings: timer: Add fsl,timrot.yaml Add fsl,timrot.yaml for i.MX23/i.MX28 timer. Also add a generic fallback compatible string "fsl,timrot" for legacy devices, which have existed for over 15 years. Signed-off-by: Frank Li Signed-off-by: Daniel Lezcano Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250528165351.691848-1-Frank.Li@nxp.com --- .../devicetree/bindings/timer/fsl,timrot.yaml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/fsl,timrot.yaml diff --git a/Documentation/devicetree/bindings/timer/fsl,timrot.yaml b/Documentation/devicetree/bindings/timer/fsl,timrot.yaml new file mode 100644 index 000000000000..d181f274ef9f --- /dev/null +++ b/Documentation/devicetree/bindings/timer/fsl,timrot.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/fsl,timrot.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale MXS Timer + +maintainers: + - Frank Li + +properties: + compatible: + items: + - enum: + - fsl,imx23-timrot + - fsl,imx28-timrot + - const: fsl,timrot + + reg: + maxItems: 1 + + interrupts: + items: + - description: irq for timer0 + - description: irq for timer1 + - description: irq for timer2 + - description: irq for timer3 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + timer: timer@80068000 { + compatible = "fsl,imx28-timrot", "fsl,timrot"; + reg = <0x80068000 0x2000>; + interrupts = <48>, <49>, <50>, <51>; + clocks = <&clks 26>; + }; From d27b4e33c954f5fa6b6e366736838c98d3996f02 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 3 Jun 2025 14:04:50 +0800 Subject: [PATCH 16/52] clocksource/timer-econet-en751221: Convert comma to semicolon Replace comma between expressions with semicolons. Using a ',' in place of a ';' can have unintended side effects. Although that is not the case here, it is seems best to use ';' unless ',' is intended. Found by inspection. No functional change intended. Compile tested only. Signed-off-by: Chen Ni Signed-off-by: Daniel Lezcano Tested-by: Caleb James DeLisle Link: https://lore.kernel.org/r/20250603060450.1310204-1-nichen@iscas.ac.cn --- drivers/clocksource/timer-econet-en751221.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c index 3b449fdaafee..4008076b1a21 100644 --- a/drivers/clocksource/timer-econet-en751221.c +++ b/drivers/clocksource/timer-econet-en751221.c @@ -146,7 +146,7 @@ static int __init cevt_init(struct device_node *np) for_each_possible_cpu(i) { struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, i); - cd->rating = 310, + cd->rating = 310; cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP | CLOCK_EVT_FEAT_PERCPU; From 99d19715daf5553a28452a4ef95e9692277e2787 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 11 Jun 2025 13:07:58 +0200 Subject: [PATCH 17/52] dt-bindings: timer: mediatek,timer: Add MediaTek MT8196 compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new compatible for the MediaTek MT8196 SoC, fully compatible with MT6765. Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Daniel Lezcano Reviewed-by: Nícolas F. R. A. Prado Acked-by: Rob Herring (Arm) Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250611110800.458164-2-angelogioacchino.delregno@collabora.com --- Documentation/devicetree/bindings/timer/mediatek,timer.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/timer/mediatek,timer.yaml b/Documentation/devicetree/bindings/timer/mediatek,timer.yaml index d5b574bfd2ca..e3e38066c2cb 100644 --- a/Documentation/devicetree/bindings/timer/mediatek,timer.yaml +++ b/Documentation/devicetree/bindings/timer/mediatek,timer.yaml @@ -45,6 +45,7 @@ properties: - mediatek,mt8188-timer - mediatek,mt8192-timer - mediatek,mt8195-timer + - mediatek,mt8196-timer - mediatek,mt8365-systimer - const: mediatek,mt6765-timer From 0b781f527d6f99e68e5b3780ae03cd69a7cb5c0c Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:19 +0200 Subject: [PATCH 18/52] clocksource/drivers/vf-pit: Replace raw_readl/writel to readl/writel The driver uses the raw_readl() and raw_writel() functions. Those are not for MMIO devices. Replace them with readl() and writel() [ dlezcano: Fixed typo in the subject s/reald/readl/ ] Signed-off-by: Daniel Lezcano Acked-by: Arnd Bergmann Cc: Arnd Bergmann Link: https://lore.kernel.org/r/20250804152344.1109310-2-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 911c92146eca..8041a8f62d1f 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -35,30 +35,30 @@ static unsigned long cycle_per_jiffy; static inline void pit_timer_enable(void) { - __raw_writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL); + writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL); } static inline void pit_timer_disable(void) { - __raw_writel(0, clkevt_base + PITTCTRL); + writel(0, clkevt_base + PITTCTRL); } static inline void pit_irq_acknowledge(void) { - __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); + writel(PITTFLG_TIF, clkevt_base + PITTFLG); } static u64 notrace pit_read_sched_clock(void) { - return ~__raw_readl(clksrc_base + PITCVAL); + return ~readl(clksrc_base + PITCVAL); } static int __init pit_clocksource_init(unsigned long rate) { /* set the max load value and start the clock source counter */ - __raw_writel(0, clksrc_base + PITTCTRL); - __raw_writel(~0UL, clksrc_base + PITLDVAL); - __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); + writel(0, clksrc_base + PITTCTRL); + writel(~0UL, clksrc_base + PITLDVAL); + writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); sched_clock_register(pit_read_sched_clock, 32, rate); return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate, @@ -76,7 +76,7 @@ static int pit_set_next_event(unsigned long delta, * hardware requirement. */ pit_timer_disable(); - __raw_writel(delta - 1, clkevt_base + PITLDVAL); + writel(delta - 1, clkevt_base + PITLDVAL); pit_timer_enable(); return 0; @@ -125,8 +125,8 @@ static struct clock_event_device clockevent_pit = { static int __init pit_clockevent_init(unsigned long rate, int irq) { - __raw_writel(0, clkevt_base + PITTCTRL); - __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); + writel(0, clkevt_base + PITTCTRL); + writel(PITTFLG_TIF, clkevt_base + PITTFLG); BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, "VF pit timer", &clockevent_pit)); @@ -183,7 +183,7 @@ static int __init pit_timer_init(struct device_node *np) cycle_per_jiffy = clk_rate / (HZ); /* enable the pit module */ - __raw_writel(~PITMCR_MDIS, timer_base + PITMCR); + writel(~PITMCR_MDIS, timer_base + PITMCR); ret = pit_clocksource_init(clk_rate); if (ret) From 2decd0b63eb0a6773e9a5541300cd92e469f541b Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:20 +0200 Subject: [PATCH 19/52] clocksource/drivers/vf-pit: Add COMPILE_TEST option The VF PIT driver is a silent koption. In order to allow a better compilation test coverage, let's add the COMPILE_TEST option so it can be selected on other platforms than the Vybrid Family. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-3-daniel.lezcano@linaro.org --- drivers/clocksource/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 645f517a1ac2..6f7d371904df 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -475,7 +475,7 @@ config FSL_FTM_TIMER Support for Freescale FlexTimer Module (FTM) timer. config VF_PIT_TIMER - bool + bool "Vybrid Family Programmable timer" if COMPILE_TEST select CLKSRC_MMIO help Support for Periodic Interrupt Timer on Freescale Vybrid Family SoCs. From 3996232e6e7e34a1f783c778f6c7075293912365 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:21 +0200 Subject: [PATCH 20/52] clocksource/drivers/vf-pit: Set the scene for multiple timers The driver is implemented as using a single timer and a single clocksource. In order to take advantage of the multiple timers supported in the PIT hardware and introduce different setup for a new platform, let's encapsulate the data into a structure and pass this structure around in the function parameter. The structure will be a per timer instansiation in the next changes. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-4-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 121 +++++++++++++++++------------ 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 8041a8f62d1f..e4a8b32fff75 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -15,7 +15,7 @@ */ #define PITMCR 0x00 #define PIT0_OFFSET 0x100 -#define PITn_OFFSET(n) (PIT0_OFFSET + 0x10 * (n)) +#define PIT_CH(n) (PIT0_OFFSET + 0x10 * (n)) #define PITLDVAL 0x00 #define PITCVAL 0x04 #define PITTCTRL 0x08 @@ -29,23 +29,36 @@ #define PITTFLG_TIF 0x1 +struct pit_timer { + void __iomem *clksrc_base; + void __iomem *clkevt_base; + unsigned long cycle_per_jiffy; + struct clock_event_device ced; + struct clocksource cs; +}; + +static struct pit_timer pit_timer; + static void __iomem *clksrc_base; -static void __iomem *clkevt_base; -static unsigned long cycle_per_jiffy; -static inline void pit_timer_enable(void) +static inline struct pit_timer *ced_to_pit(struct clock_event_device *ced) { - writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL); + return container_of(ced, struct pit_timer, ced); } -static inline void pit_timer_disable(void) +static inline void pit_timer_enable(struct pit_timer *pit) { - writel(0, clkevt_base + PITTCTRL); + writel(PITTCTRL_TEN | PITTCTRL_TIE, pit->clkevt_base + PITTCTRL); } -static inline void pit_irq_acknowledge(void) +static inline void pit_timer_disable(struct pit_timer *pit) { - writel(PITTFLG_TIF, clkevt_base + PITTFLG); + writel(0, pit->clkevt_base + PITTCTRL); +} + +static inline void pit_irq_acknowledge(struct pit_timer *pit) +{ + writel(PITTFLG_TIF, pit->clkevt_base + PITTFLG); } static u64 notrace pit_read_sched_clock(void) @@ -53,21 +66,24 @@ static u64 notrace pit_read_sched_clock(void) return ~readl(clksrc_base + PITCVAL); } -static int __init pit_clocksource_init(unsigned long rate) +static int __init pit_clocksource_init(struct pit_timer *pit, unsigned long rate) { /* set the max load value and start the clock source counter */ - writel(0, clksrc_base + PITTCTRL); - writel(~0UL, clksrc_base + PITLDVAL); - writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); + writel(0, pit->clksrc_base + PITTCTRL); + writel(~0, pit->clksrc_base + PITLDVAL); + writel(PITTCTRL_TEN, pit->clksrc_base + PITTCTRL); + + clksrc_base = pit->clksrc_base; sched_clock_register(pit_read_sched_clock, 32, rate); - return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate, + return clocksource_mmio_init(pit->clksrc_base + PITCVAL, "vf-pit", rate, 300, 32, clocksource_mmio_readl_down); } -static int pit_set_next_event(unsigned long delta, - struct clock_event_device *unused) +static int pit_set_next_event(unsigned long delta, struct clock_event_device *ced) { + struct pit_timer *pit = ced_to_pit(ced); + /* * set a new value to PITLDVAL register will not restart the timer, * to abort the current cycle and start a timer period with the new @@ -75,30 +91,37 @@ static int pit_set_next_event(unsigned long delta, * and the PITLAVAL should be set to delta minus one according to pit * hardware requirement. */ - pit_timer_disable(); - writel(delta - 1, clkevt_base + PITLDVAL); - pit_timer_enable(); + pit_timer_disable(pit); + writel(delta - 1, pit->clkevt_base + PITLDVAL); + pit_timer_enable(pit); return 0; } -static int pit_shutdown(struct clock_event_device *evt) +static int pit_shutdown(struct clock_event_device *ced) { - pit_timer_disable(); + struct pit_timer *pit = ced_to_pit(ced); + + pit_timer_disable(pit); + return 0; } -static int pit_set_periodic(struct clock_event_device *evt) +static int pit_set_periodic(struct clock_event_device *ced) { - pit_set_next_event(cycle_per_jiffy, evt); + struct pit_timer *pit = ced_to_pit(ced); + + pit_set_next_event(pit->cycle_per_jiffy, ced); + return 0; } static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) { - struct clock_event_device *evt = dev_id; + struct clock_event_device *ced = dev_id; + struct pit_timer *pit = ced_to_pit(ced); - pit_irq_acknowledge(); + pit_irq_acknowledge(pit); /* * pit hardware doesn't support oneshot, it will generate an interrupt @@ -106,33 +129,33 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) * and start the counter again. So software need to disable the timer * to stop the counter loop in ONESHOT mode. */ - if (likely(clockevent_state_oneshot(evt))) - pit_timer_disable(); + if (likely(clockevent_state_oneshot(ced))) + pit_timer_disable(pit); - evt->event_handler(evt); + ced->event_handler(ced); return IRQ_HANDLED; } -static struct clock_event_device clockevent_pit = { - .name = "VF pit timer", - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_state_shutdown = pit_shutdown, - .set_state_periodic = pit_set_periodic, - .set_next_event = pit_set_next_event, - .rating = 300, -}; - -static int __init pit_clockevent_init(unsigned long rate, int irq) +static int __init pit_clockevent_init(struct pit_timer *pit, unsigned long rate, int irq) { - writel(0, clkevt_base + PITTCTRL); - writel(PITTFLG_TIF, clkevt_base + PITTFLG); + writel(0, pit->clkevt_base + PITTCTRL); + + writel(PITTFLG_TIF, pit->clkevt_base + PITTFLG); BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, - "VF pit timer", &clockevent_pit)); + "VF pit timer", &pit->ced)); + + pit->ced.cpumask = cpumask_of(0); + pit->ced.irq = irq; + + pit->ced.name = "VF pit timer"; + pit->ced.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + pit->ced.set_state_shutdown = pit_shutdown; + pit->ced.set_state_periodic = pit_set_periodic; + pit->ced.set_next_event = pit_set_next_event; + pit->ced.rating = 300; - clockevent_pit.cpumask = cpumask_of(0); - clockevent_pit.irq = irq; /* * The value for the LDVAL register trigger is calculated as: * LDVAL trigger = (period / clock period) - 1 @@ -141,7 +164,7 @@ static int __init pit_clockevent_init(unsigned long rate, int irq) * LDVAL trigger value is 1. And then the min_delta is * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit. */ - clockevents_config_and_register(&clockevent_pit, rate, 2, 0xffffffff); + clockevents_config_and_register(&pit->ced, rate, 2, 0xffffffff); return 0; } @@ -164,8 +187,8 @@ static int __init pit_timer_init(struct device_node *np) * so choose PIT2 as clocksource, PIT3 as clockevent device, * and leave PIT0 and PIT1 unused for anyone else who needs them. */ - clksrc_base = timer_base + PITn_OFFSET(2); - clkevt_base = timer_base + PITn_OFFSET(3); + pit_timer.clksrc_base = timer_base + PIT_CH(2); + pit_timer.clkevt_base = timer_base + PIT_CH(3); irq = irq_of_parse_and_map(np, 0); if (irq <= 0) @@ -180,15 +203,15 @@ static int __init pit_timer_init(struct device_node *np) return ret; clk_rate = clk_get_rate(pit_clk); - cycle_per_jiffy = clk_rate / (HZ); + pit_timer.cycle_per_jiffy = clk_rate / (HZ); /* enable the pit module */ writel(~PITMCR_MDIS, timer_base + PITMCR); - ret = pit_clocksource_init(clk_rate); + ret = pit_clocksource_init(&pit_timer, clk_rate); if (ret) return ret; - return pit_clockevent_init(clk_rate, irq); + return pit_clockevent_init(&pit_timer, clk_rate, irq); } TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); From 361580317976ff25ce4eddbf207d06b0aca9ab22 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:22 +0200 Subject: [PATCH 21/52] clocksource/drivers/vf-pit: Rework the base address usage This change passes the base address to the clockevent and clocksource initialization functions in order to use different base address in the next changes. No functional changes intended. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-5-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 35 +++++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index e4a8b32fff75..6a5f940ad0bc 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -66,8 +66,16 @@ static u64 notrace pit_read_sched_clock(void) return ~readl(clksrc_base + PITCVAL); } -static int __init pit_clocksource_init(struct pit_timer *pit, unsigned long rate) +static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base, + unsigned long rate) { + /* + * The channels 0 and 1 can be chained to build a 64-bit + * timer. Let's use the channel 2 as a clocksource and leave + * the channels 0 and 1 unused for anyone else who needs them + */ + pit->clksrc_base = base + PIT_CH(2); + /* set the max load value and start the clock source counter */ writel(0, pit->clksrc_base + PITTCTRL); writel(~0, pit->clksrc_base + PITLDVAL); @@ -76,8 +84,9 @@ static int __init pit_clocksource_init(struct pit_timer *pit, unsigned long rate clksrc_base = pit->clksrc_base; sched_clock_register(pit_read_sched_clock, 32, rate); + return clocksource_mmio_init(pit->clksrc_base + PITCVAL, "vf-pit", rate, - 300, 32, clocksource_mmio_readl_down); + 300, 32, clocksource_mmio_readl_down); } static int pit_set_next_event(unsigned long delta, struct clock_event_device *ced) @@ -137,8 +146,16 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init pit_clockevent_init(struct pit_timer *pit, unsigned long rate, int irq) +static int __init pit_clockevent_init(struct pit_timer *pit, void __iomem *base, + unsigned long rate, int irq) { + /* + * The channels 0 and 1 can be chained to build a 64-bit + * timer. Let's use the channel 3 as a clockevent and leave + * the channels 0 and 1 unused for anyone else who needs them + */ + pit->clkevt_base = base + PIT_CH(3); + writel(0, pit->clkevt_base + PITTCTRL); writel(PITTFLG_TIF, pit->clkevt_base + PITTFLG); @@ -182,14 +199,6 @@ static int __init pit_timer_init(struct device_node *np) return -ENXIO; } - /* - * PIT0 and PIT1 can be chained to build a 64-bit timer, - * so choose PIT2 as clocksource, PIT3 as clockevent device, - * and leave PIT0 and PIT1 unused for anyone else who needs them. - */ - pit_timer.clksrc_base = timer_base + PIT_CH(2); - pit_timer.clkevt_base = timer_base + PIT_CH(3); - irq = irq_of_parse_and_map(np, 0); if (irq <= 0) return -EINVAL; @@ -208,10 +217,10 @@ static int __init pit_timer_init(struct device_node *np) /* enable the pit module */ writel(~PITMCR_MDIS, timer_base + PITMCR); - ret = pit_clocksource_init(&pit_timer, clk_rate); + ret = pit_clocksource_init(&pit_timer, timer_base, clk_rate); if (ret) return ret; - return pit_clockevent_init(&pit_timer, clk_rate, irq); + return pit_clockevent_init(&pit_timer, timer_base, clk_rate, irq); } TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); From 995ebf16043392d131bd22ed8484c9c952bfb45f Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:23 +0200 Subject: [PATCH 22/52] clocksource/drivers/vf-pit: Pass the cpu number as parameter In order to initialize the timer with a cpumask tied to a cpu, let's pass it as a parameter instead of hardwiring it in the init function. No functional changes intended. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-6-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 6a5f940ad0bc..9f3b72be987a 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -147,7 +147,7 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) } static int __init pit_clockevent_init(struct pit_timer *pit, void __iomem *base, - unsigned long rate, int irq) + unsigned long rate, int irq, unsigned int cpu) { /* * The channels 0 and 1 can be chained to build a 64-bit @@ -163,7 +163,7 @@ static int __init pit_clockevent_init(struct pit_timer *pit, void __iomem *base, BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, "VF pit timer", &pit->ced)); - pit->ced.cpumask = cpumask_of(0); + pit->ced.cpumask = cpumask_of(cpu); pit->ced.irq = irq; pit->ced.name = "VF pit timer"; @@ -221,6 +221,6 @@ static int __init pit_timer_init(struct device_node *np) if (ret) return ret; - return pit_clockevent_init(&pit_timer, timer_base, clk_rate, irq); + return pit_clockevent_init(&pit_timer, timer_base, clk_rate, irq, 0); } TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); From 8b0795e0fc0cf181ecab59d3a9a1618886ea995b Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:24 +0200 Subject: [PATCH 23/52] clocksource/drivers/vf-pit: Encapsulate the initialization of the cycles_per_jiffy Move the cycles_per_jiffy initialization to the same place where the other pit timer fields are initialized. No functional changes intended. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-7-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 9f3b72be987a..a11e6a63c79f 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -155,6 +155,7 @@ static int __init pit_clockevent_init(struct pit_timer *pit, void __iomem *base, * the channels 0 and 1 unused for anyone else who needs them */ pit->clkevt_base = base + PIT_CH(3); + pit->cycle_per_jiffy = rate / (HZ); writel(0, pit->clkevt_base + PITTCTRL); @@ -212,7 +213,6 @@ static int __init pit_timer_init(struct device_node *np) return ret; clk_rate = clk_get_rate(pit_clk); - pit_timer.cycle_per_jiffy = clk_rate / (HZ); /* enable the pit module */ writel(~PITMCR_MDIS, timer_base + PITMCR); From 375fbfc66ca23873c3d9b1c5f6739bf0e2875a57 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:25 +0200 Subject: [PATCH 24/52] clocksource/drivers/vf-pit: Allocate the struct timer at init time Instead of having a static global structure for a timer, let's allocate it dynamically so we can create multiple instances in the future to support multiple timers. At the same time, add the rollbacking code in case of error. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-8-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 48 +++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index a11e6a63c79f..d408dcddb4e9 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -37,8 +37,6 @@ struct pit_timer { struct clocksource cs; }; -static struct pit_timer pit_timer; - static void __iomem *clksrc_base; static inline struct pit_timer *ced_to_pit(struct clock_event_device *ced) @@ -189,38 +187,66 @@ static int __init pit_clockevent_init(struct pit_timer *pit, void __iomem *base, static int __init pit_timer_init(struct device_node *np) { + struct pit_timer *pit; struct clk *pit_clk; void __iomem *timer_base; unsigned long clk_rate; int irq, ret; + pit = kzalloc(sizeof(*pit), GFP_KERNEL); + if (!pit) + return -ENOMEM; + + ret = -ENXIO; timer_base = of_iomap(np, 0); if (!timer_base) { pr_err("Failed to iomap\n"); - return -ENXIO; + goto out_kfree; } + ret = -EINVAL; irq = irq_of_parse_and_map(np, 0); - if (irq <= 0) - return -EINVAL; + if (irq <= 0) { + pr_err("Failed to irq_of_parse_and_map\n"); + goto out_iounmap; + } pit_clk = of_clk_get(np, 0); - if (IS_ERR(pit_clk)) - return PTR_ERR(pit_clk); + if (IS_ERR(pit_clk)) { + ret = PTR_ERR(pit_clk); + goto out_iounmap; + } ret = clk_prepare_enable(pit_clk); if (ret) - return ret; + goto out_clk_put; clk_rate = clk_get_rate(pit_clk); /* enable the pit module */ writel(~PITMCR_MDIS, timer_base + PITMCR); - ret = pit_clocksource_init(&pit_timer, timer_base, clk_rate); + ret = pit_clocksource_init(pit, timer_base, clk_rate); if (ret) - return ret; + goto out_disable_unprepare; - return pit_clockevent_init(&pit_timer, timer_base, clk_rate, irq, 0); + ret = pit_clockevent_init(pit, timer_base, clk_rate, irq, 0); + if (ret) + goto out_pit_clocksource_unregister; + + return 0; + +out_pit_clocksource_unregister: + clocksource_unregister(&pit->cs); +out_disable_unprepare: + clk_disable_unprepare(pit_clk); +out_clk_put: + clk_put(pit_clk); +out_iounmap: + iounmap(timer_base); +out_kfree: + kfree(pit); + + return ret; } TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); From 0c063c9afc1b5243adde544637e273c1ac0a31d9 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:26 +0200 Subject: [PATCH 25/52] clocksource/drivers/vf-pit: Convert raw values to BIT macros Use the BIT macros instead of the shifting syntax. No functional change intended. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-9-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index d408dcddb4e9..d1aec6aaeb02 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -21,11 +21,11 @@ #define PITTCTRL 0x08 #define PITTFLG 0x0c -#define PITMCR_MDIS (0x1 << 1) +#define PITMCR_MDIS BIT(1) -#define PITTCTRL_TEN (0x1 << 0) -#define PITTCTRL_TIE (0x1 << 1) -#define PITCTRL_CHN (0x1 << 2) +#define PITTCTRL_TEN BIT(0) +#define PITTCTRL_TIE BIT(1) +#define PITCTRL_CHN BIT(2) #define PITTFLG_TIF 0x1 From c106b698ab8d1899d62de880d73dd99edb319849 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:27 +0200 Subject: [PATCH 26/52] clocksource/drivers/vf-pit: Register the clocksource from the driver The function clocksource_mmio_init() uses its own global static clocksource variable making no possible to have several instances of a clocksource using this function. In order to support that, let's add the clocksource structure to the pit structure and use the clocksource_register_hz() function instead. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-10-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index d1aec6aaeb02..6a4043801eeb 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -44,6 +44,11 @@ static inline struct pit_timer *ced_to_pit(struct clock_event_device *ced) return container_of(ced, struct pit_timer, ced); } +static inline struct pit_timer *cs_to_pit(struct clocksource *cs) +{ + return container_of(cs, struct pit_timer, cs); +} + static inline void pit_timer_enable(struct pit_timer *pit) { writel(PITTCTRL_TEN | PITTCTRL_TIE, pit->clkevt_base + PITTCTRL); @@ -64,6 +69,13 @@ static u64 notrace pit_read_sched_clock(void) return ~readl(clksrc_base + PITCVAL); } +static u64 pit_timer_clocksource_read(struct clocksource *cs) +{ + struct pit_timer *pit = cs_to_pit(cs); + + return (u64)~readl(pit->clksrc_base + PITCVAL); +} + static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base, unsigned long rate) { @@ -73,6 +85,11 @@ static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base * the channels 0 and 1 unused for anyone else who needs them */ pit->clksrc_base = base + PIT_CH(2); + pit->cs.name = "vf-pit"; + pit->cs.rating = 300; + pit->cs.read = pit_timer_clocksource_read; + pit->cs.mask = CLOCKSOURCE_MASK(32); + pit->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; /* set the max load value and start the clock source counter */ writel(0, pit->clksrc_base + PITTCTRL); @@ -83,8 +100,7 @@ static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base sched_clock_register(pit_read_sched_clock, 32, rate); - return clocksource_mmio_init(pit->clksrc_base + PITCVAL, "vf-pit", rate, - 300, 32, clocksource_mmio_readl_down); + return clocksource_register_hz(&pit->cs, rate); } static int pit_set_next_event(unsigned long delta, struct clock_event_device *ced) From 1ba63930e72356315e1a664952f52ee340edbbd3 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:28 +0200 Subject: [PATCH 27/52] clocksource/drivers/vf-pit: Encapsulate the macros Pass the base address to the macro, so we can use the macro with multiple instances of the timer because we deal with different base address. At the same time, change writes to the register to the existing corresponding functions. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-11-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 34 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 6a4043801eeb..c81c68b826a0 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -16,18 +16,20 @@ #define PITMCR 0x00 #define PIT0_OFFSET 0x100 #define PIT_CH(n) (PIT0_OFFSET + 0x10 * (n)) -#define PITLDVAL 0x00 + #define PITCVAL 0x04 -#define PITTCTRL 0x08 -#define PITTFLG 0x0c #define PITMCR_MDIS BIT(1) -#define PITTCTRL_TEN BIT(0) -#define PITTCTRL_TIE BIT(1) -#define PITCTRL_CHN BIT(2) +#define PITLDVAL(__base) (__base) +#define PITTCTRL(__base) ((__base) + 0x08) -#define PITTFLG_TIF 0x1 +#define PITTCTRL_TEN BIT(0) +#define PITTCTRL_TIE BIT(1) + +#define PITTFLG(__base) ((__base) + 0x0c) + +#define PITTFLG_TIF BIT(0) struct pit_timer { void __iomem *clksrc_base; @@ -51,17 +53,17 @@ static inline struct pit_timer *cs_to_pit(struct clocksource *cs) static inline void pit_timer_enable(struct pit_timer *pit) { - writel(PITTCTRL_TEN | PITTCTRL_TIE, pit->clkevt_base + PITTCTRL); + writel(PITTCTRL_TEN | PITTCTRL_TIE, PITTCTRL(pit->clkevt_base)); } static inline void pit_timer_disable(struct pit_timer *pit) { - writel(0, pit->clkevt_base + PITTCTRL); + writel(0, PITTCTRL(pit->clkevt_base)); } static inline void pit_irq_acknowledge(struct pit_timer *pit) { - writel(PITTFLG_TIF, pit->clkevt_base + PITTFLG); + writel(PITTFLG_TIF, PITTFLG(pit->clkevt_base)); } static u64 notrace pit_read_sched_clock(void) @@ -92,9 +94,9 @@ static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base pit->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; /* set the max load value and start the clock source counter */ - writel(0, pit->clksrc_base + PITTCTRL); - writel(~0, pit->clksrc_base + PITLDVAL); - writel(PITTCTRL_TEN, pit->clksrc_base + PITTCTRL); + pit_timer_disable(pit); + writel(~0, PITLDVAL(pit->clksrc_base)); + writel(PITTCTRL_TEN, PITTCTRL(pit->clksrc_base)); clksrc_base = pit->clksrc_base; @@ -115,7 +117,7 @@ static int pit_set_next_event(unsigned long delta, struct clock_event_device *ce * hardware requirement. */ pit_timer_disable(pit); - writel(delta - 1, pit->clkevt_base + PITLDVAL); + writel(delta - 1, PITLDVAL(pit->clkevt_base)); pit_timer_enable(pit); return 0; @@ -171,9 +173,9 @@ static int __init pit_clockevent_init(struct pit_timer *pit, void __iomem *base, pit->clkevt_base = base + PIT_CH(3); pit->cycle_per_jiffy = rate / (HZ); - writel(0, pit->clkevt_base + PITTCTRL); + pit_timer_disable(pit); - writel(PITTFLG_TIF, pit->clkevt_base + PITTFLG); + pit_irq_acknowledge(pit); BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, "VF pit timer", &pit->ced)); From d8629b9b2c17a458ca504b10b604b8cfe95df3ab Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:29 +0200 Subject: [PATCH 28/52] clocksource/drivers/vf-pit: Encapsulate the PTLCVAL macro Pass the channel and the base address to the PITLCVAL macro so it is possible to use multiple instances of the timer with the macro. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-12-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index c81c68b826a0..4f1b85ba5de3 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -17,13 +17,14 @@ #define PIT0_OFFSET 0x100 #define PIT_CH(n) (PIT0_OFFSET + 0x10 * (n)) -#define PITCVAL 0x04 - #define PITMCR_MDIS BIT(1) #define PITLDVAL(__base) (__base) #define PITTCTRL(__base) ((__base) + 0x08) +#define PITCVAL_OFFSET 0x04 +#define PITCVAL(__base) ((__base) + 0x04) + #define PITTCTRL_TEN BIT(0) #define PITTCTRL_TIE BIT(1) @@ -39,7 +40,7 @@ struct pit_timer { struct clocksource cs; }; -static void __iomem *clksrc_base; +static void __iomem *sched_clock_base; static inline struct pit_timer *ced_to_pit(struct clock_event_device *ced) { @@ -68,14 +69,14 @@ static inline void pit_irq_acknowledge(struct pit_timer *pit) static u64 notrace pit_read_sched_clock(void) { - return ~readl(clksrc_base + PITCVAL); + return ~readl(sched_clock_base); } static u64 pit_timer_clocksource_read(struct clocksource *cs) { struct pit_timer *pit = cs_to_pit(cs); - return (u64)~readl(pit->clksrc_base + PITCVAL); + return (u64)~readl(PITCVAL(pit->clksrc_base)); } static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base, @@ -98,8 +99,7 @@ static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base writel(~0, PITLDVAL(pit->clksrc_base)); writel(PITTCTRL_TEN, PITTCTRL(pit->clksrc_base)); - clksrc_base = pit->clksrc_base; - + sched_clock_base = pit->clksrc_base + PITCVAL_OFFSET; sched_clock_register(pit_read_sched_clock, 32, rate); return clocksource_register_hz(&pit->cs, rate); From 7201c95c258936e32c950f447c59875780046848 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:30 +0200 Subject: [PATCH 29/52] clocksource/drivers/vf-pit: Use the node name for the interrupt and timer names In order to differentiate from userspace the pit timer given the device tree, let's use the node name for the interrupt and the timer names. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-13-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 4f1b85ba5de3..2a255b45561d 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -79,8 +79,8 @@ static u64 pit_timer_clocksource_read(struct clocksource *cs) return (u64)~readl(PITCVAL(pit->clksrc_base)); } -static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base, - unsigned long rate) +static int __init pit_clocksource_init(struct pit_timer *pit, const char *name, + void __iomem *base, unsigned long rate) { /* * The channels 0 and 1 can be chained to build a 64-bit @@ -88,7 +88,7 @@ static int __init pit_clocksource_init(struct pit_timer *pit, void __iomem *base * the channels 0 and 1 unused for anyone else who needs them */ pit->clksrc_base = base + PIT_CH(2); - pit->cs.name = "vf-pit"; + pit->cs.name = name; pit->cs.rating = 300; pit->cs.read = pit_timer_clocksource_read; pit->cs.mask = CLOCKSOURCE_MASK(32); @@ -162,8 +162,9 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init pit_clockevent_init(struct pit_timer *pit, void __iomem *base, - unsigned long rate, int irq, unsigned int cpu) +static int __init pit_clockevent_init(struct pit_timer *pit, const char *name, + void __iomem *base, unsigned long rate, + int irq, unsigned int cpu) { /* * The channels 0 and 1 can be chained to build a 64-bit @@ -178,12 +179,12 @@ static int __init pit_clockevent_init(struct pit_timer *pit, void __iomem *base, pit_irq_acknowledge(pit); BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, - "VF pit timer", &pit->ced)); + name, &pit->ced)); pit->ced.cpumask = cpumask_of(cpu); pit->ced.irq = irq; - pit->ced.name = "VF pit timer"; + pit->ced.name = name; pit->ced.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; pit->ced.set_state_shutdown = pit_shutdown; pit->ced.set_state_periodic = pit_set_periodic; @@ -208,6 +209,7 @@ static int __init pit_timer_init(struct device_node *np) struct pit_timer *pit; struct clk *pit_clk; void __iomem *timer_base; + const char *name = of_node_full_name(np); unsigned long clk_rate; int irq, ret; @@ -244,11 +246,11 @@ static int __init pit_timer_init(struct device_node *np) /* enable the pit module */ writel(~PITMCR_MDIS, timer_base + PITMCR); - ret = pit_clocksource_init(pit, timer_base, clk_rate); + ret = pit_clocksource_init(pit, name, timer_base, clk_rate); if (ret) goto out_disable_unprepare; - ret = pit_clockevent_init(pit, timer_base, clk_rate, irq, 0); + ret = pit_clockevent_init(pit, name, timer_base, clk_rate, irq, 0); if (ret) goto out_pit_clocksource_unregister; From fcf25b4427c7d1edb91dd02753d6153fb25da094 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:31 +0200 Subject: [PATCH 30/52] clocksource/drivers/vf-pit: Encapsulate clocksource enable / disable For the sake of lisibility, let's encapsulate the writel calls to enable and disable the timer into a function with a self-explainatory name. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-14-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 2a255b45561d..96377088a048 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -62,6 +62,16 @@ static inline void pit_timer_disable(struct pit_timer *pit) writel(0, PITTCTRL(pit->clkevt_base)); } +static inline void pit_clocksource_enable(struct pit_timer *pit) +{ + writel(PITTCTRL_TEN, PITTCTRL(pit->clksrc_base)); +} + +static inline void pit_clocksource_disable(struct pit_timer *pit) +{ + pit_timer_disable(pit); +} + static inline void pit_irq_acknowledge(struct pit_timer *pit) { writel(PITTFLG_TIF, PITTFLG(pit->clkevt_base)); @@ -95,9 +105,9 @@ static int __init pit_clocksource_init(struct pit_timer *pit, const char *name, pit->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; /* set the max load value and start the clock source counter */ - pit_timer_disable(pit); + pit_clocksource_disable(pit); writel(~0, PITLDVAL(pit->clksrc_base)); - writel(PITTCTRL_TEN, PITTCTRL(pit->clksrc_base)); + pit_clocksource_enable(pit); sched_clock_base = pit->clksrc_base + PITCVAL_OFFSET; sched_clock_register(pit_read_sched_clock, 32, rate); From 13cea8527c95e1359191347abe5d94cccc47a311 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:32 +0200 Subject: [PATCH 31/52] clocksource/drivers/vf-pit: Enable and disable module on error Encapsulate the calls to writel to enable and disable the PIT module and make use of them. Add the missing module disablement in case of error. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-15-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 96377088a048..609a4d9deb64 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -13,10 +13,12 @@ /* * Each pit takes 0x10 Bytes register space */ -#define PITMCR 0x00 #define PIT0_OFFSET 0x100 #define PIT_CH(n) (PIT0_OFFSET + 0x10 * (n)) +#define PITMCR(__base) (__base) + +#define PITMCR_FRZ BIT(0) #define PITMCR_MDIS BIT(1) #define PITLDVAL(__base) (__base) @@ -52,6 +54,16 @@ static inline struct pit_timer *cs_to_pit(struct clocksource *cs) return container_of(cs, struct pit_timer, cs); } +static inline void pit_module_enable(void __iomem *base) +{ + writel(0, PITMCR(base)); +} + +static inline void pit_module_disable(void __iomem *base) +{ + writel(PITMCR_MDIS, PITMCR(base)); +} + static inline void pit_timer_enable(struct pit_timer *pit) { writel(PITTCTRL_TEN | PITTCTRL_TIE, PITTCTRL(pit->clkevt_base)); @@ -254,11 +266,11 @@ static int __init pit_timer_init(struct device_node *np) clk_rate = clk_get_rate(pit_clk); /* enable the pit module */ - writel(~PITMCR_MDIS, timer_base + PITMCR); + pit_module_enable(timer_base); ret = pit_clocksource_init(pit, name, timer_base, clk_rate); if (ret) - goto out_disable_unprepare; + goto out_pit_module_disable; ret = pit_clockevent_init(pit, name, timer_base, clk_rate, irq, 0); if (ret) @@ -268,7 +280,8 @@ static int __init pit_timer_init(struct device_node *np) out_pit_clocksource_unregister: clocksource_unregister(&pit->cs); -out_disable_unprepare: +out_pit_module_disable: + pit_module_disable(timer_base); clk_disable_unprepare(pit_clk); out_clk_put: clk_put(pit_clk); From 46e83e4afc05c87b5edc2b0b4765283f101af0c6 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:33 +0200 Subject: [PATCH 32/52] clocksource/drivers/vf-pit: Encapsulate set counter function Encapsulate the writel() calls to set the counter into a self-explainatory function. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-16-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 609a4d9deb64..5551b61483f8 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -74,6 +74,11 @@ static inline void pit_timer_disable(struct pit_timer *pit) writel(0, PITTCTRL(pit->clkevt_base)); } +static inline void pit_timer_set_counter(void __iomem *base, unsigned int cnt) +{ + writel(cnt, PITLDVAL(base)); +} + static inline void pit_clocksource_enable(struct pit_timer *pit) { writel(PITTCTRL_TEN, PITTCTRL(pit->clksrc_base)); @@ -118,7 +123,7 @@ static int __init pit_clocksource_init(struct pit_timer *pit, const char *name, /* set the max load value and start the clock source counter */ pit_clocksource_disable(pit); - writel(~0, PITLDVAL(pit->clksrc_base)); + pit_timer_set_counter(pit->clksrc_base, ~0); pit_clocksource_enable(pit); sched_clock_base = pit->clksrc_base + PITCVAL_OFFSET; @@ -139,7 +144,7 @@ static int pit_set_next_event(unsigned long delta, struct clock_event_device *ce * hardware requirement. */ pit_timer_disable(pit); - writel(delta - 1, PITLDVAL(pit->clkevt_base)); + pit_timer_set_counter(pit->clkevt_base, delta - 1); pit_timer_enable(pit); return 0; From 5ba405c719ce68a86308dd3bd90aeea59959030d Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:34 +0200 Subject: [PATCH 33/52] clocksource/drivers/vf-pit: Consolidate calls to pit_*_disable/enable The difference between the pit_clocksource_enable() and pit_clocksource_disable() is only setting the TIF flag for the clockevent. Let's group them and pass the TIF flag parameter to the function so we save some lines of code. But as the base address is different regarding if it is a clocksource or a clockevent, we pass the base address in parameter instead of the struct pit_timer. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-17-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 34 ++++++++++++------------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 5551b61483f8..3825159a0ca7 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -64,14 +64,16 @@ static inline void pit_module_disable(void __iomem *base) writel(PITMCR_MDIS, PITMCR(base)); } -static inline void pit_timer_enable(struct pit_timer *pit) +static inline void pit_timer_enable(void __iomem *base, bool tie) { - writel(PITTCTRL_TEN | PITTCTRL_TIE, PITTCTRL(pit->clkevt_base)); + u32 val = PITTCTRL_TEN | (tie ? PITTCTRL_TIE : 0); + + writel(val, PITTCTRL(base)); } -static inline void pit_timer_disable(struct pit_timer *pit) +static inline void pit_timer_disable(void __iomem *base) { - writel(0, PITTCTRL(pit->clkevt_base)); + writel(0, PITTCTRL(base)); } static inline void pit_timer_set_counter(void __iomem *base, unsigned int cnt) @@ -79,16 +81,6 @@ static inline void pit_timer_set_counter(void __iomem *base, unsigned int cnt) writel(cnt, PITLDVAL(base)); } -static inline void pit_clocksource_enable(struct pit_timer *pit) -{ - writel(PITTCTRL_TEN, PITTCTRL(pit->clksrc_base)); -} - -static inline void pit_clocksource_disable(struct pit_timer *pit) -{ - pit_timer_disable(pit); -} - static inline void pit_irq_acknowledge(struct pit_timer *pit) { writel(PITTFLG_TIF, PITTFLG(pit->clkevt_base)); @@ -122,9 +114,9 @@ static int __init pit_clocksource_init(struct pit_timer *pit, const char *name, pit->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; /* set the max load value and start the clock source counter */ - pit_clocksource_disable(pit); + pit_timer_disable(pit->clksrc_base); pit_timer_set_counter(pit->clksrc_base, ~0); - pit_clocksource_enable(pit); + pit_timer_enable(pit->clksrc_base, 0); sched_clock_base = pit->clksrc_base + PITCVAL_OFFSET; sched_clock_register(pit_read_sched_clock, 32, rate); @@ -143,9 +135,9 @@ static int pit_set_next_event(unsigned long delta, struct clock_event_device *ce * and the PITLAVAL should be set to delta minus one according to pit * hardware requirement. */ - pit_timer_disable(pit); + pit_timer_disable(pit->clkevt_base); pit_timer_set_counter(pit->clkevt_base, delta - 1); - pit_timer_enable(pit); + pit_timer_enable(pit->clkevt_base, true); return 0; } @@ -154,7 +146,7 @@ static int pit_shutdown(struct clock_event_device *ced) { struct pit_timer *pit = ced_to_pit(ced); - pit_timer_disable(pit); + pit_timer_disable(pit->clkevt_base); return 0; } @@ -182,7 +174,7 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) * to stop the counter loop in ONESHOT mode. */ if (likely(clockevent_state_oneshot(ced))) - pit_timer_disable(pit); + pit_timer_disable(pit->clkevt_base); ced->event_handler(ced); @@ -201,7 +193,7 @@ static int __init pit_clockevent_init(struct pit_timer *pit, const char *name, pit->clkevt_base = base + PIT_CH(3); pit->cycle_per_jiffy = rate / (HZ); - pit_timer_disable(pit); + pit_timer_disable(pit->clkevt_base); pit_irq_acknowledge(pit); From 3c34321e9b5965fdbf51fd407a35322a64c9e9c6 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:35 +0200 Subject: [PATCH 34/52] clocksource/drivers/vf-pit: Unify the function name for irq ack Most the function are under the form pit_timer_*, let's change the interrupt acknowledgment function name to have the same format. No functional changes intended. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-18-daniel.lezcano@linaro.org --- drivers/clocksource/timer-vf-pit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index 3825159a0ca7..2a0ee4109ead 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -81,7 +81,7 @@ static inline void pit_timer_set_counter(void __iomem *base, unsigned int cnt) writel(cnt, PITLDVAL(base)); } -static inline void pit_irq_acknowledge(struct pit_timer *pit) +static inline void pit_timer_irqack(struct pit_timer *pit) { writel(PITTFLG_TIF, PITTFLG(pit->clkevt_base)); } @@ -165,7 +165,7 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) struct clock_event_device *ced = dev_id; struct pit_timer *pit = ced_to_pit(ced); - pit_irq_acknowledge(pit); + pit_timer_irqack(pit); /* * pit hardware doesn't support oneshot, it will generate an interrupt @@ -195,7 +195,7 @@ static int __init pit_clockevent_init(struct pit_timer *pit, const char *name, pit_timer_disable(pit->clkevt_base); - pit_irq_acknowledge(pit); + pit_timer_irqack(pit); BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, name, &pit->ced)); From fc346a155fe910a1cf4639b00b131f9a10284bdd Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:36 +0200 Subject: [PATCH 35/52] clocksource/drivers/vf-pit: Rename the VF PIT to NXP PIT The PIT acronym stands for Periodic Interrupt Timer which is found on different NXP platforms not only on the Vybrid Family. Change the name to be more generic for the NXP platforms in general. That will be consistent with the NXP STM driver naming convention. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-19-daniel.lezcano@linaro.org --- drivers/clocksource/Kconfig | 9 ++++++--- drivers/clocksource/Makefile | 2 +- drivers/clocksource/{timer-vf-pit.c => timer-nxp-pit.c} | 0 3 files changed, 7 insertions(+), 4 deletions(-) rename drivers/clocksource/{timer-vf-pit.c => timer-nxp-pit.c} (100%) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 6f7d371904df..0fd662f67d29 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -474,11 +474,14 @@ config FSL_FTM_TIMER help Support for Freescale FlexTimer Module (FTM) timer. -config VF_PIT_TIMER - bool "Vybrid Family Programmable timer" if COMPILE_TEST +config NXP_PIT_TIMER + bool "NXP Periodic Interrupt Timer" if COMPILE_TEST select CLKSRC_MMIO help - Support for Periodic Interrupt Timer on Freescale Vybrid Family SoCs. + Support for Periodic Interrupt Timer on Freescale / NXP + SoCs. This periodic timer is found on the Vybrid Family and + the Automotive S32G2/3 platforms. It contains 4 channels + where two can be coupled to form a 64 bits channel. config SYS_SUPPORTS_SH_CMT bool diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 205bf3b0a8f3..77a0f08eb43b 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_CLKSRC_LPC32XX) += timer-lpc32xx.o obj-$(CONFIG_CLKSRC_MPS2) += mps2-timer.o obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o obj-$(CONFIG_FSL_FTM_TIMER) += timer-fsl-ftm.o -obj-$(CONFIG_VF_PIT_TIMER) += timer-vf-pit.o +obj-$(CONFIG_NXP_PIT_TIMER) += timer-nxp-pit.o obj-$(CONFIG_CLKSRC_QCOM) += timer-qcom.o obj-$(CONFIG_MTK_TIMER) += timer-mediatek.o obj-$(CONFIG_MTK_CPUX_TIMER) += timer-mediatek-cpux.o diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-nxp-pit.c similarity index 100% rename from drivers/clocksource/timer-vf-pit.c rename to drivers/clocksource/timer-nxp-pit.c From adaf5b248ff38f98ea1b3696b9a793db270f8c3e Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:37 +0200 Subject: [PATCH 36/52] dt: bindings: fsl,vf610-pit: Add compatible for s32g2 and s32g3 The Vybrid Family is a NXP (formerly Freescale) platform having a Programmable Interrupt Timer (PIT). This timer is an IP found also on the NXP Automotive platform S32G2 and S32G3. Add the compatible for those platforms to describe the timer. Signed-off-by: Daniel Lezcano Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250804152344.1109310-20-daniel.lezcano@linaro.org --- .../devicetree/bindings/timer/fsl,vf610-pit.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/timer/fsl,vf610-pit.yaml b/Documentation/devicetree/bindings/timer/fsl,vf610-pit.yaml index bee2c35bd0e2..42e130654d58 100644 --- a/Documentation/devicetree/bindings/timer/fsl,vf610-pit.yaml +++ b/Documentation/devicetree/bindings/timer/fsl,vf610-pit.yaml @@ -15,8 +15,13 @@ description: properties: compatible: - enum: - - fsl,vf610-pit + oneOf: + - enum: + - fsl,vf610-pit + - nxp,s32g2-pit + - items: + - const: nxp,s32g3-pit + - const: nxp,s32g2-pit reg: maxItems: 1 From bee33f22d7c30626e711b4900e3f460b6e0e104f Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 4 Aug 2025 17:23:38 +0200 Subject: [PATCH 37/52] clocksource/drivers/nxp-pit: Add NXP Automotive s32g2 / s32g3 support The previous changes put in place the encapsulation of the code in order to allow multiple instances of the driver. The S32G platform has two Periodic Interrupt Timer (PIT). The IP is exactly the same as the VF platform. Each PIT has four channels which are 32 bits wide and counting down. The two first channels can be chained to implement a 64 bits counter. The channel usage is kept unchanged with the original driver, channel 2 is used as a clocksource, channel 3 is used as a clockevent. Other channels are unused. In order to support the S32G platform which has two PIT, we initialize the timer and bind it to a CPU. The S32G platforms can have 2, 4 or 8 CPUs and this kind of configuration can appear unusual as we may endup with two PIT used as a clockevent for the two first CPUs while the other CPUs use the architected timers. However, in the context of the automotive, the platform can be partioned to assign 2 CPUs for Linux and the others CPUs to third party OS. The PIT is then used with their specifities like the ability to freeze the time which is needed for instance for debugging purpose. The setup found for this platform is each timer instance is bound to CPU0 and CPU1. A counter is incremented when a timer is successfully initialized and assigned to a CPU. This counter is used as an index for the CPU number and to detect when we reach the maximum possible instances for the platform. That in turn triggers the CPU hotplug callbacks to achieve the per CPU setup. It is the exact same mechanism found in the NXP STM driver. If the timers must be bound to different CPUs, it would require an additionnal mechanism which is not part of these changes. Tested on a s32g274a-rdb2. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804152344.1109310-21-daniel.lezcano@linaro.org --- drivers/clocksource/timer-nxp-pit.c | 130 +++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 20 deletions(-) diff --git a/drivers/clocksource/timer-nxp-pit.c b/drivers/clocksource/timer-nxp-pit.c index 2a0ee4109ead..2d0a3554b6bf 100644 --- a/drivers/clocksource/timer-nxp-pit.c +++ b/drivers/clocksource/timer-nxp-pit.c @@ -1,14 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2018,2021-2025 NXP */ - #include #include +#include #include #include #include #include +#include /* * Each pit takes 0x10 Bytes register space @@ -37,11 +39,23 @@ struct pit_timer { void __iomem *clksrc_base; void __iomem *clkevt_base; - unsigned long cycle_per_jiffy; struct clock_event_device ced; struct clocksource cs; + int rate; }; +struct pit_timer_data { + int max_pit_instances; +}; + +static DEFINE_PER_CPU(struct pit_timer *, pit_timers); + +/* + * Global structure for multiple PITs initialization + */ +static int pit_instances; +static int max_pit_instances = 1; + static void __iomem *sched_clock_base; static inline struct pit_timer *ced_to_pit(struct clock_event_device *ced) @@ -98,8 +112,8 @@ static u64 pit_timer_clocksource_read(struct clocksource *cs) return (u64)~readl(PITCVAL(pit->clksrc_base)); } -static int __init pit_clocksource_init(struct pit_timer *pit, const char *name, - void __iomem *base, unsigned long rate) +static int pit_clocksource_init(struct pit_timer *pit, const char *name, + void __iomem *base, unsigned long rate) { /* * The channels 0 and 1 can be chained to build a 64-bit @@ -155,7 +169,7 @@ static int pit_set_periodic(struct clock_event_device *ced) { struct pit_timer *pit = ced_to_pit(ced); - pit_set_next_event(pit->cycle_per_jiffy, ced); + pit_set_next_event(pit->rate / HZ, ced); return 0; } @@ -181,24 +195,28 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init pit_clockevent_init(struct pit_timer *pit, const char *name, - void __iomem *base, unsigned long rate, - int irq, unsigned int cpu) +static int pit_clockevent_per_cpu_init(struct pit_timer *pit, const char *name, + void __iomem *base, unsigned long rate, + int irq, unsigned int cpu) { + int ret; + /* * The channels 0 and 1 can be chained to build a 64-bit * timer. Let's use the channel 3 as a clockevent and leave * the channels 0 and 1 unused for anyone else who needs them */ pit->clkevt_base = base + PIT_CH(3); - pit->cycle_per_jiffy = rate / (HZ); + pit->rate = rate; pit_timer_disable(pit->clkevt_base); pit_timer_irqack(pit); - BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, - name, &pit->ced)); + ret = request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_NOBALANCING, + name, &pit->ced); + if (ret) + return ret; pit->ced.cpumask = cpumask_of(cpu); pit->ced.irq = irq; @@ -210,6 +228,32 @@ static int __init pit_clockevent_init(struct pit_timer *pit, const char *name, pit->ced.set_next_event = pit_set_next_event; pit->ced.rating = 300; + per_cpu(pit_timers, cpu) = pit; + + return 0; +} + +static void pit_clockevent_per_cpu_exit(struct pit_timer *pit, unsigned int cpu) +{ + pit_timer_disable(pit->clkevt_base); + free_irq(pit->ced.irq, &pit->ced); + per_cpu(pit_timers, cpu) = NULL; +} + +static int pit_clockevent_starting_cpu(unsigned int cpu) +{ + struct pit_timer *pit = per_cpu(pit_timers, cpu); + int ret; + + if (!pit) + return 0; + + ret = irq_force_affinity(pit->ced.irq, cpumask_of(cpu)); + if (ret) { + pit_clockevent_per_cpu_exit(pit, cpu); + return ret; + } + /* * The value for the LDVAL register trigger is calculated as: * LDVAL trigger = (period / clock period) - 1 @@ -218,12 +262,12 @@ static int __init pit_clockevent_init(struct pit_timer *pit, const char *name, * LDVAL trigger value is 1. And then the min_delta is * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit. */ - clockevents_config_and_register(&pit->ced, rate, 2, 0xffffffff); + clockevents_config_and_register(&pit->ced, pit->rate, 2, 0xffffffff); return 0; } -static int __init pit_timer_init(struct device_node *np) +static int pit_timer_init(struct device_node *np) { struct pit_timer *pit; struct clk *pit_clk; @@ -253,7 +297,7 @@ static int __init pit_timer_init(struct device_node *np) pit_clk = of_clk_get(np, 0); if (IS_ERR(pit_clk)) { ret = PTR_ERR(pit_clk); - goto out_iounmap; + goto out_irq_dispose_mapping; } ret = clk_prepare_enable(pit_clk); @@ -262,16 +306,31 @@ static int __init pit_timer_init(struct device_node *np) clk_rate = clk_get_rate(pit_clk); + pit_module_disable(timer_base); + + ret = pit_clocksource_init(pit, name, timer_base, clk_rate); + if (ret) { + pr_err("Failed to initialize clocksource '%pOF'\n", np); + goto out_pit_module_disable; + } + + ret = pit_clockevent_per_cpu_init(pit, name, timer_base, clk_rate, irq, pit_instances); + if (ret) { + pr_err("Failed to initialize clockevent '%pOF'\n", np); + goto out_pit_clocksource_unregister; + } + /* enable the pit module */ pit_module_enable(timer_base); - ret = pit_clocksource_init(pit, name, timer_base, clk_rate); - if (ret) - goto out_pit_module_disable; + pit_instances++; - ret = pit_clockevent_init(pit, name, timer_base, clk_rate, irq, 0); - if (ret) - goto out_pit_clocksource_unregister; + if (pit_instances == max_pit_instances) { + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "PIT timer:starting", + pit_clockevent_starting_cpu, NULL); + if (ret < 0) + goto out_pit_clocksource_unregister; + } return 0; @@ -282,6 +341,8 @@ static int __init pit_timer_init(struct device_node *np) clk_disable_unprepare(pit_clk); out_clk_put: clk_put(pit_clk); +out_irq_dispose_mapping: + irq_dispose_mapping(irq); out_iounmap: iounmap(timer_base); out_kfree: @@ -289,4 +350,33 @@ static int __init pit_timer_init(struct device_node *np) return ret; } + +static int pit_timer_probe(struct platform_device *pdev) +{ + const struct pit_timer_data *pit_timer_data; + + pit_timer_data = of_device_get_match_data(&pdev->dev); + if (pit_timer_data) + max_pit_instances = pit_timer_data->max_pit_instances; + + return pit_timer_init(pdev->dev.of_node); +} + +static struct pit_timer_data s32g2_data = { .max_pit_instances = 2 }; + +static const struct of_device_id pit_timer_of_match[] = { + { .compatible = "nxp,s32g2-pit", .data = &s32g2_data }, + { } +}; +MODULE_DEVICE_TABLE(of, pit_timer_of_match); + +static struct platform_driver nxp_pit_driver = { + .driver = { + .name = "nxp-pit", + .of_match_table = pit_timer_of_match, + }, + .probe = pit_timer_probe, +}; +module_platform_driver(nxp_pit_driver); + TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); From 5669d92f3efa449c3906cbf15e676768a8f4d502 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 14 Aug 2025 16:46:19 +0100 Subject: [PATCH 38/52] ACPI: GTDT: Generate platform devices for MMIO timers In preparation for the MMIO timer support code becoming an actual driver, mimic what is done for the SBSA watchdog and expose a synthetic device for each MMIO timer block. Signed-off-by: Marc Zyngier Signed-off-by: Daniel Lezcano Tested-by: Sudeep Holla Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20250807160243.1970533-2-maz@kernel.org --- drivers/acpi/arm64/gtdt.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c index 70f8290b659d..fd995a1d3d24 100644 --- a/drivers/acpi/arm64/gtdt.c +++ b/drivers/acpi/arm64/gtdt.c @@ -388,11 +388,11 @@ static int __init gtdt_import_sbsa_gwdt(struct acpi_gtdt_watchdog *wd, return 0; } -static int __init gtdt_sbsa_gwdt_init(void) +static int __init gtdt_platform_timer_init(void) { void *platform_timer; struct acpi_table_header *table; - int ret, timer_count, gwdt_count = 0; + int ret, timer_count, gwdt_count = 0, mmio_timer_count = 0; if (acpi_disabled) return 0; @@ -414,20 +414,41 @@ static int __init gtdt_sbsa_gwdt_init(void) goto out_put_gtdt; for_each_platform_timer(platform_timer) { + ret = 0; + if (is_non_secure_watchdog(platform_timer)) { ret = gtdt_import_sbsa_gwdt(platform_timer, gwdt_count); if (ret) - break; + continue; gwdt_count++; + } else if (is_timer_block(platform_timer)) { + struct arch_timer_mem atm = {}; + struct platform_device *pdev; + + ret = gtdt_parse_timer_block(platform_timer, &atm); + if (ret) + continue; + + pdev = platform_device_register_data(NULL, "gtdt-arm-mmio-timer", + gwdt_count, &atm, + sizeof(atm)); + if (IS_ERR(pdev)) { + pr_err("Can't register timer %d\n", gwdt_count); + continue; + } + + mmio_timer_count++; } } if (gwdt_count) pr_info("found %d SBSA generic Watchdog(s).\n", gwdt_count); + if (mmio_timer_count) + pr_info("found %d Generic MMIO timer(s).\n", mmio_timer_count); out_put_gtdt: acpi_put_table(table); return ret; } -device_initcall(gtdt_sbsa_gwdt_init); +device_initcall(gtdt_platform_timer_init); From 4891f01527bbbb0cf0e515c803ade67a17e247bb Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 14 Aug 2025 16:46:20 +0100 Subject: [PATCH 39/52] clocksource/drivers/arm_arch_timer: Add standalone MMIO driver Add a new driver for the MMIO side of the ARM architected timer. Most of it has been lifted from the existing arch timer code, massaged, and finally rewritten. It supports both DT and ACPI as firmware descriptions. Signed-off-by: Marc Zyngier Signed-off-by: Daniel Lezcano Tested-by: Sudeep Holla Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20250814154622.10193-3-maz@kernel.org --- MAINTAINERS | 1 + drivers/clocksource/arm_arch_timer_mmio.c | 421 ++++++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 drivers/clocksource/arm_arch_timer_mmio.c diff --git a/MAINTAINERS b/MAINTAINERS index fe168477caa4..2243b726edd7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1990,6 +1990,7 @@ S: Maintained F: arch/arm/include/asm/arch_timer.h F: arch/arm64/include/asm/arch_timer.h F: drivers/clocksource/arm_arch_timer.c +F: drivers/clocksource/arm_arch_timer_mmio.c ARM GENERIC INTERRUPT CONTROLLER DRIVERS M: Marc Zyngier diff --git a/drivers/clocksource/arm_arch_timer_mmio.c b/drivers/clocksource/arm_arch_timer_mmio.c new file mode 100644 index 000000000000..3522d1d8cb97 --- /dev/null +++ b/drivers/clocksource/arm_arch_timer_mmio.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ARM Generic Memory Mapped Timer support + * + * Split from drivers/clocksource/arm_arch_timer.c + * + * Copyright (C) 2011 ARM Ltd. + * All Rights Reserved + */ + +#define pr_fmt(fmt) "arch_timer_mmio: " fmt + +#include +#include +#include +#include +#include +#include + +#include + +#define CNTTIDR 0x08 +#define CNTTIDR_VIRT(n) (BIT(1) << ((n) * 4)) + +#define CNTACR(n) (0x40 + ((n) * 4)) +#define CNTACR_RPCT BIT(0) +#define CNTACR_RVCT BIT(1) +#define CNTACR_RFRQ BIT(2) +#define CNTACR_RVOFF BIT(3) +#define CNTACR_RWVT BIT(4) +#define CNTACR_RWPT BIT(5) + +#define CNTPCT_LO 0x00 +#define CNTVCT_LO 0x08 +#define CNTFRQ 0x10 +#define CNTP_CVAL_LO 0x20 +#define CNTP_CTL 0x2c +#define CNTV_CVAL_LO 0x30 +#define CNTV_CTL 0x3c + +enum arch_timer_access { + PHYS_ACCESS, + VIRT_ACCESS, +}; + +struct arch_timer { + struct clock_event_device evt; + struct arch_timer_mem *gt_block; + void __iomem *base; + enum arch_timer_access access; + u32 rate; +}; + +#define evt_to_arch_timer(e) container_of(e, struct arch_timer, evt) + +static void arch_timer_mmio_write(struct arch_timer *timer, + enum arch_timer_reg reg, u64 val) +{ + switch (timer->access) { + case PHYS_ACCESS: + switch (reg) { + case ARCH_TIMER_REG_CTRL: + writel_relaxed((u32)val, timer->base + CNTP_CTL); + return; + case ARCH_TIMER_REG_CVAL: + /* + * Not guaranteed to be atomic, so the timer + * must be disabled at this point. + */ + writeq_relaxed(val, timer->base + CNTP_CVAL_LO); + return; + } + break; + case VIRT_ACCESS: + switch (reg) { + case ARCH_TIMER_REG_CTRL: + writel_relaxed((u32)val, timer->base + CNTV_CTL); + return; + case ARCH_TIMER_REG_CVAL: + /* Same restriction as above */ + writeq_relaxed(val, timer->base + CNTV_CVAL_LO); + return; + } + break; + } + + /* Should never be here */ + WARN_ON_ONCE(1); +} + +static u32 arch_timer_mmio_read(struct arch_timer *timer, enum arch_timer_reg reg) +{ + switch (timer->access) { + case PHYS_ACCESS: + switch (reg) { + case ARCH_TIMER_REG_CTRL: + return readl_relaxed(timer->base + CNTP_CTL); + default: + break; + } + break; + case VIRT_ACCESS: + switch (reg) { + case ARCH_TIMER_REG_CTRL: + return readl_relaxed(timer->base + CNTV_CTL); + default: + break; + } + break; + } + + /* Should never be here */ + WARN_ON_ONCE(1); + return 0; +} + +static noinstr u64 arch_counter_mmio_get_cnt(struct arch_timer *t) +{ + int offset_lo = t->access == VIRT_ACCESS ? CNTVCT_LO : CNTPCT_LO; + u32 cnt_lo, cnt_hi, tmp_hi; + + do { + cnt_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4)); + cnt_lo = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo)); + tmp_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4)); + } while (cnt_hi != tmp_hi); + + return ((u64) cnt_hi << 32) | cnt_lo; +} + +static int arch_timer_mmio_shutdown(struct clock_event_device *clk) +{ + struct arch_timer *at = evt_to_arch_timer(clk); + unsigned long ctrl; + + ctrl = arch_timer_mmio_read(at, ARCH_TIMER_REG_CTRL); + ctrl &= ~ARCH_TIMER_CTRL_ENABLE; + arch_timer_mmio_write(at, ARCH_TIMER_REG_CTRL, ctrl); + + return 0; +} + +static int arch_timer_mmio_set_next_event(unsigned long evt, + struct clock_event_device *clk) +{ + struct arch_timer *timer = evt_to_arch_timer(clk); + unsigned long ctrl; + u64 cnt; + + ctrl = arch_timer_mmio_read(timer, ARCH_TIMER_REG_CTRL); + + /* Timer must be disabled before programming CVAL */ + if (ctrl & ARCH_TIMER_CTRL_ENABLE) { + ctrl &= ~ARCH_TIMER_CTRL_ENABLE; + arch_timer_mmio_write(timer, ARCH_TIMER_REG_CTRL, ctrl); + } + + ctrl |= ARCH_TIMER_CTRL_ENABLE; + ctrl &= ~ARCH_TIMER_CTRL_IT_MASK; + + cnt = arch_counter_mmio_get_cnt(timer); + + arch_timer_mmio_write(timer, ARCH_TIMER_REG_CVAL, evt + cnt); + arch_timer_mmio_write(timer, ARCH_TIMER_REG_CTRL, ctrl); + return 0; +} + +static irqreturn_t arch_timer_mmio_handler(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + struct arch_timer *at = evt_to_arch_timer(evt); + unsigned long ctrl; + + ctrl = arch_timer_mmio_read(at, ARCH_TIMER_REG_CTRL); + if (ctrl & ARCH_TIMER_CTRL_IT_STAT) { + ctrl |= ARCH_TIMER_CTRL_IT_MASK; + arch_timer_mmio_write(at, ARCH_TIMER_REG_CTRL, ctrl); + evt->event_handler(evt); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static struct arch_timer_mem_frame *find_best_frame(struct platform_device *pdev) +{ + struct arch_timer_mem_frame *frame, *best_frame = NULL; + struct arch_timer *at = platform_get_drvdata(pdev); + void __iomem *cntctlbase; + u32 cnttidr; + + cntctlbase = ioremap(at->gt_block->cntctlbase, at->gt_block->size); + if (!cntctlbase) { + dev_err(&pdev->dev, "Can't map CNTCTLBase @ %pa\n", + &at->gt_block->cntctlbase); + return NULL; + } + + cnttidr = readl_relaxed(cntctlbase + CNTTIDR); + + /* + * Try to find a virtual capable frame. Otherwise fall back to a + * physical capable frame. + */ + for (int i = 0; i < ARCH_TIMER_MEM_MAX_FRAMES; i++) { + u32 cntacr = CNTACR_RFRQ | CNTACR_RWPT | CNTACR_RPCT | + CNTACR_RWVT | CNTACR_RVOFF | CNTACR_RVCT; + + frame = &at->gt_block->frame[i]; + if (!frame->valid) + continue; + + /* Try enabling everything, and see what sticks */ + writel_relaxed(cntacr, cntctlbase + CNTACR(i)); + cntacr = readl_relaxed(cntctlbase + CNTACR(i)); + + /* Pick a suitable frame for which we have an IRQ */ + if ((cnttidr & CNTTIDR_VIRT(i)) && + !(~cntacr & (CNTACR_RWVT | CNTACR_RVCT)) && + frame->virt_irq) { + best_frame = frame; + at->access = VIRT_ACCESS; + break; + } + + if ((~cntacr & (CNTACR_RWPT | CNTACR_RPCT)) || + !frame->phys_irq) + continue; + + at->access = PHYS_ACCESS; + best_frame = frame; + } + + iounmap(cntctlbase); + + return best_frame; +} + +static void arch_timer_mmio_setup(struct arch_timer *at, int irq) +{ + at->evt = (struct clock_event_device) { + .features = (CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_DYNIRQ), + .name = "arch_mem_timer", + .rating = 400, + .cpumask = cpu_possible_mask, + .irq = irq, + .set_next_event = arch_timer_mmio_set_next_event, + .set_state_oneshot_stopped = arch_timer_mmio_shutdown, + .set_state_shutdown = arch_timer_mmio_shutdown, + }; + + at->evt.set_state_shutdown(&at->evt); + + clockevents_config_and_register(&at->evt, at->rate, 0xf, + (unsigned long)CLOCKSOURCE_MASK(56)); + + enable_irq(at->evt.irq); +} + +static int arch_timer_mmio_frame_register(struct platform_device *pdev, + struct arch_timer_mem_frame *frame) +{ + struct arch_timer *at = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + int ret, irq; + u32 rate; + + if (!devm_request_mem_region(&pdev->dev, frame->cntbase, frame->size, + "arch_mem_timer")) + return -EBUSY; + + at->base = devm_ioremap(&pdev->dev, frame->cntbase, frame->size); + if (!at->base) { + dev_err(&pdev->dev, "Can't map frame's registers\n"); + return -ENXIO; + } + + /* + * Allow "clock-frequency" to override the probed rate. If neither + * lead to something useful, use the CPU timer frequency as the + * fallback. The nice thing about that last point is that we woudn't + * made it here if we didn't have a valid frequency. + */ + rate = readl_relaxed(at->base + CNTFRQ); + + if (!np || of_property_read_u32(np, "clock-frequency", &at->rate)) + at->rate = rate; + + if (!at->rate) + at->rate = arch_timer_get_rate(); + + irq = at->access == VIRT_ACCESS ? frame->virt_irq : frame->phys_irq; + ret = devm_request_irq(&pdev->dev, irq, arch_timer_mmio_handler, + IRQF_TIMER | IRQF_NO_AUTOEN, "arch_mem_timer", + &at->evt); + if (ret) { + dev_err(&pdev->dev, "Failed to request mem timer irq\n"); + return ret; + } + + /* Afer this point, we're not allowed to fail anymore */ + arch_timer_mmio_setup(at, irq); + return 0; +} + +static int of_populate_gt_block(struct platform_device *pdev, + struct arch_timer *at) +{ + struct resource res; + + if (of_address_to_resource(pdev->dev.of_node, 0, &res)) + return -EINVAL; + + at->gt_block->cntctlbase = res.start; + at->gt_block->size = resource_size(&res); + + for_each_available_child_of_node_scoped(pdev->dev.of_node, frame_node) { + struct arch_timer_mem_frame *frame; + u32 n; + + if (of_property_read_u32(frame_node, "frame-number", &n)) { + dev_err(&pdev->dev, FW_BUG "Missing frame-number\n"); + return -EINVAL; + } + if (n >= ARCH_TIMER_MEM_MAX_FRAMES) { + dev_err(&pdev->dev, + FW_BUG "Wrong frame-number, only 0-%u are permitted\n", + ARCH_TIMER_MEM_MAX_FRAMES - 1); + return -EINVAL; + } + + frame = &at->gt_block->frame[n]; + + if (frame->valid) { + dev_err(&pdev->dev, FW_BUG "Duplicated frame-number\n"); + return -EINVAL; + } + + if (of_address_to_resource(frame_node, 0, &res)) + return -EINVAL; + + frame->cntbase = res.start; + frame->size = resource_size(&res); + + frame->phys_irq = irq_of_parse_and_map(frame_node, 0); + frame->virt_irq = irq_of_parse_and_map(frame_node, 1); + + frame->valid = true; + } + + return 0; +} + +static int arch_timer_mmio_probe(struct platform_device *pdev) +{ + struct arch_timer_mem_frame *frame; + struct arch_timer *at; + struct device_node *np; + int ret; + + np = pdev->dev.of_node; + + at = devm_kmalloc(&pdev->dev, sizeof(*at), GFP_KERNEL | __GFP_ZERO); + if (!at) + return -ENOMEM; + + if (np) { + at->gt_block = devm_kmalloc(&pdev->dev, sizeof(*at->gt_block), + GFP_KERNEL | __GFP_ZERO); + if (!at->gt_block) + return -ENOMEM; + ret = of_populate_gt_block(pdev, at); + if (ret) + return ret; + } else { + at->gt_block = dev_get_platdata(&pdev->dev); + } + + platform_set_drvdata(pdev, at); + + frame = find_best_frame(pdev); + if (!frame) { + dev_err(&pdev->dev, + "Unable to find a suitable frame in timer @ %pa\n", + &at->gt_block->cntctlbase); + return -EINVAL; + } + + ret = arch_timer_mmio_frame_register(pdev, frame); + if (!ret) + dev_info(&pdev->dev, + "mmio timer running at %lu.%02luMHz (%s)\n", + (unsigned long)at->rate / 1000000, + (unsigned long)(at->rate / 10000) % 100, + at->access == VIRT_ACCESS ? "virt" : "phys"); + + return ret; +} + +static const struct of_device_id arch_timer_mmio_of_table[] = { + { .compatible = "arm,armv7-timer-mem", }, + {} +}; + +static struct platform_driver arch_timer_mmio_drv = { + .driver = { + .name = "arch-timer-mmio", + .of_match_table = arch_timer_mmio_of_table, + }, + .probe = arch_timer_mmio_probe, +}; +builtin_platform_driver(arch_timer_mmio_drv); + +static struct platform_driver arch_timer_mmio_acpi_drv = { + .driver = { + .name = "gtdt-arm-mmio-timer", + }, + .probe = arch_timer_mmio_probe, +}; +builtin_platform_driver(arch_timer_mmio_acpi_drv); From 0f67b56d84b4c49adfd61f19f81f84ec613ab51a Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 14 Aug 2025 16:46:21 +0100 Subject: [PATCH 40/52] clocksource/drivers/arm_arch_timer_mmio: Switch over to standalone driver Remove all the MMIO support from the per-CPU timer driver, and switch over to the standalove driver. Signed-off-by: Marc Zyngier Signed-off-by: Daniel Lezcano Tested-by: Sudeep Holla Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20250814154622.10193-4-maz@kernel.org --- drivers/clocksource/Makefile | 1 + drivers/clocksource/arm_arch_timer.c | 686 +++------------------------ include/clocksource/arm_arch_timer.h | 5 - 3 files changed, 66 insertions(+), 626 deletions(-) diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 77a0f08eb43b..ec4452ee958f 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_REALTEK_OTTO_TIMER) += timer-rtl-otto.o obj-$(CONFIG_ARC_TIMERS) += arc_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o +obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer_mmio.o obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o obj-$(CONFIG_ARMV7M_SYSTICK) += armv7m_systick.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp804.o diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 80ba6a54248c..90aeff44a276 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -34,42 +34,12 @@ #include -#define CNTTIDR 0x08 -#define CNTTIDR_VIRT(n) (BIT(1) << ((n) * 4)) - -#define CNTACR(n) (0x40 + ((n) * 4)) -#define CNTACR_RPCT BIT(0) -#define CNTACR_RVCT BIT(1) -#define CNTACR_RFRQ BIT(2) -#define CNTACR_RVOFF BIT(3) -#define CNTACR_RWVT BIT(4) -#define CNTACR_RWPT BIT(5) - -#define CNTPCT_LO 0x00 -#define CNTVCT_LO 0x08 -#define CNTFRQ 0x10 -#define CNTP_CVAL_LO 0x20 -#define CNTP_CTL 0x2c -#define CNTV_CVAL_LO 0x30 -#define CNTV_CTL 0x3c - /* * The minimum amount of time a generic counter is guaranteed to not roll over * (40 years) */ #define MIN_ROLLOVER_SECS (40ULL * 365 * 24 * 3600) -static unsigned arch_timers_present __initdata; - -struct arch_timer { - void __iomem *base; - struct clock_event_device evt; -}; - -static struct arch_timer *arch_timer_mem __ro_after_init; - -#define to_arch_timer(e) container_of(e, struct arch_timer, evt) - static u32 arch_timer_rate __ro_after_init; static int arch_timer_ppi[ARCH_TIMER_MAX_TIMER_PPI] __ro_after_init; @@ -85,7 +55,6 @@ static struct clock_event_device __percpu *arch_timer_evt; static enum arch_timer_ppi_nr arch_timer_uses_ppi __ro_after_init = ARCH_TIMER_VIRT_PPI; static bool arch_timer_c3stop __ro_after_init; -static bool arch_timer_mem_use_virtual __ro_after_init; static bool arch_counter_suspend_stop __ro_after_init; #ifdef CONFIG_GENERIC_GETTIMEOFDAY static enum vdso_clock_mode vdso_default = VDSO_CLOCKMODE_ARCHTIMER; @@ -121,76 +90,6 @@ static int arch_counter_get_width(void) /* * Architected system timer support. */ - -static __always_inline -void arch_timer_reg_write(int access, enum arch_timer_reg reg, u64 val, - struct clock_event_device *clk) -{ - if (access == ARCH_TIMER_MEM_PHYS_ACCESS) { - struct arch_timer *timer = to_arch_timer(clk); - switch (reg) { - case ARCH_TIMER_REG_CTRL: - writel_relaxed((u32)val, timer->base + CNTP_CTL); - break; - case ARCH_TIMER_REG_CVAL: - /* - * Not guaranteed to be atomic, so the timer - * must be disabled at this point. - */ - writeq_relaxed(val, timer->base + CNTP_CVAL_LO); - break; - default: - BUILD_BUG(); - } - } else if (access == ARCH_TIMER_MEM_VIRT_ACCESS) { - struct arch_timer *timer = to_arch_timer(clk); - switch (reg) { - case ARCH_TIMER_REG_CTRL: - writel_relaxed((u32)val, timer->base + CNTV_CTL); - break; - case ARCH_TIMER_REG_CVAL: - /* Same restriction as above */ - writeq_relaxed(val, timer->base + CNTV_CVAL_LO); - break; - default: - BUILD_BUG(); - } - } else { - arch_timer_reg_write_cp15(access, reg, val); - } -} - -static __always_inline -u32 arch_timer_reg_read(int access, enum arch_timer_reg reg, - struct clock_event_device *clk) -{ - u32 val; - - if (access == ARCH_TIMER_MEM_PHYS_ACCESS) { - struct arch_timer *timer = to_arch_timer(clk); - switch (reg) { - case ARCH_TIMER_REG_CTRL: - val = readl_relaxed(timer->base + CNTP_CTL); - break; - default: - BUILD_BUG(); - } - } else if (access == ARCH_TIMER_MEM_VIRT_ACCESS) { - struct arch_timer *timer = to_arch_timer(clk); - switch (reg) { - case ARCH_TIMER_REG_CTRL: - val = readl_relaxed(timer->base + CNTV_CTL); - break; - default: - BUILD_BUG(); - } - } else { - val = arch_timer_reg_read_cp15(access, reg); - } - - return val; -} - static noinstr u64 raw_counter_get_cntpct_stable(void) { return __arch_counter_get_cntpct_stable(); @@ -424,7 +323,7 @@ void erratum_set_next_event_generic(const int access, unsigned long evt, unsigned long ctrl; u64 cval; - ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk); + ctrl = arch_timer_reg_read_cp15(access, ARCH_TIMER_REG_CTRL); ctrl |= ARCH_TIMER_CTRL_ENABLE; ctrl &= ~ARCH_TIMER_CTRL_IT_MASK; @@ -436,7 +335,7 @@ void erratum_set_next_event_generic(const int access, unsigned long evt, write_sysreg(cval, cntv_cval_el0); } - arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); + arch_timer_reg_write_cp15(access, ARCH_TIMER_REG_CTRL, ctrl); } static __maybe_unused int erratum_set_next_event_virt(unsigned long evt, @@ -667,10 +566,10 @@ static __always_inline irqreturn_t timer_handler(const int access, { unsigned long ctrl; - ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, evt); + ctrl = arch_timer_reg_read_cp15(access, ARCH_TIMER_REG_CTRL); if (ctrl & ARCH_TIMER_CTRL_IT_STAT) { ctrl |= ARCH_TIMER_CTRL_IT_MASK; - arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, evt); + arch_timer_reg_write_cp15(access, ARCH_TIMER_REG_CTRL, ctrl); evt->event_handler(evt); return IRQ_HANDLED; } @@ -692,28 +591,14 @@ static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id) return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt); } -static irqreturn_t arch_timer_handler_phys_mem(int irq, void *dev_id) -{ - struct clock_event_device *evt = dev_id; - - return timer_handler(ARCH_TIMER_MEM_PHYS_ACCESS, evt); -} - -static irqreturn_t arch_timer_handler_virt_mem(int irq, void *dev_id) -{ - struct clock_event_device *evt = dev_id; - - return timer_handler(ARCH_TIMER_MEM_VIRT_ACCESS, evt); -} - static __always_inline int arch_timer_shutdown(const int access, struct clock_event_device *clk) { unsigned long ctrl; - ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk); + ctrl = arch_timer_reg_read_cp15(access, ARCH_TIMER_REG_CTRL); ctrl &= ~ARCH_TIMER_CTRL_ENABLE; - arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); + arch_timer_reg_write_cp15(access, ARCH_TIMER_REG_CTRL, ctrl); return 0; } @@ -728,23 +613,13 @@ static int arch_timer_shutdown_phys(struct clock_event_device *clk) return arch_timer_shutdown(ARCH_TIMER_PHYS_ACCESS, clk); } -static int arch_timer_shutdown_virt_mem(struct clock_event_device *clk) -{ - return arch_timer_shutdown(ARCH_TIMER_MEM_VIRT_ACCESS, clk); -} - -static int arch_timer_shutdown_phys_mem(struct clock_event_device *clk) -{ - return arch_timer_shutdown(ARCH_TIMER_MEM_PHYS_ACCESS, clk); -} - static __always_inline void set_next_event(const int access, unsigned long evt, struct clock_event_device *clk) { unsigned long ctrl; u64 cnt; - ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk); + ctrl = arch_timer_reg_read_cp15(access, ARCH_TIMER_REG_CTRL); ctrl |= ARCH_TIMER_CTRL_ENABLE; ctrl &= ~ARCH_TIMER_CTRL_IT_MASK; @@ -753,8 +628,8 @@ static __always_inline void set_next_event(const int access, unsigned long evt, else cnt = __arch_counter_get_cntvct(); - arch_timer_reg_write(access, ARCH_TIMER_REG_CVAL, evt + cnt, clk); - arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); + arch_timer_reg_write_cp15(access, ARCH_TIMER_REG_CVAL, evt + cnt); + arch_timer_reg_write_cp15(access, ARCH_TIMER_REG_CTRL, ctrl); } static int arch_timer_set_next_event_virt(unsigned long evt, @@ -771,60 +646,6 @@ static int arch_timer_set_next_event_phys(unsigned long evt, return 0; } -static noinstr u64 arch_counter_get_cnt_mem(struct arch_timer *t, int offset_lo) -{ - u32 cnt_lo, cnt_hi, tmp_hi; - - do { - cnt_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4)); - cnt_lo = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo)); - tmp_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4)); - } while (cnt_hi != tmp_hi); - - return ((u64) cnt_hi << 32) | cnt_lo; -} - -static __always_inline void set_next_event_mem(const int access, unsigned long evt, - struct clock_event_device *clk) -{ - struct arch_timer *timer = to_arch_timer(clk); - unsigned long ctrl; - u64 cnt; - - ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk); - - /* Timer must be disabled before programming CVAL */ - if (ctrl & ARCH_TIMER_CTRL_ENABLE) { - ctrl &= ~ARCH_TIMER_CTRL_ENABLE; - arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); - } - - ctrl |= ARCH_TIMER_CTRL_ENABLE; - ctrl &= ~ARCH_TIMER_CTRL_IT_MASK; - - if (access == ARCH_TIMER_MEM_VIRT_ACCESS) - cnt = arch_counter_get_cnt_mem(timer, CNTVCT_LO); - else - cnt = arch_counter_get_cnt_mem(timer, CNTPCT_LO); - - arch_timer_reg_write(access, ARCH_TIMER_REG_CVAL, evt + cnt, clk); - arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); -} - -static int arch_timer_set_next_event_virt_mem(unsigned long evt, - struct clock_event_device *clk) -{ - set_next_event_mem(ARCH_TIMER_MEM_VIRT_ACCESS, evt, clk); - return 0; -} - -static int arch_timer_set_next_event_phys_mem(unsigned long evt, - struct clock_event_device *clk) -{ - set_next_event_mem(ARCH_TIMER_MEM_PHYS_ACCESS, evt, clk); - return 0; -} - static u64 __arch_timer_check_delta(void) { #ifdef CONFIG_ARM64 @@ -850,63 +671,41 @@ static u64 __arch_timer_check_delta(void) return CLOCKSOURCE_MASK(arch_counter_get_width()); } -static void __arch_timer_setup(unsigned type, - struct clock_event_device *clk) +static void __arch_timer_setup(struct clock_event_device *clk) { + typeof(clk->set_next_event) sne; u64 max_delta; clk->features = CLOCK_EVT_FEAT_ONESHOT; - if (type == ARCH_TIMER_TYPE_CP15) { - typeof(clk->set_next_event) sne; + arch_timer_check_ool_workaround(ate_match_local_cap_id, NULL); - arch_timer_check_ool_workaround(ate_match_local_cap_id, NULL); - - if (arch_timer_c3stop) - clk->features |= CLOCK_EVT_FEAT_C3STOP; - clk->name = "arch_sys_timer"; - clk->rating = 450; - clk->cpumask = cpumask_of(smp_processor_id()); - clk->irq = arch_timer_ppi[arch_timer_uses_ppi]; - switch (arch_timer_uses_ppi) { - case ARCH_TIMER_VIRT_PPI: - clk->set_state_shutdown = arch_timer_shutdown_virt; - clk->set_state_oneshot_stopped = arch_timer_shutdown_virt; - sne = erratum_handler(set_next_event_virt); - break; - case ARCH_TIMER_PHYS_SECURE_PPI: - case ARCH_TIMER_PHYS_NONSECURE_PPI: - case ARCH_TIMER_HYP_PPI: - clk->set_state_shutdown = arch_timer_shutdown_phys; - clk->set_state_oneshot_stopped = arch_timer_shutdown_phys; - sne = erratum_handler(set_next_event_phys); - break; - default: - BUG(); - } - - clk->set_next_event = sne; - max_delta = __arch_timer_check_delta(); - } else { - clk->features |= CLOCK_EVT_FEAT_DYNIRQ; - clk->name = "arch_mem_timer"; - clk->rating = 400; - clk->cpumask = cpu_possible_mask; - if (arch_timer_mem_use_virtual) { - clk->set_state_shutdown = arch_timer_shutdown_virt_mem; - clk->set_state_oneshot_stopped = arch_timer_shutdown_virt_mem; - clk->set_next_event = - arch_timer_set_next_event_virt_mem; - } else { - clk->set_state_shutdown = arch_timer_shutdown_phys_mem; - clk->set_state_oneshot_stopped = arch_timer_shutdown_phys_mem; - clk->set_next_event = - arch_timer_set_next_event_phys_mem; - } - - max_delta = CLOCKSOURCE_MASK(56); + if (arch_timer_c3stop) + clk->features |= CLOCK_EVT_FEAT_C3STOP; + clk->name = "arch_sys_timer"; + clk->rating = 450; + clk->cpumask = cpumask_of(smp_processor_id()); + clk->irq = arch_timer_ppi[arch_timer_uses_ppi]; + switch (arch_timer_uses_ppi) { + case ARCH_TIMER_VIRT_PPI: + clk->set_state_shutdown = arch_timer_shutdown_virt; + clk->set_state_oneshot_stopped = arch_timer_shutdown_virt; + sne = erratum_handler(set_next_event_virt); + break; + case ARCH_TIMER_PHYS_SECURE_PPI: + case ARCH_TIMER_PHYS_NONSECURE_PPI: + case ARCH_TIMER_HYP_PPI: + clk->set_state_shutdown = arch_timer_shutdown_phys; + clk->set_state_oneshot_stopped = arch_timer_shutdown_phys; + sne = erratum_handler(set_next_event_phys); + break; + default: + BUG(); } + clk->set_next_event = sne; + max_delta = __arch_timer_check_delta(); + clk->set_state_shutdown(clk); clockevents_config_and_register(clk, arch_timer_rate, 0xf, max_delta); @@ -1029,7 +828,7 @@ static int arch_timer_starting_cpu(unsigned int cpu) struct clock_event_device *clk = this_cpu_ptr(arch_timer_evt); u32 flags; - __arch_timer_setup(ARCH_TIMER_TYPE_CP15, clk); + __arch_timer_setup(clk); flags = check_ppi_trigger(arch_timer_ppi[arch_timer_uses_ppi]); enable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi], flags); @@ -1075,22 +874,12 @@ static void __init arch_timer_of_configure_rate(u32 rate, struct device_node *np pr_warn("frequency not available\n"); } -static void __init arch_timer_banner(unsigned type) +static void __init arch_timer_banner(void) { - pr_info("%s%s%s timer(s) running at %lu.%02luMHz (%s%s%s).\n", - type & ARCH_TIMER_TYPE_CP15 ? "cp15" : "", - type == (ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM) ? - " and " : "", - type & ARCH_TIMER_TYPE_MEM ? "mmio" : "", + pr_info("cp15 timer running at %lu.%02luMHz (%s).\n", (unsigned long)arch_timer_rate / 1000000, (unsigned long)(arch_timer_rate / 10000) % 100, - type & ARCH_TIMER_TYPE_CP15 ? - (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) ? "virt" : "phys" : - "", - type == (ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM) ? "/" : "", - type & ARCH_TIMER_TYPE_MEM ? - arch_timer_mem_use_virtual ? "virt" : "phys" : - ""); + (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) ? "virt" : "phys"); } u32 arch_timer_get_rate(void) @@ -1108,11 +897,6 @@ bool arch_timer_evtstrm_available(void) return cpumask_test_cpu(raw_smp_processor_id(), &evtstrm_available); } -static noinstr u64 arch_counter_get_cntvct_mem(void) -{ - return arch_counter_get_cnt_mem(arch_timer_mem, CNTVCT_LO); -} - static struct arch_timer_kvm_info arch_timer_kvm_info; struct arch_timer_kvm_info *arch_timer_get_kvm_info(void) @@ -1120,42 +904,35 @@ struct arch_timer_kvm_info *arch_timer_get_kvm_info(void) return &arch_timer_kvm_info; } -static void __init arch_counter_register(unsigned type) +static void __init arch_counter_register(void) { u64 (*scr)(void); + u64 (*rd)(void); u64 start_count; int width; - /* Register the CP15 based counter if we have one */ - if (type & ARCH_TIMER_TYPE_CP15) { - u64 (*rd)(void); - - if ((IS_ENABLED(CONFIG_ARM64) && !is_hyp_mode_available()) || - arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) { - if (arch_timer_counter_has_wa()) { - rd = arch_counter_get_cntvct_stable; - scr = raw_counter_get_cntvct_stable; - } else { - rd = arch_counter_get_cntvct; - scr = arch_counter_get_cntvct; - } + if ((IS_ENABLED(CONFIG_ARM64) && !is_hyp_mode_available()) || + arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) { + if (arch_timer_counter_has_wa()) { + rd = arch_counter_get_cntvct_stable; + scr = raw_counter_get_cntvct_stable; } else { - if (arch_timer_counter_has_wa()) { - rd = arch_counter_get_cntpct_stable; - scr = raw_counter_get_cntpct_stable; - } else { - rd = arch_counter_get_cntpct; - scr = arch_counter_get_cntpct; - } + rd = arch_counter_get_cntvct; + scr = arch_counter_get_cntvct; } - - arch_timer_read_counter = rd; - clocksource_counter.vdso_clock_mode = vdso_default; } else { - arch_timer_read_counter = arch_counter_get_cntvct_mem; - scr = arch_counter_get_cntvct_mem; + if (arch_timer_counter_has_wa()) { + rd = arch_counter_get_cntpct_stable; + scr = raw_counter_get_cntpct_stable; + } else { + rd = arch_counter_get_cntpct; + scr = arch_counter_get_cntpct; + } } + arch_timer_read_counter = rd; + clocksource_counter.vdso_clock_mode = vdso_default; + width = arch_counter_get_width(); clocksource_counter.mask = CLOCKSOURCE_MASK(width); cyclecounter.mask = CLOCKSOURCE_MASK(width); @@ -1303,76 +1080,10 @@ static int __init arch_timer_register(void) return err; } -static int __init arch_timer_mem_register(void __iomem *base, unsigned int irq) -{ - int ret; - irq_handler_t func; - - arch_timer_mem = kzalloc(sizeof(*arch_timer_mem), GFP_KERNEL); - if (!arch_timer_mem) - return -ENOMEM; - - arch_timer_mem->base = base; - arch_timer_mem->evt.irq = irq; - __arch_timer_setup(ARCH_TIMER_TYPE_MEM, &arch_timer_mem->evt); - - if (arch_timer_mem_use_virtual) - func = arch_timer_handler_virt_mem; - else - func = arch_timer_handler_phys_mem; - - ret = request_irq(irq, func, IRQF_TIMER, "arch_mem_timer", &arch_timer_mem->evt); - if (ret) { - pr_err("Failed to request mem timer irq\n"); - kfree(arch_timer_mem); - arch_timer_mem = NULL; - } - - return ret; -} - -static const struct of_device_id arch_timer_of_match[] __initconst = { - { .compatible = "arm,armv7-timer", }, - { .compatible = "arm,armv8-timer", }, - {}, -}; - -static const struct of_device_id arch_timer_mem_of_match[] __initconst = { - { .compatible = "arm,armv7-timer-mem", }, - {}, -}; - -static bool __init arch_timer_needs_of_probing(void) -{ - struct device_node *dn; - bool needs_probing = false; - unsigned int mask = ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM; - - /* We have two timers, and both device-tree nodes are probed. */ - if ((arch_timers_present & mask) == mask) - return false; - - /* - * Only one type of timer is probed, - * check if we have another type of timer node in device-tree. - */ - if (arch_timers_present & ARCH_TIMER_TYPE_CP15) - dn = of_find_matching_node(NULL, arch_timer_mem_of_match); - else - dn = of_find_matching_node(NULL, arch_timer_of_match); - - if (dn && of_device_is_available(dn)) - needs_probing = true; - - of_node_put(dn); - - return needs_probing; -} - static int __init arch_timer_common_init(void) { - arch_timer_banner(arch_timers_present); - arch_counter_register(arch_timers_present); + arch_timer_banner(); + arch_counter_register(); return arch_timer_arch_init(); } @@ -1421,13 +1132,11 @@ static int __init arch_timer_of_init(struct device_node *np) u32 rate; bool has_names; - if (arch_timers_present & ARCH_TIMER_TYPE_CP15) { + if (arch_timer_evt) { pr_warn("multiple nodes in dt, skipping\n"); return 0; } - arch_timers_present |= ARCH_TIMER_TYPE_CP15; - has_names = of_property_present(np, "interrupt-names"); for (i = ARCH_TIMER_PHYS_SECURE_PPI; i < ARCH_TIMER_MAX_TIMER_PPI; i++) { @@ -1472,283 +1181,22 @@ static int __init arch_timer_of_init(struct device_node *np) if (ret) return ret; - if (arch_timer_needs_of_probing()) - return 0; - return arch_timer_common_init(); } TIMER_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init); TIMER_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init); -static u32 __init -arch_timer_mem_frame_get_cntfrq(struct arch_timer_mem_frame *frame) -{ - void __iomem *base; - u32 rate; - - base = ioremap(frame->cntbase, frame->size); - if (!base) { - pr_err("Unable to map frame @ %pa\n", &frame->cntbase); - return 0; - } - - rate = readl_relaxed(base + CNTFRQ); - - iounmap(base); - - return rate; -} - -static struct arch_timer_mem_frame * __init -arch_timer_mem_find_best_frame(struct arch_timer_mem *timer_mem) -{ - struct arch_timer_mem_frame *frame, *best_frame = NULL; - void __iomem *cntctlbase; - u32 cnttidr; - int i; - - cntctlbase = ioremap(timer_mem->cntctlbase, timer_mem->size); - if (!cntctlbase) { - pr_err("Can't map CNTCTLBase @ %pa\n", - &timer_mem->cntctlbase); - return NULL; - } - - cnttidr = readl_relaxed(cntctlbase + CNTTIDR); - - /* - * Try to find a virtual capable frame. Otherwise fall back to a - * physical capable frame. - */ - for (i = 0; i < ARCH_TIMER_MEM_MAX_FRAMES; i++) { - u32 cntacr = CNTACR_RFRQ | CNTACR_RWPT | CNTACR_RPCT | - CNTACR_RWVT | CNTACR_RVOFF | CNTACR_RVCT; - - frame = &timer_mem->frame[i]; - if (!frame->valid) - continue; - - /* Try enabling everything, and see what sticks */ - writel_relaxed(cntacr, cntctlbase + CNTACR(i)); - cntacr = readl_relaxed(cntctlbase + CNTACR(i)); - - if ((cnttidr & CNTTIDR_VIRT(i)) && - !(~cntacr & (CNTACR_RWVT | CNTACR_RVCT))) { - best_frame = frame; - arch_timer_mem_use_virtual = true; - break; - } - - if (~cntacr & (CNTACR_RWPT | CNTACR_RPCT)) - continue; - - best_frame = frame; - } - - iounmap(cntctlbase); - - return best_frame; -} - -static int __init -arch_timer_mem_frame_register(struct arch_timer_mem_frame *frame) -{ - void __iomem *base; - int ret, irq; - - if (arch_timer_mem_use_virtual) - irq = frame->virt_irq; - else - irq = frame->phys_irq; - - if (!irq) { - pr_err("Frame missing %s irq.\n", - arch_timer_mem_use_virtual ? "virt" : "phys"); - return -EINVAL; - } - - if (!request_mem_region(frame->cntbase, frame->size, - "arch_mem_timer")) - return -EBUSY; - - base = ioremap(frame->cntbase, frame->size); - if (!base) { - pr_err("Can't map frame's registers\n"); - return -ENXIO; - } - - ret = arch_timer_mem_register(base, irq); - if (ret) { - iounmap(base); - return ret; - } - - arch_timers_present |= ARCH_TIMER_TYPE_MEM; - - return 0; -} - -static int __init arch_timer_mem_of_init(struct device_node *np) -{ - struct arch_timer_mem *timer_mem; - struct arch_timer_mem_frame *frame; - struct resource res; - int ret = -EINVAL; - u32 rate; - - timer_mem = kzalloc(sizeof(*timer_mem), GFP_KERNEL); - if (!timer_mem) - return -ENOMEM; - - if (of_address_to_resource(np, 0, &res)) - goto out; - timer_mem->cntctlbase = res.start; - timer_mem->size = resource_size(&res); - - for_each_available_child_of_node_scoped(np, frame_node) { - u32 n; - struct arch_timer_mem_frame *frame; - - if (of_property_read_u32(frame_node, "frame-number", &n)) { - pr_err(FW_BUG "Missing frame-number.\n"); - goto out; - } - if (n >= ARCH_TIMER_MEM_MAX_FRAMES) { - pr_err(FW_BUG "Wrong frame-number, only 0-%u are permitted.\n", - ARCH_TIMER_MEM_MAX_FRAMES - 1); - goto out; - } - frame = &timer_mem->frame[n]; - - if (frame->valid) { - pr_err(FW_BUG "Duplicated frame-number.\n"); - goto out; - } - - if (of_address_to_resource(frame_node, 0, &res)) - goto out; - - frame->cntbase = res.start; - frame->size = resource_size(&res); - - frame->virt_irq = irq_of_parse_and_map(frame_node, - ARCH_TIMER_VIRT_SPI); - frame->phys_irq = irq_of_parse_and_map(frame_node, - ARCH_TIMER_PHYS_SPI); - - frame->valid = true; - } - - frame = arch_timer_mem_find_best_frame(timer_mem); - if (!frame) { - pr_err("Unable to find a suitable frame in timer @ %pa\n", - &timer_mem->cntctlbase); - ret = -EINVAL; - goto out; - } - - rate = arch_timer_mem_frame_get_cntfrq(frame); - arch_timer_of_configure_rate(rate, np); - - ret = arch_timer_mem_frame_register(frame); - if (!ret && !arch_timer_needs_of_probing()) - ret = arch_timer_common_init(); -out: - kfree(timer_mem); - return ret; -} -TIMER_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", - arch_timer_mem_of_init); - #ifdef CONFIG_ACPI_GTDT -static int __init -arch_timer_mem_verify_cntfrq(struct arch_timer_mem *timer_mem) -{ - struct arch_timer_mem_frame *frame; - u32 rate; - int i; - - for (i = 0; i < ARCH_TIMER_MEM_MAX_FRAMES; i++) { - frame = &timer_mem->frame[i]; - - if (!frame->valid) - continue; - - rate = arch_timer_mem_frame_get_cntfrq(frame); - if (rate == arch_timer_rate) - continue; - - pr_err(FW_BUG "CNTFRQ mismatch: frame @ %pa: (0x%08lx), CPU: (0x%08lx)\n", - &frame->cntbase, - (unsigned long)rate, (unsigned long)arch_timer_rate); - - return -EINVAL; - } - - return 0; -} - -static int __init arch_timer_mem_acpi_init(int platform_timer_count) -{ - struct arch_timer_mem *timers, *timer; - struct arch_timer_mem_frame *frame, *best_frame = NULL; - int timer_count, i, ret = 0; - - timers = kcalloc(platform_timer_count, sizeof(*timers), - GFP_KERNEL); - if (!timers) - return -ENOMEM; - - ret = acpi_arch_timer_mem_init(timers, &timer_count); - if (ret || !timer_count) - goto out; - - /* - * While unlikely, it's theoretically possible that none of the frames - * in a timer expose the combination of feature we want. - */ - for (i = 0; i < timer_count; i++) { - timer = &timers[i]; - - frame = arch_timer_mem_find_best_frame(timer); - if (!best_frame) - best_frame = frame; - - ret = arch_timer_mem_verify_cntfrq(timer); - if (ret) { - pr_err("Disabling MMIO timers due to CNTFRQ mismatch\n"); - goto out; - } - - if (!best_frame) /* implies !frame */ - /* - * Only complain about missing suitable frames if we - * haven't already found one in a previous iteration. - */ - pr_err("Unable to find a suitable frame in timer @ %pa\n", - &timer->cntctlbase); - } - - if (best_frame) - ret = arch_timer_mem_frame_register(best_frame); -out: - kfree(timers); - return ret; -} - -/* Initialize per-processor generic timer and memory-mapped timer(if present) */ static int __init arch_timer_acpi_init(struct acpi_table_header *table) { - int ret, platform_timer_count; + int ret; - if (arch_timers_present & ARCH_TIMER_TYPE_CP15) { + if (arch_timer_evt) { pr_warn("already initialized, skipping\n"); return -EINVAL; } - arch_timers_present |= ARCH_TIMER_TYPE_CP15; - - ret = acpi_gtdt_init(table, &platform_timer_count); + ret = acpi_gtdt_init(table, NULL); if (ret) return ret; @@ -1790,10 +1238,6 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table) if (ret) return ret; - if (platform_timer_count && - arch_timer_mem_acpi_init(platform_timer_count)) - pr_err("Failed to initialize memory-mapped timer.\n"); - return arch_timer_common_init(); } TIMER_ACPI_DECLARE(arch_timer, ACPI_SIG_GTDT, arch_timer_acpi_init); diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index ce6521ad04d1..2eda895f19f5 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -9,9 +9,6 @@ #include #include -#define ARCH_TIMER_TYPE_CP15 BIT(0) -#define ARCH_TIMER_TYPE_MEM BIT(1) - #define ARCH_TIMER_CTRL_ENABLE (1 << 0) #define ARCH_TIMER_CTRL_IT_MASK (1 << 1) #define ARCH_TIMER_CTRL_IT_STAT (1 << 2) @@ -51,8 +48,6 @@ enum arch_timer_spi_nr { #define ARCH_TIMER_PHYS_ACCESS 0 #define ARCH_TIMER_VIRT_ACCESS 1 -#define ARCH_TIMER_MEM_PHYS_ACCESS 2 -#define ARCH_TIMER_MEM_VIRT_ACCESS 3 #define ARCH_TIMER_MEM_MAX_FRAMES 8 From 4e9bfe6969a768ef40c669fd100c9be70fb78a1f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 14 Aug 2025 16:46:22 +0100 Subject: [PATCH 41/52] clocksource/drivers/arm_arch_timer_mmio: Add MMIO clocksource The MMIO driver can also double as a clocksource, something that was missing in its previous incarnation. Add it for completeness. Signed-off-by: Marc Zyngier Signed-off-by: Daniel Lezcano Tested-by: Sudeep Holla Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20250814154622.10193-5-maz@kernel.org --- drivers/clocksource/arm_arch_timer_mmio.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/clocksource/arm_arch_timer_mmio.c b/drivers/clocksource/arm_arch_timer_mmio.c index 3522d1d8cb97..ebe1987d651e 100644 --- a/drivers/clocksource/arm_arch_timer_mmio.c +++ b/drivers/clocksource/arm_arch_timer_mmio.c @@ -45,6 +45,7 @@ enum arch_timer_access { struct arch_timer { struct clock_event_device evt; + struct clocksource cs; struct arch_timer_mem *gt_block; void __iomem *base; enum arch_timer_access access; @@ -52,6 +53,7 @@ struct arch_timer { }; #define evt_to_arch_timer(e) container_of(e, struct arch_timer, evt) +#define cs_to_arch_timer(c) container_of(c, struct arch_timer, cs) static void arch_timer_mmio_write(struct arch_timer *timer, enum arch_timer_reg reg, u64 val) @@ -128,6 +130,13 @@ static noinstr u64 arch_counter_mmio_get_cnt(struct arch_timer *t) return ((u64) cnt_hi << 32) | cnt_lo; } +static u64 arch_mmio_counter_read(struct clocksource *cs) +{ + struct arch_timer *at = cs_to_arch_timer(cs); + + return arch_counter_mmio_get_cnt(at); +} + static int arch_timer_mmio_shutdown(struct clock_event_device *clk) { struct arch_timer *at = evt_to_arch_timer(clk); @@ -256,6 +265,16 @@ static void arch_timer_mmio_setup(struct arch_timer *at, int irq) (unsigned long)CLOCKSOURCE_MASK(56)); enable_irq(at->evt.irq); + + at->cs = (struct clocksource) { + .name = "arch_mmio_counter", + .rating = 300, + .read = arch_mmio_counter_read, + .mask = CLOCKSOURCE_MASK(56), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + }; + + clocksource_register_hz(&at->cs, at->rate); } static int arch_timer_mmio_frame_register(struct platform_device *pdev, From 0494fc345b377d1207c2cbfef67dc51f6ec874c0 Mon Sep 17 00:00:00 2001 From: Gokul Praveen Date: Tue, 12 Aug 2025 16:23:46 +0530 Subject: [PATCH 42/52] clocksource/drivers/timer-ti-dm : Capture functionality for OMAP DM timer Add PWM capture function in DM timer driver. OMAP DM timer hardware supports capture feature.It can be used to timestamp events (falling/rising edges) detected on input signal. Signed-off-by: Gokul Praveen Signed-off-by: Daniel Lezcano Reviewed-by: Neha Malcom Francis Link: https://lore.kernel.org/r/20250812105346.203541-1-g-praveen@ti.com --- drivers/clocksource/timer-ti-dm.c | 119 ++++++++++++++++++++- include/linux/platform_data/dmtimer-omap.h | 4 + 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index e9e32df6b566..793e7cdcb1b1 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -31,6 +31,7 @@ #include #include +#include /* * timer errata flags @@ -836,6 +837,48 @@ static int omap_dm_timer_set_match(struct omap_dm_timer *cookie, int enable, return 0; } +static int omap_dm_timer_set_cap(struct omap_dm_timer *cookie, + int autoreload, bool config_period) +{ + struct dmtimer *timer; + struct device *dev; + int rc; + u32 l; + + timer = to_dmtimer(cookie); + if (unlikely(!timer)) + return -EINVAL; + + dev = &timer->pdev->dev; + rc = pm_runtime_resume_and_get(dev); + if (rc) + return rc; + /* + * 1. Select autoreload mode. TIMER_TCLR[1] AR bit. + * 2. TIMER_TCLR[14]: Sets the functionality of the TIMER IO pin. + * 3. TIMER_TCLR[13] : Capture mode select bit. + * 3. TIMER_TCLR[9-8] : Select transition capture mode. + */ + + l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); + + if (autoreload) + l |= OMAP_TIMER_CTRL_AR; + + l |= OMAP_TIMER_CTRL_CAPTMODE | OMAP_TIMER_CTRL_GPOCFG; + + if (config_period == true) + l |= OMAP_TIMER_CTRL_TCM_LOWTOHIGH; /* Time Period config */ + else + l |= OMAP_TIMER_CTRL_TCM_BOTHEDGES; /* Duty Cycle config */ + + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); + + pm_runtime_put_sync(dev); + + return 0; +} + static int omap_dm_timer_set_pwm(struct omap_dm_timer *cookie, int def_on, int toggle, int trigger, int autoreload) { @@ -1023,23 +1066,92 @@ static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *cookie) return __omap_dm_timer_read_counter(timer); } +static inline unsigned int __omap_dm_timer_cap(struct dmtimer *timer, int idx) +{ + return idx == 0 ? dmtimer_read(timer, OMAP_TIMER_CAPTURE_REG) : + dmtimer_read(timer, OMAP_TIMER_CAPTURE2_REG); +} + static int omap_dm_timer_write_counter(struct omap_dm_timer *cookie, unsigned int value) { struct dmtimer *timer; + struct device *dev; timer = to_dmtimer(cookie); - if (unlikely(!timer || !atomic_read(&timer->enabled))) { - pr_err("%s: timer not available or enabled.\n", __func__); + if (unlikely(!timer)) { + pr_err("%s: timer not available.\n", __func__); return -EINVAL; } + dev = &timer->pdev->dev; + + pm_runtime_resume_and_get(dev); dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, value); + pm_runtime_put_sync(dev); /* Save the context */ timer->context.tcrr = value; return 0; } +/** + * omap_dm_timer_cap_counter() - Calculate the high count or period count depending on the + * configuration. + * @cookie:Pointer to OMAP DM timer + * @is_period:Whether to configure timer in period or duty cycle mode + * + * Return high count or period count if timer is enabled else appropriate error. + */ +static unsigned int omap_dm_timer_cap_counter(struct omap_dm_timer *cookie, bool is_period) +{ + struct dmtimer *timer; + unsigned int cap1 = 0; + unsigned int cap2 = 0; + u32 l, ret; + + timer = to_dmtimer(cookie); + if (unlikely(!timer || !atomic_read(&timer->enabled))) { + pr_err("%s:timer is not available or enabled.%p\n", __func__, (void *)timer); + return -EINVAL; + } + + /* Stop the timer */ + omap_dm_timer_stop(cookie); + + /* Clear the timer counter value to 0 */ + ret = omap_dm_timer_write_counter(cookie, 0); + if (ret) + return ret; + + /* Sets the timer capture configuration for period/duty cycle calculation */ + ret = omap_dm_timer_set_cap(cookie, true, is_period); + if (ret) { + pr_err("%s: Failed to set timer capture configuration.\n", __func__); + return ret; + } + /* Start the timer */ + omap_dm_timer_start(cookie); + + /* + * 1 sec delay is given so as to provide + * enough time to capture low frequency signals. + */ + msleep(1000); + + cap1 = __omap_dm_timer_cap(timer, 0); + cap2 = __omap_dm_timer_cap(timer, 1); + + /* + * Clears the TCLR configuration. + * The start bit must be set to 1 as the timer is already in start mode. + */ + l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); + l &= ~(0xffff) | 0x1; + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); + + return (cap2-cap1); +} + static int __maybe_unused omap_dm_timer_runtime_suspend(struct device *dev) { struct dmtimer *timer = dev_get_drvdata(dev); @@ -1246,6 +1358,9 @@ static const struct omap_dm_timer_ops dmtimer_ops = { .write_counter = omap_dm_timer_write_counter, .read_status = omap_dm_timer_read_status, .write_status = omap_dm_timer_write_status, + .set_cap = omap_dm_timer_set_cap, + .get_cap_status = omap_dm_timer_get_pwm_status, + .read_cap = omap_dm_timer_cap_counter, }; static const struct dmtimer_platform_data omap3plus_pdata = { diff --git a/include/linux/platform_data/dmtimer-omap.h b/include/linux/platform_data/dmtimer-omap.h index 95d852aef130..726d89143842 100644 --- a/include/linux/platform_data/dmtimer-omap.h +++ b/include/linux/platform_data/dmtimer-omap.h @@ -36,9 +36,13 @@ struct omap_dm_timer_ops { int (*set_pwm)(struct omap_dm_timer *timer, int def_on, int toggle, int trigger, int autoreload); int (*get_pwm_status)(struct omap_dm_timer *timer); + int (*set_cap)(struct omap_dm_timer *timer, + int autoreload, bool config_period); + int (*get_cap_status)(struct omap_dm_timer *timer); int (*set_prescaler)(struct omap_dm_timer *timer, int prescaler); unsigned int (*read_counter)(struct omap_dm_timer *timer); + unsigned int (*read_cap)(struct omap_dm_timer *timer, bool is_period); int (*write_counter)(struct omap_dm_timer *timer, unsigned int value); unsigned int (*read_status)(struct omap_dm_timer *timer); From e7a25106335041aeca4fdf50a84804c90142c886 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Mon, 4 Aug 2025 04:03:25 -0400 Subject: [PATCH 43/52] clocksource/drivers/timer-rtl-otto: Work around dying timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OpenWrt distribution has switched from kernel longterm 6.6 to 6.12. Reports show that devices with the Realtek Otto switch platform die during operation and are rebooted by the watchdog. Sorting out other possible reasons the Otto timer is to blame. The platform currently consists of 4 targets with different hardware revisions. It is not 100% clear which devices and revisions are affected. Analysis shows: A more aggressive sched/deadline handling leads to more timer starts with small intervals. This increases the bug chances. See https://marc.info/?l=linux-kernel&m=175276556023276&w=2 Focusing on the real issue a hardware limitation on some devices was found. There is a minimal chance that a timer ends without firing an interrupt if it is reprogrammed within the 5us before its expiration time. Work around this issue by introducing a bounce() function. It restarts the timer directly before the normal restart functions as follows: - Stop timer - Restart timer with a slow frequency. - Target time will be >5us - The subsequent normal restart is outside the critical window Downstream has already tested and confirmed a patch. See https://github.com/openwrt/openwrt/pull/19468 https://forum.openwrt.org/t/support-for-rtl838x-based-managed-switches/57875/3788 Signed-off-by: Markus Stockhausen Signed-off-by: Daniel Lezcano Tested-by: Stephen Howell Tested-by: Bjørn Mork Link: https://lore.kernel.org/r/20250804080328.2609287-2-markus.stockhausen@gmx.de --- drivers/clocksource/timer-rtl-otto.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/clocksource/timer-rtl-otto.c b/drivers/clocksource/timer-rtl-otto.c index 8a3068b36e75..8be45a11fb8b 100644 --- a/drivers/clocksource/timer-rtl-otto.c +++ b/drivers/clocksource/timer-rtl-otto.c @@ -38,6 +38,7 @@ #define RTTM_BIT_COUNT 28 #define RTTM_MIN_DELTA 8 #define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28) +#define RTTM_MAX_DIVISOR GENMASK(15, 0) /* * Timers are derived from the LXB clock frequency. Usually this is a fixed @@ -112,6 +113,22 @@ static irqreturn_t rttm_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static void rttm_bounce_timer(void __iomem *base, u32 mode) +{ + /* + * When a running timer has less than ~5us left, a stop/start sequence + * might fail. While the details are unknown the most evident effect is + * that the subsequent interrupt will not be fired. + * + * As a workaround issue an intermediate restart with a very slow + * frequency of ~3kHz keeping the target counter (>=8). So the follow + * up restart will always be issued outside the critical window. + */ + + rttm_disable_timer(base); + rttm_enable_timer(base, mode, RTTM_MAX_DIVISOR); +} + static void rttm_stop_timer(void __iomem *base) { rttm_disable_timer(base); @@ -129,6 +146,7 @@ static int rttm_next_event(unsigned long delta, struct clock_event_device *clkev struct timer_of *to = to_timer_of(clkevt); RTTM_DEBUG(to->of_base.base); + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); rttm_stop_timer(to->of_base.base); rttm_set_period(to->of_base.base, delta); rttm_start_timer(to, RTTM_CTRL_COUNTER); @@ -141,6 +159,7 @@ static int rttm_state_oneshot(struct clock_event_device *clkevt) struct timer_of *to = to_timer_of(clkevt); RTTM_DEBUG(to->of_base.base); + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); rttm_stop_timer(to->of_base.base); rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); rttm_start_timer(to, RTTM_CTRL_COUNTER); @@ -153,6 +172,7 @@ static int rttm_state_periodic(struct clock_event_device *clkevt) struct timer_of *to = to_timer_of(clkevt); RTTM_DEBUG(to->of_base.base); + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER); rttm_stop_timer(to->of_base.base); rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); rttm_start_timer(to, RTTM_CTRL_TIMER); From ca90147e55a78441794aef5cb4a8d1cf8d0e209f Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Mon, 4 Aug 2025 04:03:26 -0400 Subject: [PATCH 44/52] clocksource/drivers/timer-rtl-otto: Drop set_counter function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current counter value is a read only register. It will be reset when writing a new target timer value with rttm_set_period(). rttm_set_counter() is essentially a noop. Drop it. While this makes rttm_start_timer() and rttm_enable_timer() the same functions keep both to make the established abstraction layers for register and control functions active. Downstream has already tested and confirmed a patch. See https://github.com/openwrt/openwrt/pull/19468 https://forum.openwrt.org/t/support-for-rtl838x-based-managed-switches/57875/3788 Signed-off-by: Markus Stockhausen Signed-off-by: Daniel Lezcano Tested-by: Stephen Howell Tested-by: Bjørn Mork Link: https://lore.kernel.org/r/20250804080328.2609287-3-markus.stockhausen@gmx.de --- drivers/clocksource/timer-rtl-otto.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/clocksource/timer-rtl-otto.c b/drivers/clocksource/timer-rtl-otto.c index 8be45a11fb8b..48ba1164f3fb 100644 --- a/drivers/clocksource/timer-rtl-otto.c +++ b/drivers/clocksource/timer-rtl-otto.c @@ -56,11 +56,6 @@ struct rttm_cs { }; /* Simple internal register functions */ -static inline void rttm_set_counter(void __iomem *base, unsigned int counter) -{ - iowrite32(counter, base + RTTM_CNT); -} - static inline unsigned int rttm_get_counter(void __iomem *base) { return ioread32(base + RTTM_CNT); @@ -137,7 +132,6 @@ static void rttm_stop_timer(void __iomem *base) static void rttm_start_timer(struct timer_of *to, u32 mode) { - rttm_set_counter(to->of_base.base, 0); rttm_enable_timer(to->of_base.base, mode, to->of_clk.rate / RTTM_TICKS_PER_SEC); } From c445bffbf28f721e05d0ce06895045fc62aaff7c Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Mon, 4 Aug 2025 04:03:27 -0400 Subject: [PATCH 45/52] clocksource/drivers/timer-rtl-otto: Do not interfere with interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During normal operation the timers are reprogrammed including an interrupt acknowledgement. This has no effect as the whole timer is setup from scratch afterwards. Especially in an interrupt this has already been done by rttm_timer_interrupt(). Change the behaviour as follows: - Use rttm_disable_timer() during reprogramming - Keep rttm_stop_timer() for all other use cases. Downstream has already tested and confirmed a patch. See https://github.com/openwrt/openwrt/pull/19468 https://forum.openwrt.org/t/support-for-rtl838x-based-managed-switches/57875/3788 Signed-off-by: Markus Stockhausen Signed-off-by: Daniel Lezcano Tested-by: Stephen Howell Tested-by: Bjørn Mork Link: https://lore.kernel.org/r/20250804080328.2609287-4-markus.stockhausen@gmx.de --- drivers/clocksource/timer-rtl-otto.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clocksource/timer-rtl-otto.c b/drivers/clocksource/timer-rtl-otto.c index 48ba1164f3fb..42f702aca689 100644 --- a/drivers/clocksource/timer-rtl-otto.c +++ b/drivers/clocksource/timer-rtl-otto.c @@ -141,7 +141,7 @@ static int rttm_next_event(unsigned long delta, struct clock_event_device *clkev RTTM_DEBUG(to->of_base.base); rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); - rttm_stop_timer(to->of_base.base); + rttm_disable_timer(to->of_base.base); rttm_set_period(to->of_base.base, delta); rttm_start_timer(to, RTTM_CTRL_COUNTER); @@ -154,7 +154,7 @@ static int rttm_state_oneshot(struct clock_event_device *clkevt) RTTM_DEBUG(to->of_base.base); rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); - rttm_stop_timer(to->of_base.base); + rttm_disable_timer(to->of_base.base); rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); rttm_start_timer(to, RTTM_CTRL_COUNTER); @@ -167,7 +167,7 @@ static int rttm_state_periodic(struct clock_event_device *clkevt) RTTM_DEBUG(to->of_base.base); rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER); - rttm_stop_timer(to->of_base.base); + rttm_disable_timer(to->of_base.base); rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); rttm_start_timer(to, RTTM_CTRL_TIMER); From 931bd9273848aca9dc40dd5cad3fcfe5d0818972 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Mon, 4 Aug 2025 04:03:28 -0400 Subject: [PATCH 46/52] clocksource/drivers/timer-rtl-otto: Simplify documentation While the main SoC PLL is responsible for the lexra bus frequency it has no implications on the the timer divisior. Update the comments accordingly. Signed-off-by: Markus Stockhausen Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250804080328.2609287-5-markus.stockhausen@gmx.de --- drivers/clocksource/timer-rtl-otto.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/timer-rtl-otto.c b/drivers/clocksource/timer-rtl-otto.c index 42f702aca689..6113d2fdd4de 100644 --- a/drivers/clocksource/timer-rtl-otto.c +++ b/drivers/clocksource/timer-rtl-otto.c @@ -41,12 +41,10 @@ #define RTTM_MAX_DIVISOR GENMASK(15, 0) /* - * Timers are derived from the LXB clock frequency. Usually this is a fixed - * multiple of the 25 MHz oscillator. The 930X SOC is an exception from that. - * Its LXB clock has only dividers and uses the switch PLL of 2.45 GHz as its - * base. The only meaningful frequencies we can achieve from that are 175.000 - * MHz and 153.125 MHz. The greatest common divisor of all explained possible - * speeds is 3125000. Pin the timers to this 3.125 MHz reference frequency. + * Timers are derived from the lexra bus (LXB) clock frequency. This is 175 MHz + * on RTL930x and 200 MHz on the other platforms. With 3.125 MHz choose a common + * divisor to have enough range and detail. This provides comparability between + * the different platforms. */ #define RTTM_TICKS_PER_SEC 3125000 From 764d0654114b5ce52aafabdb2bf9ccee0e998651 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 13 Aug 2025 21:06:58 +0200 Subject: [PATCH 47/52] clocksource/drivers/timer-tegra186: Don't print superfluous errors The watchdog core will handle error messages already. Signed-off-by: Wolfram Sang Signed-off-by: Daniel Lezcano Reviewed-by: Mikko Perttunen Link: https://lore.kernel.org/r/20250813190657.3628-2-wsa+renesas@sang-engineering.com --- drivers/clocksource/timer-tegra186.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index 1ec7b38ff8c6..355558893e5f 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -333,16 +333,12 @@ static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra, wdt->base.parent = tegra->dev; err = watchdog_init_timeout(&wdt->base, 5, tegra->dev); - if (err < 0) { - dev_err(tegra->dev, "failed to initialize timeout: %d\n", err); + if (err < 0) return ERR_PTR(err); - } err = devm_watchdog_register_device(tegra->dev, &wdt->base); - if (err < 0) { - dev_err(tegra->dev, "failed to register WDT: %d\n", err); + if (err < 0) return ERR_PTR(err); - } return wdt; } From 21b8a635f3b3d6a165fa257808ed381c13c72e9b Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 10 Aug 2025 18:37:10 -0400 Subject: [PATCH 48/52] clocksource/drivers/ingenic-sysost: Convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch appended to the "under-the-cut" portion of the patch. While changes are being made to 'struct clk_ops', let's also go ahead and fix the formatting of set_rate so that everything lines up as expected. Signed-off-by: Brian Masney Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20250810-clocksource-round-rate-v1-1-486ef53e45eb@redhat.com --- drivers/clocksource/ingenic-sysost.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/clocksource/ingenic-sysost.c b/drivers/clocksource/ingenic-sysost.c index cb6fc2f152d4..e79cfb0b8e05 100644 --- a/drivers/clocksource/ingenic-sysost.c +++ b/drivers/clocksource/ingenic-sysost.c @@ -127,18 +127,23 @@ static u8 ingenic_ost_get_prescale(unsigned long rate, unsigned long req_rate) return 2; /* /16 divider */ } -static long ingenic_ost_round_rate(struct clk_hw *hw, unsigned long req_rate, - unsigned long *parent_rate) +static int ingenic_ost_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - unsigned long rate = *parent_rate; + unsigned long rate = req->best_parent_rate; u8 prescale; - if (req_rate > rate) - return rate; + if (req->rate > rate) { + req->rate = rate; - prescale = ingenic_ost_get_prescale(rate, req_rate); + return 0; + } - return rate >> (prescale * 2); + prescale = ingenic_ost_get_prescale(rate, req->rate); + + req->rate = rate >> (prescale * 2); + + return 0; } static int ingenic_ost_percpu_timer_set_rate(struct clk_hw *hw, unsigned long req_rate, @@ -175,14 +180,14 @@ static int ingenic_ost_global_timer_set_rate(struct clk_hw *hw, unsigned long re static const struct clk_ops ingenic_ost_percpu_timer_ops = { .recalc_rate = ingenic_ost_percpu_timer_recalc_rate, - .round_rate = ingenic_ost_round_rate, - .set_rate = ingenic_ost_percpu_timer_set_rate, + .determine_rate = ingenic_ost_determine_rate, + .set_rate = ingenic_ost_percpu_timer_set_rate, }; static const struct clk_ops ingenic_ost_global_timer_ops = { .recalc_rate = ingenic_ost_global_timer_recalc_rate, - .round_rate = ingenic_ost_round_rate, - .set_rate = ingenic_ost_global_timer_set_rate, + .determine_rate = ingenic_ost_determine_rate, + .set_rate = ingenic_ost_global_timer_set_rate, }; static const char * const ingenic_ost_clk_parents[] = { "ext" }; From 1c4b87c921fb158d853adcb8fd48c2dc07fc6f91 Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 19 Aug 2025 09:52:41 +0200 Subject: [PATCH 49/52] 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 Signed-off-by: Daniel Lezcano Tested-by: Kevin Hilman Tested-by: Patrice Chotard Tested-by: Judith Mendez Reviewed-by: Kevin Hilman Link: https://lore.kernel.org/r/20250819-topic-am43-arm-global-timer-v6-16-v2-1-6d082e2a5161@baylibre.com --- drivers/clocksource/Kconfig | 4 +-- drivers/clocksource/arm_global_timer.c | 44 +++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 0fd662f67d29..ffcd23668763 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -395,8 +395,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 + default 0 depends on ARM_GLOBAL_TIMER help When the ARM global timer initializes, its current rate is declared @@ -406,6 +405,7 @@ config ARM_GT_INITIAL_PRESCALER_VAL 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. + Use 0 to use auto-detection in the driver. config ARM_TIMER_SP804 bool "Support for Dual Timer SP804 module" diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c index 2d86bbc2764a..5e3d6bb7e437 100644 --- a/drivers/clocksource/arm_global_timer.c +++ b/drivers/clocksource/arm_global_timer.c @@ -263,14 +263,13 @@ static void __init gt_delay_timer_init(void) register_current_timer_delay(>_delay_timer); } -static int __init gt_clocksource_init(void) +static int __init gt_clocksource_init(unsigned int psv) { writel(0, gt_base + GT_CONTROL); writel(0, gt_base + GT_COUNTER0); writel(0, gt_base + GT_COUNTER1); /* set prescaler and enable timer on all the cores */ - writel(FIELD_PREP(GT_CONTROL_PRESCALER_MASK, - CONFIG_ARM_GT_INITIAL_PRESCALER_VAL - 1) | + writel(FIELD_PREP(GT_CONTROL_PRESCALER_MASK, psv - 1) | GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); #ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK @@ -338,11 +337,45 @@ static int gt_clk_rate_change_cb(struct notifier_block *nb, return NOTIFY_DONE; } +struct gt_prescaler_config { + const char *compatible; + unsigned long prescaler; +}; + +static const struct gt_prescaler_config gt_prescaler_configs[] = { + /* + * On am43 the global timer clock is a child of the clock used for CPU + * OPPs, so the initial prescaler has to be compatible with all OPPs + * which are 300, 600, 720, 800 and 1000 with a fixed divider of 2, this + * gives us a GCD of 10. Initial frequency is 1000, so the prescaler is + * 50. + */ + { .compatible = "ti,am43", .prescaler = 50 }, + { .compatible = "xlnx,zynq-7000", .prescaler = 2 }, + { .compatible = NULL } +}; + +static unsigned long gt_get_initial_prescaler_value(struct device_node *np) +{ + const struct gt_prescaler_config *config; + + if (CONFIG_ARM_GT_INITIAL_PRESCALER_VAL != 0) + return CONFIG_ARM_GT_INITIAL_PRESCALER_VAL; + + for (config = gt_prescaler_configs; config->compatible; config++) { + if (of_machine_is_compatible(config->compatible)) + return config->prescaler; + } + + return 1; +} + static int __init global_timer_of_register(struct device_node *np) { struct clk *gt_clk; static unsigned long gt_clk_rate; int err; + unsigned long psv; /* * 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) goto out_unmap; } + psv = gt_get_initial_prescaler_value(np); gt_clk_rate = clk_get_rate(gt_clk); - gt_target_rate = gt_clk_rate / CONFIG_ARM_GT_INITIAL_PRESCALER_VAL; + gt_target_rate = gt_clk_rate / psv; gt_clk_rate_change_nb.notifier_call = gt_clk_rate_change_cb; err = clk_notifier_register(gt_clk, >_clk_rate_change_nb); @@ -404,7 +438,7 @@ static int __init global_timer_of_register(struct device_node *np) } /* Register and immediately configure the timer on the boot CPU */ - err = gt_clocksource_init(); + err = gt_clocksource_init(psv); if (err) goto out_irq; From cd32e596f02fc981674573402c1138f616df1728 Mon Sep 17 00:00:00 2001 From: Zhen Ni Date: Thu, 14 Aug 2025 20:33:24 +0800 Subject: [PATCH 50/52] clocksource/drivers/clps711x: Fix resource leaks in error paths The current implementation of clps711x_timer_init() has multiple error paths that directly return without releasing the base I/O memory mapped via of_iomap(). Fix of_iomap leaks in error paths. Fixes: 04410efbb6bc ("clocksource/drivers/clps711x: Convert init function to return error") Fixes: 2a6a8e2d9004 ("clocksource/drivers/clps711x: Remove board support") Signed-off-by: Zhen Ni Signed-off-by: Daniel Lezcano Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20250814123324.1516495-1-zhen.ni@easystack.cn --- drivers/clocksource/clps711x-timer.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c index e95fdc49c226..bbceb0289d45 100644 --- a/drivers/clocksource/clps711x-timer.c +++ b/drivers/clocksource/clps711x-timer.c @@ -78,24 +78,33 @@ static int __init clps711x_timer_init(struct device_node *np) unsigned int irq = irq_of_parse_and_map(np, 0); struct clk *clock = of_clk_get(np, 0); void __iomem *base = of_iomap(np, 0); + int ret = 0; if (!base) return -ENOMEM; - if (!irq) - return -EINVAL; - if (IS_ERR(clock)) - return PTR_ERR(clock); + if (!irq) { + ret = -EINVAL; + goto unmap_io; + } + if (IS_ERR(clock)) { + ret = PTR_ERR(clock); + goto unmap_io; + } switch (of_alias_get_id(np, "timer")) { case CLPS711X_CLKSRC_CLOCKSOURCE: clps711x_clksrc_init(clock, base); break; case CLPS711X_CLKSRC_CLOCKEVENT: - return _clps711x_clkevt_init(clock, base, irq); + ret = _clps711x_clkevt_init(clock, base, irq); + break; default: - return -EINVAL; + ret = -EINVAL; + break; } - return 0; +unmap_io: + iounmap(base); + return ret; } TIMER_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init); From 0c617a3f62100ff25c291735ff907a7ca1c084ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 10 Sep 2025 16:26:56 +0200 Subject: [PATCH 51/52] clocksource/drivers/sh_cmt: Split start/stop of clock source and events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CMT do a housekeeping such as dealing with runtime PM and enable/disable clocks when either a clock source is enabled, or when a new clock event is registered. Doing this type of housekeeping for when a clock event is registered is not always possible as it can happen in contexts where holding spinlocks is not possible. However doing it when registering a clock source is possible. As a first step to address this design break apart the CMT start and stop functions. The path for clock sources need not change, while the one for clock events need to be reworked in future work. There is no indented functional change, just breaking the two use-cases controlled by a flag into two distinct functions. Signed-off-by: Niklas Söderlund Signed-off-by: Daniel Lezcano Tested-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20250910142657.1148696-2-niklas.soderlund+renesas@ragnatech.se --- drivers/clocksource/sh_cmt.c | 92 ++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index b72b36e0abed..385eb94bbe7c 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -578,37 +578,33 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int sh_cmt_start(struct sh_cmt_channel *ch, unsigned long flag) +static int sh_cmt_start_clocksource(struct sh_cmt_channel *ch) { int ret = 0; unsigned long flags; - if (flag & FLAG_CLOCKSOURCE) - pm_runtime_get_sync(&ch->cmt->pdev->dev); + pm_runtime_get_sync(&ch->cmt->pdev->dev); raw_spin_lock_irqsave(&ch->lock, flags); - if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) { - if (flag & FLAG_CLOCKEVENT) - pm_runtime_get_sync(&ch->cmt->pdev->dev); + if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) ret = sh_cmt_enable(ch); - } if (ret) goto out; - ch->flags |= flag; + + ch->flags |= FLAG_CLOCKSOURCE; /* setup timeout if no clockevent */ - if (ch->cmt->num_channels == 1 && - flag == FLAG_CLOCKSOURCE && (!(ch->flags & FLAG_CLOCKEVENT))) + if (ch->cmt->num_channels == 1 && !(ch->flags & FLAG_CLOCKEVENT)) __sh_cmt_set_next(ch, ch->max_match_value); - out: +out: raw_spin_unlock_irqrestore(&ch->lock, flags); return ret; } -static void sh_cmt_stop(struct sh_cmt_channel *ch, unsigned long flag) +static void sh_cmt_stop_clocksource(struct sh_cmt_channel *ch) { unsigned long flags; unsigned long f; @@ -616,22 +612,60 @@ static void sh_cmt_stop(struct sh_cmt_channel *ch, unsigned long flag) raw_spin_lock_irqsave(&ch->lock, flags); f = ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE); - ch->flags &= ~flag; - if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) { + ch->flags &= ~FLAG_CLOCKSOURCE; + + if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) sh_cmt_disable(ch); - if (flag & FLAG_CLOCKEVENT) - pm_runtime_put(&ch->cmt->pdev->dev); - } - - /* adjust the timeout to maximum if only clocksource left */ - if ((flag == FLAG_CLOCKEVENT) && (ch->flags & FLAG_CLOCKSOURCE)) - __sh_cmt_set_next(ch, ch->max_match_value); raw_spin_unlock_irqrestore(&ch->lock, flags); - if (flag & FLAG_CLOCKSOURCE) + pm_runtime_put(&ch->cmt->pdev->dev); +} + +static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch) +{ + int ret = 0; + unsigned long flags; + + raw_spin_lock_irqsave(&ch->lock, flags); + + if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) { + pm_runtime_get_sync(&ch->cmt->pdev->dev); + ret = sh_cmt_enable(ch); + } + + if (ret) + goto out; + + ch->flags |= FLAG_CLOCKEVENT; + out: + raw_spin_unlock_irqrestore(&ch->lock, flags); + + return ret; +} + +static void sh_cmt_stop_clockevent(struct sh_cmt_channel *ch) +{ + unsigned long flags; + unsigned long f; + + raw_spin_lock_irqsave(&ch->lock, flags); + + f = ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE); + + ch->flags &= ~FLAG_CLOCKEVENT; + + if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) { + sh_cmt_disable(ch); pm_runtime_put(&ch->cmt->pdev->dev); + } + + /* adjust the timeout to maximum if only clocksource left */ + if (ch->flags & FLAG_CLOCKSOURCE) + __sh_cmt_set_next(ch, ch->max_match_value); + + raw_spin_unlock_irqrestore(&ch->lock, flags); } static struct sh_cmt_channel *cs_to_sh_cmt(struct clocksource *cs) @@ -672,7 +706,7 @@ static int sh_cmt_clocksource_enable(struct clocksource *cs) ch->total_cycles = 0; - ret = sh_cmt_start(ch, FLAG_CLOCKSOURCE); + ret = sh_cmt_start_clocksource(ch); if (!ret) ch->cs_enabled = true; @@ -685,7 +719,7 @@ static void sh_cmt_clocksource_disable(struct clocksource *cs) WARN_ON(!ch->cs_enabled); - sh_cmt_stop(ch, FLAG_CLOCKSOURCE); + sh_cmt_stop_clocksource(ch); ch->cs_enabled = false; } @@ -696,7 +730,7 @@ static void sh_cmt_clocksource_suspend(struct clocksource *cs) if (!ch->cs_enabled) return; - sh_cmt_stop(ch, FLAG_CLOCKSOURCE); + sh_cmt_stop_clocksource(ch); dev_pm_genpd_suspend(&ch->cmt->pdev->dev); } @@ -708,7 +742,7 @@ static void sh_cmt_clocksource_resume(struct clocksource *cs) return; dev_pm_genpd_resume(&ch->cmt->pdev->dev); - sh_cmt_start(ch, FLAG_CLOCKSOURCE); + sh_cmt_start_clocksource(ch); } static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch, @@ -740,7 +774,7 @@ static struct sh_cmt_channel *ced_to_sh_cmt(struct clock_event_device *ced) static void sh_cmt_clock_event_start(struct sh_cmt_channel *ch, int periodic) { - sh_cmt_start(ch, FLAG_CLOCKEVENT); + sh_cmt_start_clockevent(ch); if (periodic) sh_cmt_set_next(ch, ((ch->cmt->rate + HZ/2) / HZ) - 1); @@ -752,7 +786,7 @@ static int sh_cmt_clock_event_shutdown(struct clock_event_device *ced) { struct sh_cmt_channel *ch = ced_to_sh_cmt(ced); - sh_cmt_stop(ch, FLAG_CLOCKEVENT); + sh_cmt_stop_clockevent(ch); return 0; } @@ -763,7 +797,7 @@ static int sh_cmt_clock_event_set_state(struct clock_event_device *ced, /* deal with old setting first */ if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced)) - sh_cmt_stop(ch, FLAG_CLOCKEVENT); + sh_cmt_stop_clockevent(ch); dev_info(&ch->cmt->pdev->dev, "ch%u: used for %s clock events\n", ch->index, periodic ? "periodic" : "oneshot"); From 45d78cd0bf2c40e74c31f70340484e20aae45b07 Mon Sep 17 00:00:00 2001 From: SungMin Park Date: Wed, 17 Sep 2025 12:43:11 +0530 Subject: [PATCH 52/52] dt-bindings: timer: exynos4210-mct: Add compatible for ARTPEC-9 SoC Add Axis ARTPEC-9 mct compatible to the bindings documentation. The design for the timer is reused from previous Samsung SoCs like exynos4210 and ARTPEC-8. Signed-off-by: SungMin Park Signed-off-by: Ravi Patel Signed-off-by: Daniel Lezcano Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250917071311.1404-1-ravi.patel@samsung.com --- .../devicetree/bindings/timer/samsung,exynos4210-mct.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml index 10578f544581..a4b229e0e78a 100644 --- a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml +++ b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml @@ -26,6 +26,7 @@ properties: - items: - enum: - axis,artpec8-mct + - axis,artpec9-mct - google,gs101-mct - samsung,exynos2200-mct-peris - samsung,exynos3250-mct @@ -131,6 +132,7 @@ allOf: contains: enum: - axis,artpec8-mct + - axis,artpec9-mct - google,gs101-mct - samsung,exynos2200-mct-peris - samsung,exynos5260-mct