xref: /linux/drivers/clocksource/timer-realtek.c (revision 2437f798809d4420350b0118e4723024ce8d203b)
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);
151