- Use 64-bits for timer compensation for IoT usage where the suspend

time is much longer than what 32-bits can provide (Enlin Mu)
 
 - Add delay support on sp804 for ARM32 platforms (Stephen Eta Zhou)
 
 - Fix missing resource release on error in the probe path of in the
   ralink driver (Haotian Zhang)
 
 - Fix double deregistration on probe failure in the NXP STM driver
   (Johan Hovold)
 
 - Disable runtime PM for the Renesas SH CMT timer because it is
   incompatible with PREEMPT_RT=y (Niklas Söderlund)
 
 - Fix section mismatches in the NXP STM driver (Johan Hovold)
 
 - Preventing unbinding the NXP PIT, STM and MMIO ARM Arch timers as
   the code does not suppport bind/unbind (Johan Hovold)
 
 - Use the clocksource instead of ticks on the RDA8810PL platform
   (Enlin Mu)
 
 - Drop the unused module alias for the STM32-LP (Johan Hovold)
 
 - Add Realtek system timer driver (Hao-Wen Ting)
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEGn3N4YVz0WNVyHskqDIjiipP6E8FAmkm6t4ACgkQqDIjiipP
 6E8xqQgAlXnV3vRJmEbjd3ILECvbKMLI2haHV2eA+75P+DvbfriL+ePMHkfkOPI6
 CC5UhCSy410cQLO88tzy5+9K8Po2KnHxb+lVS2P6zzcdefL5ZWMZ9Q+CAOwSo1s9
 An1A4nUgcTB52mAR+jlz++SF1VV/fMvskMrtiTg8bSIScSc+xi4sEC3GaZR09qSG
 RODtzmVsyeoHQ1u6ziRJen8GzpX1q6vUP0eAAr+vXqTUXdCuUL8P20h2mwzxPJWH
 mFo53OuKVbTMOoY1Av7euvO1ZZ1tsHsS4NxJfD1qatq+eh1As1dxYodB4dp44qZt
 jjnVuj0QrE40VB6EnHAA4kKb6WWpow==
 =WXsR
 -----END PGP SIGNATURE-----

Merge tag 'timers-v6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/daniel.lezcano/linux into timers/clocksource

Pull clocksource/event changes from Daniel Lezcano:

    - Use 64-bits for timer compensation for IoT usage where the suspend
      time is much longer than what 32-bits can provide (Enlin Mu)

    - Add delay support on sp804 for ARM32 platforms (Stephen Eta Zhou)

    - Fix missing resource release on error in the probe path of in the
      ralink driver (Haotian Zhang)

    - Fix double deregistration on probe failure in the NXP STM driver
      (Johan Hovold)

    - Disable runtime PM for the Renesas SH CMT timer because it is
      incompatible with PREEMPT_RT=y (Niklas Söderlund)

    - Fix section mismatches in the NXP STM driver (Johan Hovold)

    - Preventing unbinding the NXP PIT, STM and MMIO ARM Arch timers as
      the code does not suppport bind/unbind (Johan Hovold)

    - Use the clocksource instead of ticks on the RDA8810PL platform
      (Enlin Mu)

    - Drop the unused module alias for the STM32-LP (Johan Hovold)

    - Add Realtek system timer driver (Hao-Wen Ting)

Link: https://lore.kernel.org/all/9303b790-28d4-4bd9-b01d-28fb05493596@linaro.org
This commit is contained in:
Thomas Gleixner 2025-11-26 15:36:52 +01:00
commit 2437f79880
14 changed files with 291 additions and 56 deletions

View File

@ -0,0 +1,47 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/realtek,rtd1625-systimer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek System Timer
maintainers:
- Hao-Wen Ting <haowen.ting@realtek.com>
description:
The Realtek SYSTIMER (System Timer) is a 64-bit global hardware counter operating
at a fixed 1MHz frequency. Thanks to its compare match interrupt capability,
the timer natively supports oneshot mode for tick broadcast functionality.
properties:
compatible:
oneOf:
- const: realtek,rtd1625-systimer
- items:
- const: realtek,rtd1635-systimer
- const: realtek,rtd1625-systimer
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
timer@89420 {
compatible = "realtek,rtd1635-systimer",
"realtek,rtd1625-systimer";
reg = <0x89420 0x18>;
interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
};

View File

