]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
e3887714 | 2 | /* |
0b7402dc | 3 | * linux/drivers/clocksource/timer-sp.c |
e3887714 RK |
4 | * |
5 | * Copyright (C) 1999 - 2003 ARM Limited | |
6 | * Copyright (C) 2000 Deep Blue Solutions Ltd | |
e3887714 | 7 | */ |
7ff550de | 8 | #include <linux/clk.h> |
e3887714 RK |
9 | #include <linux/clocksource.h> |
10 | #include <linux/clockchips.h> | |
7ff550de | 11 | #include <linux/err.h> |
e3887714 RK |
12 | #include <linux/interrupt.h> |
13 | #include <linux/irq.h> | |
14 | #include <linux/io.h> | |
7a0eca71 RH |
15 | #include <linux/of.h> |
16 | #include <linux/of_address.h> | |
b799cac7 | 17 | #include <linux/of_clk.h> |
7a0eca71 | 18 | #include <linux/of_irq.h> |
38ff87f7 | 19 | #include <linux/sched_clock.h> |
e3887714 | 20 | |
0b7402dc | 21 | #include "timer-sp.h" |
e3887714 | 22 | |
bd5a1936 ZL |
23 | /* Hisilicon 64-bit timer(a variant of ARM SP804) */ |
24 | #define HISI_TIMER_1_BASE 0x00 | |
25 | #define HISI_TIMER_2_BASE 0x40 | |
26 | #define HISI_TIMER_LOAD 0x00 | |
549437a4 | 27 | #define HISI_TIMER_LOAD_H 0x04 |
bd5a1936 | 28 | #define HISI_TIMER_VALUE 0x08 |
549437a4 | 29 | #define HISI_TIMER_VALUE_H 0x0c |
bd5a1936 ZL |
30 | #define HISI_TIMER_CTRL 0x10 |
31 | #define HISI_TIMER_INTCLR 0x14 | |
32 | #define HISI_TIMER_RIS 0x18 | |
33 | #define HISI_TIMER_MIS 0x1c | |
34 | #define HISI_TIMER_BGLOAD 0x20 | |
549437a4 | 35 | #define HISI_TIMER_BGLOAD_H 0x24 |
bd5a1936 | 36 | |
3c07bf0f | 37 | static struct sp804_timer arm_sp804_timer __initdata = { |
23c788cd ZL |
38 | .load = TIMER_LOAD, |
39 | .value = TIMER_VALUE, | |
40 | .ctrl = TIMER_CTRL, | |
41 | .intclr = TIMER_INTCLR, | |
42 | .timer_base = {TIMER_1_BASE, TIMER_2_BASE}, | |
43 | .width = 32, | |
44 | }; | |
45 | ||
3c07bf0f | 46 | static struct sp804_timer hisi_sp804_timer __initdata = { |
bd5a1936 | 47 | .load = HISI_TIMER_LOAD, |
549437a4 | 48 | .load_h = HISI_TIMER_LOAD_H, |
bd5a1936 | 49 | .value = HISI_TIMER_VALUE, |
549437a4 | 50 | .value_h = HISI_TIMER_VALUE_H, |
bd5a1936 ZL |
51 | .ctrl = HISI_TIMER_CTRL, |
52 | .intclr = HISI_TIMER_INTCLR, | |
53 | .timer_base = {HISI_TIMER_1_BASE, HISI_TIMER_2_BASE}, | |
54 | .width = 64, | |
55 | }; | |
56 | ||
23c788cd ZL |
57 | static struct sp804_clkevt sp804_clkevt[NR_TIMERS]; |
58 | ||
7d19d521 | 59 | static long __init sp804_get_clock_rate(struct clk *clk, const char *name) |
7ff550de | 60 | { |
7ff550de RK |
61 | long rate; |
62 | int err; | |
63 | ||
7d19d521 KW |
64 | if (!clk) |
65 | clk = clk_get_sys("sp804", name); | |
66 | if (IS_ERR(clk)) { | |
67 | pr_err("sp804: %s clock not found: %ld\n", name, PTR_ERR(clk)); | |
68 | return PTR_ERR(clk); | |
69 | } | |
70 | ||
6f5ad963 RK |
71 | err = clk_prepare(clk); |
72 | if (err) { | |
7a0eca71 | 73 | pr_err("sp804: clock failed to prepare: %d\n", err); |
6f5ad963 RK |
74 | clk_put(clk); |
75 | return err; | |
76 | } | |
77 | ||
7ff550de RK |
78 | err = clk_enable(clk); |
79 | if (err) { | |
7a0eca71 | 80 | pr_err("sp804: clock failed to enable: %d\n", err); |
6f5ad963 | 81 | clk_unprepare(clk); |
7ff550de RK |
82 | clk_put(clk); |
83 | return err; | |
84 | } | |
85 | ||
86 | rate = clk_get_rate(clk); | |
87 | if (rate < 0) { | |
7a0eca71 | 88 | pr_err("sp804: clock failed to get rate: %ld\n", rate); |
7ff550de | 89 | clk_disable(clk); |
6f5ad963 | 90 | clk_unprepare(clk); |
7ff550de RK |
91 | clk_put(clk); |
92 | } | |
93 | ||
94 | return rate; | |
95 | } | |
96 | ||
23c788cd ZL |
97 | static struct sp804_clkevt * __init sp804_clkevt_get(void __iomem *base) |
98 | { | |
99 | int i; | |
100 | ||
101 | for (i = 0; i < NR_TIMERS; i++) { | |
102 | if (sp804_clkevt[i].base == base) | |
103 | return &sp804_clkevt[i]; | |
104 | } | |
105 | ||
106 | /* It's impossible to reach here */ | |
107 | WARN_ON(1); | |
108 | ||
109 | return NULL; | |
110 | } | |
111 | ||
112 | static struct sp804_clkevt *sched_clkevt; | |
a7bf6162 | 113 | |
9b12f3a8 | 114 | static u64 notrace sp804_read(void) |
a7bf6162 | 115 | { |
23c788cd | 116 | return ~readl_relaxed(sched_clkevt->value); |
a7bf6162 RH |
117 | } |
118 | ||
3c0a4b18 ZL |
119 | static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base, |
120 | const char *name, | |
121 | struct clk *clk, | |
122 | int use_sched_clock) | |
e3887714 | 123 | { |
7a0eca71 | 124 | long rate; |
23c788cd | 125 | struct sp804_clkevt *clkevt; |
7a0eca71 | 126 | |
7d19d521 | 127 | rate = sp804_get_clock_rate(clk, name); |
7ff550de | 128 | if (rate < 0) |
2ef2538b | 129 | return -EINVAL; |
7ff550de | 130 | |
23c788cd ZL |
131 | clkevt = sp804_clkevt_get(base); |
132 | ||
133 | writel(0, clkevt->ctrl); | |
134 | writel(0xffffffff, clkevt->load); | |
135 | writel(0xffffffff, clkevt->value); | |
549437a4 ZL |
136 | if (clkevt->width == 64) { |
137 | writel(0xffffffff, clkevt->load_h); | |
138 | writel(0xffffffff, clkevt->value_h); | |
139 | } | |
e3887714 | 140 | writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, |
23c788cd | 141 | clkevt->ctrl); |
e3887714 | 142 | |
23c788cd | 143 | clocksource_mmio_init(clkevt->value, name, |
7ff550de | 144 | rate, 200, 32, clocksource_mmio_readl_down); |
a7bf6162 RH |
145 | |
146 | if (use_sched_clock) { | |
23c788cd | 147 | sched_clkevt = clkevt; |
9b12f3a8 | 148 | sched_clock_register(sp804_read, 32, rate); |
a7bf6162 | 149 | } |
2ef2538b DL |
150 | |
151 | return 0; | |
e3887714 RK |
152 | } |
153 | ||
154 | ||
23c788cd | 155 | static struct sp804_clkevt *common_clkevt; |
e3887714 RK |
156 | |
157 | /* | |
158 | * IRQ handler for the timer | |
159 | */ | |
160 | static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id) | |
161 | { | |
162 | struct clock_event_device *evt = dev_id; | |
163 | ||
164 | /* clear the interrupt */ | |
23c788cd | 165 | writel(1, common_clkevt->intclr); |
e3887714 RK |
166 | |
167 | evt->event_handler(evt); | |
168 | ||
169 | return IRQ_HANDLED; | |
170 | } | |
171 | ||
daea7283 | 172 | static inline void timer_shutdown(struct clock_event_device *evt) |
e3887714 | 173 | { |
23c788cd | 174 | writel(0, common_clkevt->ctrl); |
daea7283 | 175 | } |
e3887714 | 176 | |
daea7283 VK |
177 | static int sp804_shutdown(struct clock_event_device *evt) |
178 | { | |
179 | timer_shutdown(evt); | |
180 | return 0; | |
181 | } | |
e3887714 | 182 | |
daea7283 VK |
183 | static int sp804_set_periodic(struct clock_event_device *evt) |
184 | { | |
185 | unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE | | |
186 | TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; | |
e3887714 | 187 | |
daea7283 | 188 | timer_shutdown(evt); |
23c788cd ZL |
189 | writel(common_clkevt->reload, common_clkevt->load); |
190 | writel(ctrl, common_clkevt->ctrl); | |
daea7283 | 191 | return 0; |
e3887714 RK |
192 | } |
193 | ||
194 | static int sp804_set_next_event(unsigned long next, | |
195 | struct clock_event_device *evt) | |
196 | { | |
daea7283 VK |
197 | unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE | |
198 | TIMER_CTRL_ONESHOT | TIMER_CTRL_ENABLE; | |
e3887714 | 199 | |
23c788cd ZL |
200 | writel(next, common_clkevt->load); |
201 | writel(ctrl, common_clkevt->ctrl); | |
e3887714 RK |
202 | |
203 | return 0; | |
204 | } | |
205 | ||
206 | static struct clock_event_device sp804_clockevent = { | |
daea7283 VK |
207 | .features = CLOCK_EVT_FEAT_PERIODIC | |
208 | CLOCK_EVT_FEAT_ONESHOT | | |
209 | CLOCK_EVT_FEAT_DYNIRQ, | |
210 | .set_state_shutdown = sp804_shutdown, | |
211 | .set_state_periodic = sp804_set_periodic, | |
212 | .set_state_oneshot = sp804_shutdown, | |
213 | .tick_resume = sp804_shutdown, | |
214 | .set_next_event = sp804_set_next_event, | |
215 | .rating = 300, | |
e3887714 RK |
216 | }; |
217 | ||
3c0a4b18 ZL |
218 | static int __init sp804_clockevents_init(void __iomem *base, unsigned int irq, |
219 | struct clk *clk, const char *name) | |
e3887714 RK |
220 | { |
221 | struct clock_event_device *evt = &sp804_clockevent; | |
7a0eca71 RH |
222 | long rate; |
223 | ||
7d19d521 | 224 | rate = sp804_get_clock_rate(clk, name); |
23828a7a | 225 | if (rate < 0) |
2ef2538b | 226 | return -EINVAL; |
e3887714 | 227 | |
23c788cd ZL |
228 | common_clkevt = sp804_clkevt_get(base); |
229 | common_clkevt->reload = DIV_ROUND_CLOSEST(rate, HZ); | |
57cc4f7d RK |
230 | evt->name = name; |
231 | evt->irq = irq; | |
ea3aacf5 | 232 | evt->cpumask = cpu_possible_mask; |
e3887714 | 233 | |
23c788cd | 234 | writel(0, common_clkevt->ctrl); |
7a0eca71 | 235 | |
cc2550b4 | 236 | if (request_irq(irq, sp804_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, |
237 | "timer", &sp804_clockevent)) | |
238 | pr_err("%s: request_irq() failed\n", "timer"); | |
7c324d83 | 239 | clockevents_config_and_register(evt, rate, 0xf, 0xffffffff); |
2ef2538b DL |
240 | |
241 | return 0; | |
e3887714 | 242 | } |
7a0eca71 | 243 | |
23c788cd ZL |
244 | static void __init sp804_clkevt_init(struct sp804_timer *timer, void __iomem *base) |
245 | { | |
246 | int i; | |
247 | ||
248 | for (i = 0; i < NR_TIMERS; i++) { | |
249 | void __iomem *timer_base; | |
250 | struct sp804_clkevt *clkevt; | |
251 | ||
252 | timer_base = base + timer->timer_base[i]; | |
253 | clkevt = &sp804_clkevt[i]; | |
254 | clkevt->base = timer_base; | |
255 | clkevt->load = timer_base + timer->load; | |
549437a4 | 256 | clkevt->load_h = timer_base + timer->load_h; |
23c788cd | 257 | clkevt->value = timer_base + timer->value; |
549437a4 | 258 | clkevt->value_h = timer_base + timer->value_h; |
23c788cd ZL |
259 | clkevt->ctrl = timer_base + timer->ctrl; |
260 | clkevt->intclr = timer_base + timer->intclr; | |
261 | clkevt->width = timer->width; | |
262 | } | |
263 | } | |
264 | ||
265 | static int __init sp804_of_init(struct device_node *np, struct sp804_timer *timer) | |
7a0eca71 RH |
266 | { |
267 | static bool initialized = false; | |
268 | void __iomem *base; | |
e69aae71 ZL |
269 | void __iomem *timer1_base; |
270 | void __iomem *timer2_base; | |
2ef2538b | 271 | int irq, ret = -EINVAL; |
7a0eca71 RH |
272 | u32 irq_num = 0; |
273 | struct clk *clk1, *clk2; | |
274 | const char *name = of_get_property(np, "compatible", NULL); | |
275 | ||
276 | base = of_iomap(np, 0); | |
2ef2538b DL |
277 | if (!base) |
278 | return -ENXIO; | |
7a0eca71 | 279 | |
23c788cd ZL |
280 | timer1_base = base + timer->timer_base[0]; |
281 | timer2_base = base + timer->timer_base[1]; | |
e69aae71 | 282 | |
7a0eca71 | 283 | /* Ensure timers are disabled */ |
23c788cd ZL |
284 | writel(0, timer1_base + timer->ctrl); |
285 | writel(0, timer2_base + timer->ctrl); | |
7a0eca71 | 286 | |
2ef2538b DL |
287 | if (initialized || !of_device_is_available(np)) { |
288 | ret = -EINVAL; | |
7a0eca71 | 289 | goto err; |
2ef2538b | 290 | } |
7a0eca71 RH |
291 | |
292 | clk1 = of_clk_get(np, 0); | |
293 | if (IS_ERR(clk1)) | |
294 | clk1 = NULL; | |
295 | ||
1bde9906 | 296 | /* Get the 2nd clock if the timer has 3 timer clocks */ |
b799cac7 | 297 | if (of_clk_get_parent_count(np) == 3) { |
7a0eca71 RH |
298 | clk2 = of_clk_get(np, 1); |
299 | if (IS_ERR(clk2)) { | |
2a4849d2 | 300 | pr_err("sp804: %pOFn clock not found: %d\n", np, |
7a0eca71 | 301 | (int)PTR_ERR(clk2)); |
1bde9906 | 302 | clk2 = NULL; |
7a0eca71 RH |
303 | } |
304 | } else | |
305 | clk2 = clk1; | |
306 | ||
307 | irq = irq_of_parse_and_map(np, 0); | |
308 | if (irq <= 0) | |
309 | goto err; | |
310 | ||
23c788cd ZL |
311 | sp804_clkevt_init(timer, base); |
312 | ||
7a0eca71 RH |
313 | of_property_read_u32(np, "arm,sp804-has-irq", &irq_num); |
314 | if (irq_num == 2) { | |
2ef2538b | 315 | |
e69aae71 | 316 | ret = sp804_clockevents_init(timer2_base, irq, clk2, name); |
2ef2538b DL |
317 | if (ret) |
318 | goto err; | |
319 | ||
e69aae71 | 320 | ret = sp804_clocksource_and_sched_clock_init(timer1_base, |
975434f8 | 321 | name, clk1, 1); |
2ef2538b DL |
322 | if (ret) |
323 | goto err; | |
7a0eca71 | 324 | } else { |
2ef2538b | 325 | |
e69aae71 | 326 | ret = sp804_clockevents_init(timer1_base, irq, clk1, name); |
2ef2538b DL |
327 | if (ret) |
328 | goto err; | |
329 | ||
e69aae71 | 330 | ret = sp804_clocksource_and_sched_clock_init(timer2_base, |
975434f8 | 331 | name, clk2, 1); |
2ef2538b DL |
332 | if (ret) |
333 | goto err; | |
7a0eca71 RH |
334 | } |
335 | initialized = true; | |
336 | ||
2ef2538b | 337 | return 0; |
7a0eca71 RH |
338 | err: |
339 | iounmap(base); | |
2ef2538b | 340 | return ret; |
7a0eca71 | 341 | } |
23c788cd ZL |
342 | |
343 | static int __init arm_sp804_of_init(struct device_node *np) | |
344 | { | |
345 | return sp804_of_init(np, &arm_sp804_timer); | |
346 | } | |
347 | TIMER_OF_DECLARE(sp804, "arm,sp804", arm_sp804_of_init); | |
870e2928 | 348 | |
bd5a1936 ZL |
349 | static int __init hisi_sp804_of_init(struct device_node *np) |
350 | { | |
351 | return sp804_of_init(np, &hisi_sp804_timer); | |
352 | } | |
353 | TIMER_OF_DECLARE(hisi_sp804, "hisilicon,sp804", hisi_sp804_of_init); | |
354 | ||
2ef2538b | 355 | static int __init integrator_cp_of_init(struct device_node *np) |
870e2928 RH |
356 | { |
357 | static int init_count = 0; | |
358 | void __iomem *base; | |
2ef2538b | 359 | int irq, ret = -EINVAL; |
870e2928 | 360 | const char *name = of_get_property(np, "compatible", NULL); |
9cf31380 | 361 | struct clk *clk; |
870e2928 RH |
362 | |
363 | base = of_iomap(np, 0); | |
2ef2538b | 364 | if (!base) { |
ac9ce6d1 | 365 | pr_err("Failed to iomap\n"); |
2ef2538b DL |
366 | return -ENXIO; |
367 | } | |
368 | ||
9cf31380 | 369 | clk = of_clk_get(np, 0); |
2ef2538b | 370 | if (IS_ERR(clk)) { |
ac9ce6d1 | 371 | pr_err("Failed to get clock\n"); |
2ef2538b DL |
372 | return PTR_ERR(clk); |
373 | } | |
870e2928 RH |
374 | |
375 | /* Ensure timer is disabled */ | |
23c788cd | 376 | writel(0, base + arm_sp804_timer.ctrl); |
870e2928 RH |
377 | |
378 | if (init_count == 2 || !of_device_is_available(np)) | |
379 | goto err; | |
380 | ||
23c788cd ZL |
381 | sp804_clkevt_init(&arm_sp804_timer, base); |
382 | ||
2ef2538b | 383 | if (!init_count) { |
975434f8 ZL |
384 | ret = sp804_clocksource_and_sched_clock_init(base, |
385 | name, clk, 0); | |
2ef2538b DL |
386 | if (ret) |
387 | goto err; | |
388 | } else { | |
870e2928 RH |
389 | irq = irq_of_parse_and_map(np, 0); |
390 | if (irq <= 0) | |
391 | goto err; | |
392 | ||
975434f8 | 393 | ret = sp804_clockevents_init(base, irq, clk, name); |
2ef2538b DL |
394 | if (ret) |
395 | goto err; | |
870e2928 RH |
396 | } |
397 | ||
398 | init_count++; | |
2ef2538b | 399 | return 0; |
870e2928 RH |
400 | err: |
401 | iounmap(base); | |
2ef2538b | 402 | return ret; |
870e2928 | 403 | } |
17273395 | 404 | TIMER_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init); |