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