@ -21669,6 +21669,11 @@ S: Maintained
F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
F: drivers/spi/spi-realtek-rtl-snand.c F: drivers/spi/spi-realtek-rtl-snand.c
REALTEK SYSTIMER DRIVER
M: Hao-Wen Ting <haowen.ting@realtek.com>
S: Maintained
F: drivers/clocksource/timer-realtek.c
REALTEK WIRELESS DRIVER (rtlwifi family) REALTEK WIRELESS DRIVER (rtlwifi family)
M: Ping-Ke Shih <pkshih@realtek.com> M: Ping-Ke Shih <pkshih@realtek.com>
L: linux-wireless@vger.kernel.org L: linux-wireless@vger.kernel.org

View File

@ -782,4 +782,15 @@ config NXP_STM_TIMER
Enables the support for NXP System Timer Module found in the Enables the support for NXP System Timer Module found in the
s32g NXP platform series. s32g NXP platform series.
config RTK_SYSTIMER
bool "Realtek SYSTIMER support"
depends on ARM || ARM64
depends on ARCH_REALTEK || COMPILE_TEST
select TIMER_OF
help
This option enables the driver that registers the global 1 MHz hardware
counter as a clock event device on Realtek SoCs. Make sure to enable
this option only when building for a Realtek platform or for compilation
testing.
endmenu endmenu

View File

@ -95,3 +95,4 @@ obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o
obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o
obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o
obj-$(CONFIG_NXP_STM_TIMER) += timer-nxp-stm.o obj-$(CONFIG_NXP_STM_TIMER) += timer-nxp-stm.o
obj-$(CONFIG_RTK_SYSTIMER) += timer-realtek.o

View File

@ -426,6 +426,7 @@ static struct platform_driver arch_timer_mmio_drv = {
.driver = { .driver = {
.name = "arch-timer-mmio", .name = "arch-timer-mmio",
.of_match_table = arch_timer_mmio_of_table, .of_match_table = arch_timer_mmio_of_table,
.suppress_bind_attrs = true,
}, },
.probe = arch_timer_mmio_probe, .probe = arch_timer_mmio_probe,
}; };
@ -434,6 +435,7 @@ builtin_platform_driver(arch_timer_mmio_drv);
static struct platform_driver arch_timer_mmio_acpi_drv = { static struct platform_driver arch_timer_mmio_acpi_drv = {
.driver = { .driver = {
.name = "gtdt-arm-mmio-timer", .name = "gtdt-arm-mmio-timer",
.suppress_bind_attrs = true,
}, },
.probe = arch_timer_mmio_probe, .probe = arch_timer_mmio_probe,
}; };

View File

