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