Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
06c3df49 JI |
2 | /* |
3 | * (C) Copyright 2009 Intel Corporation | |
4 | * Author: Jacob Pan (jacob.jun.pan@intel.com) | |
5 | * | |
6 | * Shared with ARM platforms, Jamie Iles, Picochip 2011 | |
7 | * | |
06c3df49 JI |
8 | * Support for the Synopsys DesignWare APB Timers. |
9 | */ | |
10 | #include <linux/dw_apb_timer.h> | |
11 | #include <linux/delay.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/irq.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/slab.h> | |
17 | ||
18 | #define APBT_MIN_PERIOD 4 | |
19 | #define APBT_MIN_DELTA_USEC 200 | |
20 | ||
d3d8fee4 JS |
21 | #define APBTMR_N_LOAD_COUNT 0x00 |
22 | #define APBTMR_N_CURRENT_VALUE 0x04 | |
23 | #define APBTMR_N_CONTROL 0x08 | |
24 | #define APBTMR_N_EOI 0x0c | |
25 | #define APBTMR_N_INT_STATUS 0x10 | |
26 | ||
06c3df49 JI |
27 | #define APBTMRS_INT_STATUS 0xa0 |
28 | #define APBTMRS_EOI 0xa4 | |
29 | #define APBTMRS_RAW_INT_STATUS 0xa8 | |
30 | #define APBTMRS_COMP_VERSION 0xac | |
31 | ||
32 | #define APBTMR_CONTROL_ENABLE (1 << 0) | |
33 | /* 1: periodic, 0:free running. */ | |
34 | #define APBTMR_CONTROL_MODE_PERIODIC (1 << 1) | |
35 | #define APBTMR_CONTROL_INT (1 << 2) | |
36 | ||
37 | static inline struct dw_apb_clock_event_device * | |
38 | ced_to_dw_apb_ced(struct clock_event_device *evt) | |
39 | { | |
40 | return container_of(evt, struct dw_apb_clock_event_device, ced); | |
41 | } | |
42 | ||
43 | static inline struct dw_apb_clocksource * | |
44 | clocksource_to_dw_apb_clocksource(struct clocksource *cs) | |
45 | { | |
46 | return container_of(cs, struct dw_apb_clocksource, cs); | |
47 | } | |
48 | ||
520ddad4 | 49 | static inline u32 apbt_readl(struct dw_apb_timer *timer, unsigned long offs) |
06c3df49 JI |
50 | { |
51 | return readl(timer->base + offs); | |
52 | } | |
53 | ||
520ddad4 JZ |
54 | static inline void apbt_writel(struct dw_apb_timer *timer, u32 val, |
55 | unsigned long offs) | |
06c3df49 JI |
56 | { |
57 | writel(val, timer->base + offs); | |
58 | } | |
59 | ||
39d3611f JZ |
60 | static inline u32 apbt_readl_relaxed(struct dw_apb_timer *timer, unsigned long offs) |
61 | { | |
62 | return readl_relaxed(timer->base + offs); | |
63 | } | |
64 | ||
65 | static inline void apbt_writel_relaxed(struct dw_apb_timer *timer, u32 val, | |
66 | unsigned long offs) | |
67 | { | |
68 | writel_relaxed(val, timer->base + offs); | |
69 | } | |
70 | ||
06c3df49 JI |
71 | static void apbt_disable_int(struct dw_apb_timer *timer) |
72 | { | |
9f4165dc | 73 | u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL); |
06c3df49 JI |
74 | |
75 | ctrl |= APBTMR_CONTROL_INT; | |
76 | apbt_writel(timer, ctrl, APBTMR_N_CONTROL); | |
77 | } | |
78 | ||
79 | /** | |
80 | * dw_apb_clockevent_pause() - stop the clock_event_device from running | |
81 | * | |
82 | * @dw_ced: The APB clock to stop generating events. | |
83 | */ | |
84 | void dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced) | |
85 | { | |
86 | disable_irq(dw_ced->timer.irq); | |
87 | apbt_disable_int(&dw_ced->timer); | |
88 | } | |
89 | ||
90 | static void apbt_eoi(struct dw_apb_timer *timer) | |
91 | { | |
39d3611f | 92 | apbt_readl_relaxed(timer, APBTMR_N_EOI); |
06c3df49 JI |
93 | } |
94 | ||
95 | static irqreturn_t dw_apb_clockevent_irq(int irq, void *data) | |
96 | { | |
97 | struct clock_event_device *evt = data; | |
98 | struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); | |
99 | ||
100 | if (!evt->event_handler) { | |
ac9ce6d1 | 101 | pr_info("Spurious APBT timer interrupt %d\n", irq); |
06c3df49 JI |
102 | return IRQ_NONE; |
103 | } | |
104 | ||
105 | if (dw_ced->eoi) | |
106 | dw_ced->eoi(&dw_ced->timer); | |
107 | ||
108 | evt->event_handler(evt); | |
109 | return IRQ_HANDLED; | |
110 | } | |
111 | ||
112 | static void apbt_enable_int(struct dw_apb_timer *timer) | |
113 | { | |
9f4165dc | 114 | u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL); |
06c3df49 JI |
115 | /* clear pending intr */ |
116 | apbt_readl(timer, APBTMR_N_EOI); | |
117 | ctrl &= ~APBTMR_CONTROL_INT; | |
118 | apbt_writel(timer, ctrl, APBTMR_N_CONTROL); | |
119 | } | |
120 | ||
226be92b | 121 | static int apbt_shutdown(struct clock_event_device *evt) |
06c3df49 | 122 | { |
226be92b | 123 | struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); |
9f4165dc | 124 | u32 ctrl; |
226be92b VK |
125 | |
126 | pr_debug("%s CPU %d state=shutdown\n", __func__, | |
127 | cpumask_first(evt->cpumask)); | |
128 | ||
129 | ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); | |
130 | ctrl &= ~APBTMR_CONTROL_ENABLE; | |
131 | apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); | |
132 | return 0; | |
133 | } | |
134 | ||
135 | static int apbt_set_oneshot(struct clock_event_device *evt) | |
136 | { | |
06c3df49 | 137 | struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); |
9f4165dc | 138 | u32 ctrl; |
06c3df49 | 139 | |
226be92b VK |
140 | pr_debug("%s CPU %d state=oneshot\n", __func__, |
141 | cpumask_first(evt->cpumask)); | |
142 | ||
143 | ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); | |
144 | /* | |
145 | * set free running mode, this mode will let timer reload max | |
146 | * timeout which will give time (3min on 25MHz clock) to rearm | |
147 | * the next event, therefore emulate the one-shot mode. | |
148 | */ | |
149 | ctrl &= ~APBTMR_CONTROL_ENABLE; | |
150 | ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC; | |
151 | ||
152 | apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); | |
153 | /* write again to set free running mode */ | |
154 | apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); | |
155 | ||
156 | /* | |
157 | * DW APB p. 46, load counter with all 1s before starting free | |
158 | * running mode. | |
159 | */ | |
160 | apbt_writel(&dw_ced->timer, ~0, APBTMR_N_LOAD_COUNT); | |
161 | ctrl &= ~APBTMR_CONTROL_INT; | |
162 | ctrl |= APBTMR_CONTROL_ENABLE; | |
163 | apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); | |
164 | return 0; | |
165 | } | |
166 | ||
167 | static int apbt_set_periodic(struct clock_event_device *evt) | |
168 | { | |
169 | struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); | |
170 | unsigned long period = DIV_ROUND_UP(dw_ced->timer.freq, HZ); | |
9f4165dc | 171 | u32 ctrl; |
226be92b VK |
172 | |
173 | pr_debug("%s CPU %d state=periodic\n", __func__, | |
174 | cpumask_first(evt->cpumask)); | |
175 | ||
176 | ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); | |
177 | ctrl |= APBTMR_CONTROL_MODE_PERIODIC; | |
178 | apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); | |
179 | /* | |
180 | * DW APB p. 46, have to disable timer before load counter, | |
181 | * may cause sync problem. | |
182 | */ | |
183 | ctrl &= ~APBTMR_CONTROL_ENABLE; | |
184 | apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); | |
185 | udelay(1); | |
186 | pr_debug("Setting clock period %lu for HZ %d\n", period, HZ); | |
187 | apbt_writel(&dw_ced->timer, period, APBTMR_N_LOAD_COUNT); | |
188 | ctrl |= APBTMR_CONTROL_ENABLE; | |
189 | apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); | |
190 | return 0; | |
191 | } | |
192 | ||
193 | static int apbt_resume(struct clock_event_device *evt) | |
194 | { | |
195 | struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); | |
196 | ||
197 | pr_debug("%s CPU %d state=resume\n", __func__, | |
198 | cpumask_first(evt->cpumask)); | |
199 | ||
200 | apbt_enable_int(&dw_ced->timer); | |
201 | return 0; | |
06c3df49 JI |
202 | } |
203 | ||
204 | static int apbt_next_event(unsigned long delta, | |
205 | struct clock_event_device *evt) | |
206 | { | |
9f4165dc | 207 | u32 ctrl; |
06c3df49 JI |
208 | struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); |
209 | ||
210 | /* Disable timer */ | |
39d3611f | 211 | ctrl = apbt_readl_relaxed(&dw_ced->timer, APBTMR_N_CONTROL); |
06c3df49 | 212 | ctrl &= ~APBTMR_CONTROL_ENABLE; |
39d3611f | 213 | apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); |
06c3df49 | 214 | /* write new count */ |
39d3611f | 215 | apbt_writel_relaxed(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT); |
06c3df49 | 216 | ctrl |= APBTMR_CONTROL_ENABLE; |
39d3611f | 217 | apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); |
06c3df49 JI |
218 | |
219 | return 0; | |
220 | } | |
221 | ||
222 | /** | |
223 | * dw_apb_clockevent_init() - use an APB timer as a clock_event_device | |
224 | * | |
cee43dbf SS |
225 | * @cpu: The CPU the events will be targeted at or -1 if CPU affiliation |
226 | * isn't required. | |
06c3df49 JI |
227 | * @name: The name used for the timer and the IRQ for it. |
228 | * @rating: The rating to give the timer. | |
229 | * @base: I/O base for the timer registers. | |
230 | * @irq: The interrupt number to use for the timer. | |
231 | * @freq: The frequency that the timer counts at. | |
232 | * | |
233 | * This creates a clock_event_device for using with the generic clock layer | |
234 | * but does not start and register it. This should be done with | |
235 | * dw_apb_clockevent_register() as the next step. If this is the first time | |
236 | * it has been called for a timer then the IRQ will be requested, if not it | |
237 | * just be enabled to allow CPU hotplug to avoid repeatedly requesting and | |
238 | * releasing the IRQ. | |
239 | */ | |
240 | struct dw_apb_clock_event_device * | |
241 | dw_apb_clockevent_init(int cpu, const char *name, unsigned rating, | |
242 | void __iomem *base, int irq, unsigned long freq) | |
243 | { | |
244 | struct dw_apb_clock_event_device *dw_ced = | |
245 | kzalloc(sizeof(*dw_ced), GFP_KERNEL); | |
246 | int err; | |
247 | ||
248 | if (!dw_ced) | |
249 | return NULL; | |
250 | ||
251 | dw_ced->timer.base = base; | |
252 | dw_ced->timer.irq = irq; | |
253 | dw_ced->timer.freq = freq; | |
254 | ||
255 | clockevents_calc_mult_shift(&dw_ced->ced, freq, APBT_MIN_PERIOD); | |
256 | dw_ced->ced.max_delta_ns = clockevent_delta2ns(0x7fffffff, | |
257 | &dw_ced->ced); | |
8317b53f | 258 | dw_ced->ced.max_delta_ticks = 0x7fffffff; |
06c3df49 | 259 | dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced); |
8317b53f | 260 | dw_ced->ced.min_delta_ticks = 5000; |
cee43dbf | 261 | dw_ced->ced.cpumask = cpu < 0 ? cpu_possible_mask : cpumask_of(cpu); |
8b5f0010 JZ |
262 | dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC | |
263 | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ; | |
226be92b VK |
264 | dw_ced->ced.set_state_shutdown = apbt_shutdown; |
265 | dw_ced->ced.set_state_periodic = apbt_set_periodic; | |
266 | dw_ced->ced.set_state_oneshot = apbt_set_oneshot; | |
45735326 | 267 | dw_ced->ced.set_state_oneshot_stopped = apbt_shutdown; |
226be92b | 268 | dw_ced->ced.tick_resume = apbt_resume; |
06c3df49 JI |
269 | dw_ced->ced.set_next_event = apbt_next_event; |
270 | dw_ced->ced.irq = dw_ced->timer.irq; | |
271 | dw_ced->ced.rating = rating; | |
272 | dw_ced->ced.name = name; | |
273 | ||
06c3df49 | 274 | dw_ced->eoi = apbt_eoi; |
cc2550b4 | 275 | err = request_irq(irq, dw_apb_clockevent_irq, |
276 | IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, | |
277 | dw_ced->ced.name, &dw_ced->ced); | |
06c3df49 JI |
278 | if (err) { |
279 | pr_err("failed to request timer irq\n"); | |
280 | kfree(dw_ced); | |
281 | dw_ced = NULL; | |
282 | } | |
283 | ||
284 | return dw_ced; | |
285 | } | |
286 | ||
287 | /** | |
288 | * dw_apb_clockevent_resume() - resume a clock that has been paused. | |
289 | * | |
290 | * @dw_ced: The APB clock to resume. | |
291 | */ | |
292 | void dw_apb_clockevent_resume(struct dw_apb_clock_event_device *dw_ced) | |
293 | { | |
294 | enable_irq(dw_ced->timer.irq); | |
295 | } | |
296 | ||
297 | /** | |
298 | * dw_apb_clockevent_stop() - stop the clock_event_device and release the IRQ. | |
299 | * | |
300 | * @dw_ced: The APB clock to stop generating the events. | |
301 | */ | |
302 | void dw_apb_clockevent_stop(struct dw_apb_clock_event_device *dw_ced) | |
303 | { | |
304 | free_irq(dw_ced->timer.irq, &dw_ced->ced); | |
305 | } | |
306 | ||
307 | /** | |
308 | * dw_apb_clockevent_register() - register the clock with the generic layer | |
309 | * | |
310 | * @dw_ced: The APB clock to register as a clock_event_device. | |
311 | */ | |
312 | void dw_apb_clockevent_register(struct dw_apb_clock_event_device *dw_ced) | |
313 | { | |
314 | apbt_writel(&dw_ced->timer, 0, APBTMR_N_CONTROL); | |
315 | clockevents_register_device(&dw_ced->ced); | |
316 | apbt_enable_int(&dw_ced->timer); | |
317 | } | |
318 | ||
319 | /** | |
320 | * dw_apb_clocksource_start() - start the clocksource counting. | |
321 | * | |
322 | * @dw_cs: The clocksource to start. | |
323 | * | |
324 | * This is used to start the clocksource before registration and can be used | |
325 | * to enable calibration of timers. | |
326 | */ | |
327 | void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs) | |
328 | { | |
329 | /* | |
330 | * start count down from 0xffff_ffff. this is done by toggling the | |
331 | * enable bit then load initial load count to ~0. | |
332 | */ | |
9f4165dc | 333 | u32 ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL); |
06c3df49 JI |
334 | |
335 | ctrl &= ~APBTMR_CONTROL_ENABLE; | |
336 | apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL); | |
337 | apbt_writel(&dw_cs->timer, ~0, APBTMR_N_LOAD_COUNT); | |
338 | /* enable, mask interrupt */ | |
339 | ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC; | |
340 | ctrl |= (APBTMR_CONTROL_ENABLE | APBTMR_CONTROL_INT); | |
341 | apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL); | |
342 | /* read it once to get cached counter value initialized */ | |
343 | dw_apb_clocksource_read(dw_cs); | |
344 | } | |
345 | ||
a5a1d1c2 | 346 | static u64 __apbt_read_clocksource(struct clocksource *cs) |
06c3df49 | 347 | { |
9f4165dc | 348 | u32 current_count; |
06c3df49 JI |
349 | struct dw_apb_clocksource *dw_cs = |
350 | clocksource_to_dw_apb_clocksource(cs); | |
351 | ||
39d3611f JZ |
352 | current_count = apbt_readl_relaxed(&dw_cs->timer, |
353 | APBTMR_N_CURRENT_VALUE); | |
06c3df49 | 354 | |
a5a1d1c2 | 355 | return (u64)~current_count; |
06c3df49 JI |
356 | } |
357 | ||
358 | static void apbt_restart_clocksource(struct clocksource *cs) | |
359 | { | |
360 | struct dw_apb_clocksource *dw_cs = | |
361 | clocksource_to_dw_apb_clocksource(cs); | |
362 | ||
363 | dw_apb_clocksource_start(dw_cs); | |
364 | } | |
365 | ||
366 | /** | |
367 | * dw_apb_clocksource_init() - use an APB timer as a clocksource. | |
368 | * | |
369 | * @rating: The rating to give the clocksource. | |
370 | * @name: The name for the clocksource. | |
371 | * @base: The I/O base for the timer registers. | |
372 | * @freq: The frequency that the timer counts at. | |
373 | * | |
374 | * This creates a clocksource using an APB timer but does not yet register it | |
375 | * with the clocksource system. This should be done with | |
376 | * dw_apb_clocksource_register() as the next step. | |
377 | */ | |
378 | struct dw_apb_clocksource * | |
a1330228 | 379 | dw_apb_clocksource_init(unsigned rating, const char *name, void __iomem *base, |
06c3df49 JI |
380 | unsigned long freq) |
381 | { | |
382 | struct dw_apb_clocksource *dw_cs = kzalloc(sizeof(*dw_cs), GFP_KERNEL); | |
383 | ||
384 | if (!dw_cs) | |
385 | return NULL; | |
386 | ||
387 | dw_cs->timer.base = base; | |
388 | dw_cs->timer.freq = freq; | |
389 | dw_cs->cs.name = name; | |
390 | dw_cs->cs.rating = rating; | |
391 | dw_cs->cs.read = __apbt_read_clocksource; | |
392 | dw_cs->cs.mask = CLOCKSOURCE_MASK(32); | |
393 | dw_cs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; | |
394 | dw_cs->cs.resume = apbt_restart_clocksource; | |
395 | ||
396 | return dw_cs; | |
397 | } | |
398 | ||
399 | /** | |
400 | * dw_apb_clocksource_register() - register the APB clocksource. | |
401 | * | |
402 | * @dw_cs: The clocksource to register. | |
403 | */ | |
404 | void dw_apb_clocksource_register(struct dw_apb_clocksource *dw_cs) | |
405 | { | |
406 | clocksource_register_hz(&dw_cs->cs, dw_cs->timer.freq); | |
407 | } | |
408 | ||
409 | /** | |
410 | * dw_apb_clocksource_read() - read the current value of a clocksource. | |
411 | * | |
412 | * @dw_cs: The clocksource to read. | |
413 | */ | |
a5a1d1c2 | 414 | u64 dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs) |
06c3df49 | 415 | { |
a5a1d1c2 | 416 | return (u64)~apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE); |
06c3df49 | 417 | } |