@ -355,14 +355,6 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch)
dev_pm_syscore_device(&ch->cmt->pdev->dev, true); dev_pm_syscore_device(&ch->cmt->pdev->dev, true);
/* enable clock */
ret = clk_enable(ch->cmt->clk);
if (ret) {
dev_err(&ch->cmt->pdev->dev, "ch%u: cannot enable clock\n",
ch->index);
goto err0;
}
/* make sure channel is disabled */ /* make sure channel is disabled */
sh_cmt_start_stop_ch(ch, 0); sh_cmt_start_stop_ch(ch, 0);
@ -384,19 +376,12 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch)
if (ret || sh_cmt_read_cmcnt(ch)) { if (ret || sh_cmt_read_cmcnt(ch)) {
dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n", dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n",
ch->index); ch->index);
ret = -ETIMEDOUT; return -ETIMEDOUT;
goto err1;
} }
/* enable channel */ /* enable channel */
sh_cmt_start_stop_ch(ch, 1); sh_cmt_start_stop_ch(ch, 1);
return 0; return 0;
err1:
/* stop clock */
clk_disable(ch->cmt->clk);
err0:
return ret;
} }
static void sh_cmt_disable(struct sh_cmt_channel *ch) static void sh_cmt_disable(struct sh_cmt_channel *ch)
@ -407,9 +392,6 @@ static void sh_cmt_disable(struct sh_cmt_channel *ch)
/* disable interrupts in CMT block */ /* disable interrupts in CMT block */
sh_cmt_write_cmcsr(ch, 0); sh_cmt_write_cmcsr(ch, 0);
/* stop clock */
clk_disable(ch->cmt->clk);
dev_pm_syscore_device(&ch->cmt->pdev->dev, false); dev_pm_syscore_device(&ch->cmt->pdev->dev, false);
} }
@ -583,8 +565,6 @@ static int sh_cmt_start_clocksource(struct sh_cmt_channel *ch)
int ret = 0; int ret = 0;
unsigned long flags; unsigned long flags;
pm_runtime_get_sync(&ch->cmt->pdev->dev);
raw_spin_lock_irqsave(&ch->lock, flags); raw_spin_lock_irqsave(&ch->lock, flags);
if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
@ -619,8 +599,6 @@ static void sh_cmt_stop_clocksource(struct sh_cmt_channel *ch)
sh_cmt_disable(ch); sh_cmt_disable(ch);
raw_spin_unlock_irqrestore(&ch->lock, flags); raw_spin_unlock_irqrestore(&ch->lock, flags);
pm_runtime_put(&ch->cmt->pdev->dev);
} }
static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch) static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch)
@ -630,10 +608,8 @@ static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch)
raw_spin_lock_irqsave(&ch->lock, flags); raw_spin_lock_irqsave(&ch->lock, flags);
if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) { if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
pm_runtime_get_sync(&ch->cmt->pdev->dev);
ret = sh_cmt_enable(ch); ret = sh_cmt_enable(ch);
}
if (ret) if (ret)
goto out; goto out;
@ -656,10 +632,8 @@ static void sh_cmt_stop_clockevent(struct sh_cmt_channel *ch)
ch->flags &= ~FLAG_CLOCKEVENT; ch->flags &= ~FLAG_CLOCKEVENT;
if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) { if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
sh_cmt_disable(ch); sh_cmt_disable(ch);
pm_runtime_put(&ch->cmt->pdev->dev);
}
/* adjust the timeout to maximum if only clocksource left */ /* adjust the timeout to maximum if only clocksource left */
if (ch->flags & FLAG_CLOCKSOURCE) if (ch->flags & FLAG_CLOCKSOURCE)
@ -1134,8 +1108,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
mask &= ~(1 << hwidx); mask &= ~(1 << hwidx);
} }
clk_disable(cmt->clk);
platform_set_drvdata(pdev, cmt); platform_set_drvdata(pdev, cmt);
return 0; return 0;
@ -1183,8 +1155,6 @@ static int sh_cmt_probe(struct platform_device *pdev)
out: out:
if (cmt->has_clockevent || cmt->has_clocksource) if (cmt->has_clockevent || cmt->has_clocksource)
pm_runtime_irq_safe(&pdev->dev); pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
return 0; return 0;
} }

View File

@ -374,9 +374,10 @@ static struct platform_driver nxp_pit_driver = {
.driver = { .driver = {
.name = "nxp-pit", .name = "nxp-pit",
.of_match_table = pit_timer_of_match, .of_match_table = pit_timer_of_match,
.suppress_bind_attrs = true,
}, },
.probe = pit_timer_probe, .probe = pit_timer_probe,
}; };
module_platform_driver(nxp_pit_driver); builtin_platform_driver(nxp_pit_driver);
TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);

View File

