Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
468b8c4c DL |
2 | /* |
3 | * Rockchip timer support | |
4 | * | |
5 | * Copyright (C) Daniel Lezcano <daniel.lezcano@linaro.org> | |
468b8c4c DL |
6 | */ |
7 | #include <linux/clk.h> | |
8 | #include <linux/clockchips.h> | |
9 | #include <linux/init.h> | |
10 | #include <linux/interrupt.h> | |
5e0a39d0 AK |
11 | #include <linux/sched_clock.h> |
12 | #include <linux/slab.h> | |
468b8c4c DL |
13 | #include <linux/of.h> |
14 | #include <linux/of_address.h> | |
15 | #include <linux/of_irq.h> | |
16 | ||
17 | #define TIMER_NAME "rk_timer" | |
18 | ||
a0d2216e CW |
19 | #define TIMER_LOAD_COUNT0 0x00 |
20 | #define TIMER_LOAD_COUNT1 0x04 | |
5e0a39d0 AK |
21 | #define TIMER_CURRENT_VALUE0 0x08 |
22 | #define TIMER_CURRENT_VALUE1 0x0C | |
be6af450 HT |
23 | #define TIMER_CONTROL_REG3288 0x10 |
24 | #define TIMER_CONTROL_REG3399 0x1c | |
a0d2216e | 25 | #define TIMER_INT_STATUS 0x18 |
468b8c4c | 26 | |
a0d2216e CW |
27 | #define TIMER_DISABLE 0x0 |
28 | #define TIMER_ENABLE 0x1 | |
29 | #define TIMER_MODE_FREE_RUNNING (0 << 1) | |
30 | #define TIMER_MODE_USER_DEFINED_COUNT (1 << 1) | |
31 | #define TIMER_INT_UNMASK (1 << 2) | |
468b8c4c | 32 | |
5e0a39d0 | 33 | struct rk_timer { |
468b8c4c | 34 | void __iomem *base; |
be6af450 | 35 | void __iomem *ctrl; |
5e0a39d0 AK |
36 | struct clk *clk; |
37 | struct clk *pclk; | |
468b8c4c | 38 | u32 freq; |
5e0a39d0 | 39 | int irq; |
468b8c4c DL |
40 | }; |
41 | ||
5e0a39d0 AK |
42 | struct rk_clkevt { |
43 | struct clock_event_device ce; | |
44 | struct rk_timer timer; | |
45 | }; | |
468b8c4c | 46 | |
5e0a39d0 AK |
47 | static struct rk_clkevt *rk_clkevt; |
48 | static struct rk_timer *rk_clksrc; | |
468b8c4c | 49 | |
5e0a39d0 | 50 | static inline struct rk_timer *rk_timer(struct clock_event_device *ce) |
be6af450 | 51 | { |
5e0a39d0 | 52 | return &container_of(ce, struct rk_clkevt, ce)->timer; |
be6af450 HT |
53 | } |
54 | ||
5e0a39d0 | 55 | static inline void rk_timer_disable(struct rk_timer *timer) |
468b8c4c | 56 | { |
5e0a39d0 | 57 | writel_relaxed(TIMER_DISABLE, timer->ctrl); |
468b8c4c DL |
58 | } |
59 | ||
5e0a39d0 | 60 | static inline void rk_timer_enable(struct rk_timer *timer, u32 flags) |
468b8c4c | 61 | { |
5e0a39d0 | 62 | writel_relaxed(TIMER_ENABLE | flags, timer->ctrl); |
468b8c4c DL |
63 | } |
64 | ||
65 | static void rk_timer_update_counter(unsigned long cycles, | |
5e0a39d0 | 66 | struct rk_timer *timer) |
468b8c4c | 67 | { |
5e0a39d0 AK |
68 | writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0); |
69 | writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1); | |
468b8c4c DL |
70 | } |
71 | ||
5e0a39d0 | 72 | static void rk_timer_interrupt_clear(struct rk_timer *timer) |
468b8c4c | 73 | { |
5e0a39d0 | 74 | writel_relaxed(1, timer->base + TIMER_INT_STATUS); |
468b8c4c DL |
75 | } |
76 | ||
77 | static inline int rk_timer_set_next_event(unsigned long cycles, | |
78 | struct clock_event_device *ce) | |
79 | { | |
5e0a39d0 AK |
80 | struct rk_timer *timer = rk_timer(ce); |
81 | ||
82 | rk_timer_disable(timer); | |
83 | rk_timer_update_counter(cycles, timer); | |
84 | rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT | | |
85 | TIMER_INT_UNMASK); | |
468b8c4c DL |
86 | return 0; |
87 | } | |
88 | ||
99b3fa72 | 89 | static int rk_timer_shutdown(struct clock_event_device *ce) |
468b8c4c | 90 | { |
5e0a39d0 AK |
91 | struct rk_timer *timer = rk_timer(ce); |
92 | ||
93 | rk_timer_disable(timer); | |
99b3fa72 VK |
94 | return 0; |
95 | } | |
96 | ||
97 | static int rk_timer_set_periodic(struct clock_event_device *ce) | |
98 | { | |
5e0a39d0 AK |
99 | struct rk_timer *timer = rk_timer(ce); |
100 | ||
101 | rk_timer_disable(timer); | |
102 | rk_timer_update_counter(timer->freq / HZ - 1, timer); | |
103 | rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING | TIMER_INT_UNMASK); | |
99b3fa72 | 104 | return 0; |
468b8c4c DL |
105 | } |
106 | ||
107 | static irqreturn_t rk_timer_interrupt(int irq, void *dev_id) | |
108 | { | |
109 | struct clock_event_device *ce = dev_id; | |
5e0a39d0 | 110 | struct rk_timer *timer = rk_timer(ce); |
468b8c4c | 111 | |
5e0a39d0 | 112 | rk_timer_interrupt_clear(timer); |
468b8c4c | 113 | |
99b3fa72 | 114 | if (clockevent_state_oneshot(ce)) |
5e0a39d0 | 115 | rk_timer_disable(timer); |
468b8c4c DL |
116 | |
117 | ce->event_handler(ce); | |
118 | ||
119 | return IRQ_HANDLED; | |
120 | } | |
121 | ||
5e0a39d0 AK |
122 | static u64 notrace rk_timer_sched_read(void) |
123 | { | |
124 | return ~readl_relaxed(rk_clksrc->base + TIMER_CURRENT_VALUE0); | |
125 | } | |
126 | ||
127 | static int __init | |
128 | rk_timer_probe(struct rk_timer *timer, struct device_node *np) | |
468b8c4c | 129 | { |
468b8c4c DL |
130 | struct clk *timer_clk; |
131 | struct clk *pclk; | |
8bdd5a2e | 132 | int ret = -EINVAL, irq; |
5e0a39d0 | 133 | u32 ctrl_reg = TIMER_CONTROL_REG3288; |
468b8c4c | 134 | |
5e0a39d0 AK |
135 | timer->base = of_iomap(np, 0); |
136 | if (!timer->base) { | |
468b8c4c | 137 | pr_err("Failed to get base address for '%s'\n", TIMER_NAME); |
8bdd5a2e | 138 | return -ENXIO; |
468b8c4c | 139 | } |
5e0a39d0 AK |
140 | |
141 | if (of_device_is_compatible(np, "rockchip,rk3399-timer")) | |
142 | ctrl_reg = TIMER_CONTROL_REG3399; | |
143 | ||
144 | timer->ctrl = timer->base + ctrl_reg; | |
468b8c4c DL |
145 | |
146 | pclk = of_clk_get_by_name(np, "pclk"); | |
147 | if (IS_ERR(pclk)) { | |
8bdd5a2e | 148 | ret = PTR_ERR(pclk); |
468b8c4c | 149 | pr_err("Failed to get pclk for '%s'\n", TIMER_NAME); |
522ed95c | 150 | goto out_unmap; |
468b8c4c DL |
151 | } |
152 | ||
8bdd5a2e DL |
153 | ret = clk_prepare_enable(pclk); |
154 | if (ret) { | |
468b8c4c | 155 | pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME); |
522ed95c | 156 | goto out_unmap; |
468b8c4c | 157 | } |
5e0a39d0 | 158 | timer->pclk = pclk; |
468b8c4c DL |
159 | |
160 | timer_clk = of_clk_get_by_name(np, "timer"); | |
161 | if (IS_ERR(timer_clk)) { | |
8bdd5a2e | 162 | ret = PTR_ERR(timer_clk); |
468b8c4c | 163 | pr_err("Failed to get timer clock for '%s'\n", TIMER_NAME); |
522ed95c | 164 | goto out_timer_clk; |
468b8c4c DL |
165 | } |
166 | ||
8bdd5a2e DL |
167 | ret = clk_prepare_enable(timer_clk); |
168 | if (ret) { | |
468b8c4c | 169 | pr_err("Failed to enable timer clock\n"); |
522ed95c | 170 | goto out_timer_clk; |
468b8c4c | 171 | } |
5e0a39d0 | 172 | timer->clk = timer_clk; |
468b8c4c | 173 | |
5e0a39d0 | 174 | timer->freq = clk_get_rate(timer_clk); |
468b8c4c DL |
175 | |
176 | irq = irq_of_parse_and_map(np, 0); | |
ccc42592 | 177 | if (!irq) { |
8bdd5a2e | 178 | ret = -EINVAL; |
468b8c4c | 179 | pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME); |
522ed95c | 180 | goto out_irq; |
468b8c4c | 181 | } |
5e0a39d0 AK |
182 | timer->irq = irq; |
183 | ||
184 | rk_timer_interrupt_clear(timer); | |
185 | rk_timer_disable(timer); | |
186 | return 0; | |
187 | ||
188 | out_irq: | |
189 | clk_disable_unprepare(timer_clk); | |
190 | out_timer_clk: | |
191 | clk_disable_unprepare(pclk); | |
192 | out_unmap: | |
193 | iounmap(timer->base); | |
194 | ||
195 | return ret; | |
196 | } | |
197 | ||
198 | static void __init rk_timer_cleanup(struct rk_timer *timer) | |
199 | { | |
200 | clk_disable_unprepare(timer->clk); | |
201 | clk_disable_unprepare(timer->pclk); | |
202 | iounmap(timer->base); | |
203 | } | |
204 | ||
205 | static int __init rk_clkevt_init(struct device_node *np) | |
206 | { | |
207 | struct clock_event_device *ce; | |
208 | int ret = -EINVAL; | |
209 | ||
210 | rk_clkevt = kzalloc(sizeof(struct rk_clkevt), GFP_KERNEL); | |
211 | if (!rk_clkevt) { | |
212 | ret = -ENOMEM; | |
213 | goto out; | |
214 | } | |
468b8c4c | 215 | |
5e0a39d0 AK |
216 | ret = rk_timer_probe(&rk_clkevt->timer, np); |
217 | if (ret) | |
218 | goto out_probe; | |
219 | ||
220 | ce = &rk_clkevt->ce; | |
468b8c4c | 221 | ce->name = TIMER_NAME; |
716897d9 HT |
222 | ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | |
223 | CLOCK_EVT_FEAT_DYNIRQ; | |
468b8c4c | 224 | ce->set_next_event = rk_timer_set_next_event; |
99b3fa72 VK |
225 | ce->set_state_shutdown = rk_timer_shutdown; |
226 | ce->set_state_periodic = rk_timer_set_periodic; | |
5e0a39d0 | 227 | ce->irq = rk_clkevt->timer.irq; |
716897d9 | 228 | ce->cpumask = cpu_possible_mask; |
468b8c4c DL |
229 | ce->rating = 250; |
230 | ||
5e0a39d0 AK |
231 | ret = request_irq(rk_clkevt->timer.irq, rk_timer_interrupt, IRQF_TIMER, |
232 | TIMER_NAME, ce); | |
468b8c4c | 233 | if (ret) { |
5e0a39d0 AK |
234 | pr_err("Failed to initialize '%s': %d\n", |
235 | TIMER_NAME, ret); | |
522ed95c | 236 | goto out_irq; |
468b8c4c DL |
237 | } |
238 | ||
5e0a39d0 AK |
239 | clockevents_config_and_register(&rk_clkevt->ce, |
240 | rk_clkevt->timer.freq, 1, UINT_MAX); | |
8bdd5a2e | 241 | return 0; |
522ed95c SL |
242 | |
243 | out_irq: | |
5e0a39d0 AK |
244 | rk_timer_cleanup(&rk_clkevt->timer); |
245 | out_probe: | |
246 | kfree(rk_clkevt); | |
247 | out: | |
248 | /* Leave rk_clkevt not NULL to prevent future init */ | |
249 | rk_clkevt = ERR_PTR(ret); | |
8bdd5a2e | 250 | return ret; |
468b8c4c | 251 | } |
a0d2216e | 252 | |
5e0a39d0 | 253 | static int __init rk_clksrc_init(struct device_node *np) |
be6af450 | 254 | { |
5e0a39d0 AK |
255 | int ret = -EINVAL; |
256 | ||
257 | rk_clksrc = kzalloc(sizeof(struct rk_timer), GFP_KERNEL); | |
258 | if (!rk_clksrc) { | |
259 | ret = -ENOMEM; | |
260 | goto out; | |
261 | } | |
262 | ||
263 | ret = rk_timer_probe(rk_clksrc, np); | |
264 | if (ret) | |
265 | goto out_probe; | |
266 | ||
267 | rk_timer_update_counter(UINT_MAX, rk_clksrc); | |
268 | rk_timer_enable(rk_clksrc, 0); | |
269 | ||
270 | ret = clocksource_mmio_init(rk_clksrc->base + TIMER_CURRENT_VALUE0, | |
271 | TIMER_NAME, rk_clksrc->freq, 250, 32, | |
272 | clocksource_mmio_readl_down); | |
273 | if (ret) { | |
2554828b | 274 | pr_err("Failed to register clocksource\n"); |
5e0a39d0 AK |
275 | goto out_clocksource; |
276 | } | |
277 | ||
278 | sched_clock_register(rk_timer_sched_read, 32, rk_clksrc->freq); | |
279 | return 0; | |
280 | ||
281 | out_clocksource: | |
282 | rk_timer_cleanup(rk_clksrc); | |
283 | out_probe: | |
284 | kfree(rk_clksrc); | |
285 | out: | |
286 | /* Leave rk_clksrc not NULL to prevent future init */ | |
287 | rk_clksrc = ERR_PTR(ret); | |
288 | return ret; | |
be6af450 HT |
289 | } |
290 | ||
5e0a39d0 | 291 | static int __init rk_timer_init(struct device_node *np) |
be6af450 | 292 | { |
5e0a39d0 AK |
293 | if (!rk_clkevt) |
294 | return rk_clkevt_init(np); | |
295 | ||
296 | if (!rk_clksrc) | |
297 | return rk_clksrc_init(np); | |
298 | ||
299 | pr_err("Too many timer definitions for '%s'\n", TIMER_NAME); | |
300 | return -EINVAL; | |
be6af450 HT |
301 | } |
302 | ||
17273395 DL |
303 | TIMER_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer", rk_timer_init); |
304 | TIMER_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer", rk_timer_init); |