Skip to content

Commit d1780dc

Browse files
Hao-Wen Tingdlezcano
authored andcommitted
clocksource/drivers: Add Realtek system timer driver
Add a system timer driver for Realtek SoCs. This driver registers the 1 MHz global hardware counter on Realtek platforms as a clock event device. Since this hardware counter starts counting automatically after SoC power-on, no clock initialization is required. Because the counter does not stop or get affected by CPU power down, and it supports oneshot mode, it is typically used as a tick broadcast timer. Signed-off-by: Hao-Wen Ting <haowen.ting@realtek.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Thomas Gleixner <tglx@linutronix.de> Link: https://patch.msgid.link/20251126060110.198330-3-haowen.ting@realtek.com
1 parent 40caba2 commit d1780dc

File tree

4 files changed

+167
-0
lines changed

4 files changed

+167
-0
lines changed

MAINTAINERS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21669,6 +21669,11 @@ S: Maintained
2166921669
F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
2167021670
F: drivers/spi/spi-realtek-rtl-snand.c
2167121671

21672+
REALTEK SYSTIMER DRIVER
21673+
M: Hao-Wen Ting <haowen.ting@realtek.com>
21674+
S: Maintained
21675+
F: drivers/clocksource/timer-realtek.c
21676+
2167221677
REALTEK WIRELESS DRIVER (rtlwifi family)
2167321678
M: Ping-Ke Shih <pkshih@realtek.com>
2167421679
L: linux-wireless@vger.kernel.org

drivers/clocksource/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,4 +782,15 @@ config NXP_STM_TIMER
782782
Enables the support for NXP System Timer Module found in the
783783
s32g NXP platform series.
784784

785+
config RTK_SYSTIMER
786+
bool "Realtek SYSTIMER support"
787+
depends on ARM || ARM64
788+
depends on ARCH_REALTEK || COMPILE_TEST
789+
select TIMER_OF
790+
help
791+
This option enables the driver that registers the global 1 MHz hardware
792+
counter as a clock event device on Realtek SoCs. Make sure to enable
793+
this option only when building for a Realtek platform or for compilation
794+
testing.
795+
785796
endmenu

drivers/clocksource/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,4 @@ obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o
9595
obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o
9696
obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o
9797
obj-$(CONFIG_NXP_STM_TIMER) += timer-nxp-stm.o
98+
obj-$(CONFIG_RTK_SYSTIMER) += timer-realtek.o
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (c) 2025 Realtek Semiconductor Corp.
4+
*/
5+
6+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7+
8+
#include <linux/irqflags.h>
9+
#include <linux/interrupt.h>
10+
#include "timer-of.h"
11+
12+
#define ENBL 1
13+
#define DSBL 0
14+
15+
#define SYSTIMER_RATE 1000000
16+
#define SYSTIMER_MIN_DELTA 0x64
17+
#define SYSTIMER_MAX_DELTA ULONG_MAX
18+
19+
/* SYSTIMER Register Offset (RTK Internal Use) */
20+
#define TS_LW_OFST 0x0
21+
#define TS_HW_OFST 0x4
22+
#define TS_CMP_VAL_LW_OFST 0x8
23+
#define TS_CMP_VAL_HW_OFST 0xC
24+
#define TS_CMP_CTRL_OFST 0x10
25+
#define TS_CMP_STAT_OFST 0x14
26+
27+
/* SYSTIMER CMP CTRL REG Mask */
28+
#define TS_CMP_EN_MASK 0x1
29+
#define TS_WR_EN0_MASK 0x2
30+
31+
static void __iomem *systimer_base;
32+
33+
static u64 rtk_ts64_read(void)
34+
{
35+
u32 low, high;
36+
u64 ts;
37+
38+
/* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */
39+
low = readl(systimer_base + TS_LW_OFST);
40+
high = readl(systimer_base + TS_HW_OFST);
41+
ts = ((u64)high << 32) | low;
42+
43+
return ts;
44+
}
45+
46+
static void rtk_cmp_value_write(u64 value)
47+
{
48+
u32 high, low;
49+
50+
low = value & 0xFFFFFFFF;
51+
high = value >> 32;
52+
53+
writel(high, systimer_base + TS_CMP_VAL_HW_OFST);
54+
writel(low, systimer_base + TS_CMP_VAL_LW_OFST);
55+
}
56+
57+
static inline void rtk_cmp_en_write(bool cmp_en)
58+
{
59+
u32 val;
60+
61+
val = TS_WR_EN0_MASK;
62+
if (cmp_en == ENBL)
63+
val |= TS_CMP_EN_MASK;
64+
65+
writel(val, systimer_base + TS_CMP_CTRL_OFST);
66+
}
67+
68+
static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt)
69+
{
70+
u64 cmp_val;
71+
72+
rtk_cmp_en_write(DSBL);
73+
cmp_val = rtk_ts64_read();
74+
75+
/* Set CMP value to current timestamp plus delta_us */
76+
rtk_cmp_value_write(cmp_val + cycles);
77+
rtk_cmp_en_write(ENBL);
78+
return 0;
79+
}
80+
81+
static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id)
82+
{
83+
struct clock_event_device *clkevt = dev_id;
84+
void __iomem *reg_base;
85+
u32 val;
86+
87+
/* Disable TS CMP Match */
88+
rtk_cmp_en_write(DSBL);
89+
90+
/* Clear TS CMP INTR */
91+
reg_base = systimer_base + TS_CMP_STAT_OFST;
92+
val = readl(reg_base) & TS_CMP_EN_MASK;
93+
writel(val | TS_CMP_EN_MASK, reg_base);
94+
clkevt->event_handler(clkevt);
95+
96+
return IRQ_HANDLED;
97+
}
98+
99+
static int rtk_syst_shutdown(struct clock_event_device *clkevt)
100+
{
101+
void __iomem *reg_base;
102+
u64 cmp_val = 0;
103+
104+
/* Disable TS CMP Match */
105+
rtk_cmp_en_write(DSBL);
106+
/* Set compare value to 0 */
107+
rtk_cmp_value_write(cmp_val);
108+
109+
/* Clear TS CMP INTR */
110+
reg_base = systimer_base + TS_CMP_STAT_OFST;
111+
writel(TS_CMP_EN_MASK, reg_base);
112+
return 0;
113+
}
114+
115+
static struct timer_of rtk_timer_to = {
116+
.flags = TIMER_OF_IRQ | TIMER_OF_BASE,
117+
118+
.clkevt = {
119+
.name = "rtk-clkevt",
120+
.rating = 300,
121+
.cpumask = cpu_possible_mask,
122+
.features = CLOCK_EVT_FEAT_DYNIRQ |
123+
CLOCK_EVT_FEAT_ONESHOT,
124+
.set_next_event = rtk_syst_clkevt_next_event,
125+
.set_state_oneshot = rtk_syst_shutdown,
126+
.set_state_shutdown = rtk_syst_shutdown,
127+
},
128+
129+
.of_irq = {
130+
.flags = IRQF_TIMER | IRQF_IRQPOLL,
131+
.handler = rtk_ts_match_intr_handler,
132+
},
133+
};
134+
135+
static int __init rtk_systimer_init(struct device_node *node)
136+
{
137+
int ret;
138+
139+
ret = timer_of_init(node, &rtk_timer_to);
140+
if (ret)
141+
return ret;
142+
143+
systimer_base = timer_of_base(&rtk_timer_to);
144+
clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE,
145+
SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA);
146+
147+
return 0;
148+
}
149+
150+
TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);

0 commit comments

Comments
 (0)