@ -177,14 +177,14 @@ static void nxp_stm_clocksource_resume(struct clocksource *cs)
nxp_stm_clocksource_enable(cs); nxp_stm_clocksource_enable(cs);
} }
static void __init devm_clocksource_unregister(void *data) static void devm_clocksource_unregister(void *data)
{ {
struct stm_timer *stm_timer = data; struct stm_timer *stm_timer = data;
clocksource_unregister(&stm_timer->cs); clocksource_unregister(&stm_timer->cs);
} }
static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer *stm_timer, static int nxp_stm_clocksource_init(struct device *dev, struct stm_timer *stm_timer,
const char *name, void __iomem *base, struct clk *clk) const char *name, void __iomem *base, struct clk *clk)
{ {
int ret; int ret;
@ -208,10 +208,8 @@ static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer
return ret; return ret;
ret = devm_add_action_or_reset(dev, devm_clocksource_unregister, stm_timer); ret = devm_add_action_or_reset(dev, devm_clocksource_unregister, stm_timer);
if (ret) { if (ret)
clocksource_unregister(&stm_timer->cs);
return ret; return ret;
}
stm_sched_clock = stm_timer; stm_sched_clock = stm_timer;
@ -298,7 +296,7 @@ static void nxp_stm_clockevent_resume(struct clock_event_device *ced)
nxp_stm_module_get(stm_timer); nxp_stm_module_get(stm_timer);
} }
static int __init nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer *stm_timer, static int nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer *stm_timer,
const char *name, void __iomem *base, int irq, const char *name, void __iomem *base, int irq,
struct clk *clk, int cpu) struct clk *clk, int cpu)
{ {
@ -388,7 +386,7 @@ static irqreturn_t nxp_stm_module_interrupt(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int __init nxp_stm_timer_probe(struct platform_device *pdev) static int nxp_stm_timer_probe(struct platform_device *pdev)
{ {
struct stm_timer *stm_timer; struct stm_timer *stm_timer;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -484,14 +482,15 @@ static const struct of_device_id nxp_stm_of_match[] = {
}; };
MODULE_DEVICE_TABLE(of, nxp_stm_of_match); MODULE_DEVICE_TABLE(of, nxp_stm_of_match);
static struct platform_driver nxp_stm_probe = { static struct platform_driver nxp_stm_driver = {
.probe = nxp_stm_timer_probe, .probe = nxp_stm_timer_probe,
.driver = { .driver = {
.name = "nxp-stm", .name = "nxp-stm",
.of_match_table = nxp_stm_of_match, .of_match_table = nxp_stm_of_match,
.suppress_bind_attrs = true,
}, },
}; };
module_platform_driver(nxp_stm_probe); builtin_platform_driver(nxp_stm_driver);
MODULE_DESCRIPTION("NXP System Timer Module driver"); MODULE_DESCRIPTION("NXP System Timer Module driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -130,14 +130,15 @@ static int __init ralink_systick_init(struct device_node *np)
systick.dev.irq = irq_of_parse_and_map(np, 0); systick.dev.irq = irq_of_parse_and_map(np, 0);
if (!systick.dev.irq) { if (!systick.dev.irq) {
pr_err("%pOFn: request_irq failed", np); pr_err("%pOFn: request_irq failed", np);
return -EINVAL; ret = -EINVAL;
goto err_iounmap;
} }
ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name, ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
SYSTICK_FREQ, 301, 16, SYSTICK_FREQ, 301, 16,
clocksource_mmio_readl_up); clocksource_mmio_readl_up);
if (ret) if (ret)
return ret; goto err_free_irq;
clockevents_register_device(&systick.dev); clockevents_register_device(&systick.dev);
@ -145,6 +146,12 @@ static int __init ralink_systick_init(struct device_node *np)
np, systick.dev.mult, systick.dev.shift); np, systick.dev.mult, systick.dev.shift);
return 0; return 0;
err_free_irq:
irq_dispose_mapping(systick.dev.irq);
err_iounmap:
iounmap(systick.membase);
return ret;
} }
TIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init); TIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);

View File

@ -13,6 +13,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sched_clock.h>
#include "timer-of.h" #include "timer-of.h"
@ -153,7 +154,7 @@ static struct timer_of rda_ostimer_of = {
}, },
}; };
static u64 rda_hwtimer_read(struct clocksource *cs) static u64 rda_hwtimer_clocksource_read(void)
{ {
void __iomem *base = timer_of_base(&rda_ostimer_of); void __iomem *base = timer_of_base(&rda_ostimer_of);
u32 lo, hi; u32 lo, hi;
@ -167,6 +168,11 @@ static u64 rda_hwtimer_read(struct clocksource *cs)
return ((u64)hi << 32) | lo; return ((u64)hi << 32) | lo;
} }
static u64 rda_hwtimer_read(struct clocksource *cs)
{
return rda_hwtimer_clocksource_read();
}
static struct clocksource rda_hwtimer_clocksource = { static struct clocksource rda_hwtimer_clocksource = {
.name = "rda-timer", .name = "rda-timer",
.rating = 400, .rating = 400,
@ -185,6 +191,7 @@ static int __init rda_timer_init(struct device_node *np)
return ret; return ret;
clocksource_register_hz(&rda_hwtimer_clocksource, rate); clocksource_register_hz(&rda_hwtimer_clocksource, rate);
sched_clock_register(rda_hwtimer_clocksource_read, 64, rate);
clockevents_config_and_register(&rda_ostimer_of.clkevt, rate, clockevents_config_and_register(&rda_ostimer_of.clkevt, rate,
0x2, UINT_MAX); 0x2, UINT_MAX);

View File

@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2025 Realtek Semiconductor Corp.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/irqflags.h>
#include <linux/interrupt.h>
#include "timer-of.h"
#define ENBL 1
#define DSBL 0
#define SYSTIMER_RATE 1000000
#define SYSTIMER_MIN_DELTA 0x64
#define SYSTIMER_MAX_DELTA ULONG_MAX
/* SYSTIMER Register Offset (RTK Internal Use) */
#define TS_LW_OFST 0x0
#define TS_HW_OFST 0x4
#define TS_CMP_VAL_LW_OFST 0x8
#define TS_CMP_VAL_HW_OFST 0xC
#define TS_CMP_CTRL_OFST 0x10
#define TS_CMP_STAT_OFST 0x14
/* SYSTIMER CMP CTRL REG Mask */
#define TS_CMP_EN_MASK 0x1
#define TS_WR_EN0_MASK 0x2
static void __iomem *systimer_base;
static u64 rtk_ts64_read(void)
{
u32 low, high;
u64 ts;
/* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */
low = readl(systimer_base + TS_LW_OFST);
high = readl(systimer_base + TS_HW_OFST);
ts = ((u64)high << 32) | low;
return ts;
}
static void rtk_cmp_value_write(u64 value)
{
u32 high, low;
low = value & 0xFFFFFFFF;
high = value >> 32;
writel(high, systimer_base + TS_CMP_VAL_HW_OFST);
writel(low, systimer_base + TS_CMP_VAL_LW_OFST);
}
static inline void rtk_cmp_en_write(bool cmp_en)
{
u32 val;
val = TS_WR_EN0_MASK;
if (cmp_en == ENBL)
val |= TS_CMP_EN_MASK;
writel(val, systimer_base + TS_CMP_CTRL_OFST);
}
static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt)
{
u64 cmp_val;
rtk_cmp_en_write(DSBL);
cmp_val = rtk_ts64_read();
/* Set CMP value to current timestamp plus delta_us */
rtk_cmp_value_write(cmp_val + cycles);
rtk_cmp_en_write(ENBL);
return 0;
}
static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id)
{
struct clock_event_device *clkevt = dev_id;
void __iomem *reg_base;
u32 val;
/* Disable TS CMP Match */
rtk_cmp_en_write(DSBL);
/* Clear TS CMP INTR */
reg_base = systimer_base + TS_CMP_STAT_OFST;
val = readl(reg_base) & TS_CMP_EN_MASK;
writel(val | TS_CMP_EN_MASK, reg_base);
clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
static int rtk_syst_shutdown(struct clock_event_device *clkevt)
{
void __iomem *reg_base;
u64 cmp_val = 0;
/* Disable TS CMP Match */
rtk_cmp_en_write(DSBL);
/* Set compare value to 0 */
rtk_cmp_value_write(cmp_val);
/* Clear TS CMP INTR */
reg_base = systimer_base + TS_CMP_STAT_OFST;
writel(TS_CMP_EN_MASK, reg_base);
return 0;
}
static struct timer_of rtk_timer_to = {
.flags = TIMER_OF_IRQ | TIMER_OF_BASE,
.clkevt = {
.name = "rtk-clkevt",
.rating = 300,
.cpumask = cpu_possible_mask,
.features = CLOCK_EVT_FEAT_DYNIRQ |
CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = rtk_syst_clkevt_next_event,
.set_state_oneshot = rtk_syst_shutdown,
.set_state_shutdown = rtk_syst_shutdown,
},
.of_irq = {
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = rtk_ts_match_intr_handler,
},
};
static int __init rtk_systimer_init(struct device_node *node)
{
int ret;
ret = timer_of_init(node, &rtk_timer_to);
if (ret)
return ret;
systimer_base = timer_of_base(&rtk_timer_to);
clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE,
SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA);
return 0;
}
TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);

View File

@ -21,6 +21,10 @@
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/sched_clock.h> #include <linux/sched_clock.h>
#ifdef CONFIG_ARM
#include <linux/delay.h>
#endif
#include "timer-sp.h" #include "timer-sp.h"
/* Hisilicon 64-bit timer(a variant of ARM SP804) */ /* Hisilicon 64-bit timer(a variant of ARM SP804) */
@ -102,6 +106,23 @@ static u64 notrace sp804_read(void)
return ~readl_relaxed(sched_clkevt->value); return ~readl_relaxed(sched_clkevt->value);
} }
#ifdef CONFIG_ARM
static struct delay_timer delay;
static unsigned long sp804_read_delay_timer_read(void)
{
return sp804_read();
}
static void sp804_register_delay_timer(int freq)
{
delay.freq = freq;
delay.read_current_timer = sp804_read_delay_timer_read;
register_current_timer_delay(&delay);
}
#else
static inline void sp804_register_delay_timer(int freq) {}
#endif
static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base, static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
const char *name, const char *name,
struct clk *clk, struct clk *clk,
@ -114,6 +135,8 @@ static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
if (rate < 0) if (rate < 0)
return -EINVAL; return -EINVAL;
sp804_register_delay_timer(rate);
clkevt = sp804_clkevt_get(base); clkevt = sp804_clkevt_get(base);
writel(0, clkevt->ctrl); writel(0, clkevt->ctrl);
@ -318,6 +341,7 @@ static int __init sp804_of_init(struct device_node *np, struct sp804_timer *time
if (ret) if (ret)
goto err; goto err;
} }
initialized = true; initialized = true;
return 0; return 0;

View File

@ -30,6 +30,7 @@
#define TIMER_VALUE_SHDW_HI 0x1c #define TIMER_VALUE_SHDW_HI 0x1c
#define TIMER_VALUE_LO_MASK GENMASK(31, 0) #define TIMER_VALUE_LO_MASK GENMASK(31, 0)
#define TIMER_VALUE_HI_MASK GENMASK(31, 0)
static void sprd_timer_enable(void __iomem *base, u32 flag) static void sprd_timer_enable(void __iomem *base, u32 flag)
{ {
@ -162,15 +163,26 @@ static struct timer_of suspend_to = {
static u64 sprd_suspend_timer_read(struct clocksource *cs) static u64 sprd_suspend_timer_read(struct clocksource *cs)
{ {
return ~(u64)readl_relaxed(timer_of_base(&suspend_to) + u32 lo, hi;
TIMER_VALUE_SHDW_LO) & cs->mask;
do {
hi = readl_relaxed(timer_of_base(&suspend_to) +
TIMER_VALUE_SHDW_HI);
lo = readl_relaxed(timer_of_base(&suspend_to) +
TIMER_VALUE_SHDW_LO);
} while (hi != readl_relaxed(timer_of_base(&suspend_to) + TIMER_VALUE_SHDW_HI));
return ~(((u64)hi << 32) | lo);
} }
static int sprd_suspend_timer_enable(struct clocksource *cs) static int sprd_suspend_timer_enable(struct clocksource *cs)
{ {
sprd_timer_update_counter(timer_of_base(&suspend_to), writel_relaxed(TIMER_VALUE_LO_MASK,
TIMER_VALUE_LO_MASK); timer_of_base(&suspend_to) + TIMER_LOAD_LO);
sprd_timer_enable(timer_of_base(&suspend_to), TIMER_CTL_PERIOD_MODE); writel_relaxed(TIMER_VALUE_HI_MASK,
timer_of_base(&suspend_to) + TIMER_LOAD_HI);
sprd_timer_enable(timer_of_base(&suspend_to),
TIMER_CTL_PERIOD_MODE|TIMER_CTL_64BIT_WIDTH);
return 0; return 0;
} }
@ -186,7 +198,7 @@ static struct clocksource suspend_clocksource = {
.read = sprd_suspend_timer_read, .read = sprd_suspend_timer_read,
.enable = sprd_suspend_timer_enable, .enable = sprd_suspend_timer_enable,
.disable = sprd_suspend_timer_disable, .disable = sprd_suspend_timer_disable,
.mask = CLOCKSOURCE_MASK(32), .mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP, .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP,
}; };

View File

@ -289,5 +289,4 @@ static struct platform_driver stm32_clkevent_lp_driver = {
}; };
module_platform_driver(stm32_clkevent_lp_driver); module_platform_driver(stm32_clkevent_lp_driver);
MODULE_ALIAS("platform:stm32-lptimer-timer");
MODULE_DESCRIPTION("STMicroelectronics STM32 clockevent low power driver"); MODULE_DESCRIPTION("STMicroelectronics STM32 clockevent low power driver");