commit | author | age
|
a07526
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
H |
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 |
* |
|
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 |
|
|
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 |
|
|
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 |
{ |
2f529f
|
46 |
return container_of(cs, struct dw_apb_clocksource, ummio.mmio.clksrc); |
a07526
|
47 |
} |
H |
48 |
|
|
49 |
static inline u32 apbt_readl(struct dw_apb_timer *timer, unsigned long offs) |
|
50 |
{ |
|
51 |
return readl(timer->base + offs); |
|
52 |
} |
|
53 |
|
|
54 |
static inline void apbt_writel(struct dw_apb_timer *timer, u32 val, |
|
55 |
unsigned long offs) |
|
56 |
{ |
|
57 |
writel(val, timer->base + offs); |
|
58 |
} |
|
59 |
|
|
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 |
|
|
71 |
static void apbt_disable_int(struct dw_apb_timer *timer) |
|
72 |
{ |
|
73 |
u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL); |
|
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 |
{ |
|
92 |
apbt_readl_relaxed(timer, APBTMR_N_EOI); |
|
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) { |
|
101 |
pr_info("Spurious APBT timer interrupt %d\n", irq); |
|
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 |
{ |
|
114 |
u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL); |
|
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 |
|
|
121 |
static int apbt_shutdown(struct clock_event_device *evt) |
|
122 |
{ |
|
123 |
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); |
|
124 |
u32 ctrl; |
|
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 |
{ |
|
137 |
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); |
|
138 |
u32 ctrl; |
|
139 |
|
|
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); |
|
171 |
u32 ctrl; |
|
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; |
|
202 |
} |
|
203 |
|
|
204 |
static int apbt_next_event(unsigned long delta, |
|
205 |
struct clock_event_device *evt) |
|
206 |
{ |
|
207 |
u32 ctrl; |
|
208 |
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); |
|
209 |
|
|
210 |
/* Disable timer */ |
|
211 |
ctrl = apbt_readl_relaxed(&dw_ced->timer, APBTMR_N_CONTROL); |
|
212 |
ctrl &= ~APBTMR_CONTROL_ENABLE; |
|
213 |
apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); |
|
214 |
/* write new count */ |
|
215 |
apbt_writel_relaxed(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT); |
|
216 |
ctrl |= APBTMR_CONTROL_ENABLE; |
|
217 |
apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); |
|
218 |
|
|
219 |
return 0; |
|
220 |
} |
|
221 |
|
|
222 |
/** |
|
223 |
* dw_apb_clockevent_init() - use an APB timer as a clock_event_device |
|
224 |
* |
|
225 |
* @cpu: The CPU the events will be targeted at or -1 if CPU affiliation |
|
226 |
* isn't required. |
|
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); |
|
258 |
dw_ced->ced.max_delta_ticks = 0x7fffffff; |
|
259 |
dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced); |
|
260 |
dw_ced->ced.min_delta_ticks = 5000; |
|
261 |
dw_ced->ced.cpumask = cpu < 0 ? cpu_possible_mask : cpumask_of(cpu); |
|
262 |
dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC | |
|
263 |
CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ; |
|
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; |
|
267 |
dw_ced->ced.set_state_oneshot_stopped = apbt_shutdown; |
|
268 |
dw_ced->ced.tick_resume = apbt_resume; |
|
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 |
|
|
274 |
dw_ced->eoi = apbt_eoi; |
|
275 |
err = request_irq(irq, dw_apb_clockevent_irq, |
|
276 |
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, |
|
277 |
dw_ced->ced.name, &dw_ced->ced); |
|
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 |
*/ |
|
333 |
u32 ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL); |
|
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 |
|
|
346 |
static void apbt_restart_clocksource(struct clocksource *cs) |
|
347 |
{ |
|
348 |
struct dw_apb_clocksource *dw_cs = |
|
349 |
clocksource_to_dw_apb_clocksource(cs); |
|
350 |
|
|
351 |
dw_apb_clocksource_start(dw_cs); |
|
352 |
} |
|
353 |
|
|
354 |
/** |
|
355 |
* dw_apb_clocksource_init() - use an APB timer as a clocksource. |
|
356 |
* |
|
357 |
* @rating: The rating to give the clocksource. |
|
358 |
* @name: The name for the clocksource. |
|
359 |
* @base: The I/O base for the timer registers. |
|
360 |
* @freq: The frequency that the timer counts at. |
|
361 |
* |
|
362 |
* This creates a clocksource using an APB timer but does not yet register it |
|
363 |
* with the clocksource system. This should be done with |
|
364 |
* dw_apb_clocksource_register() as the next step. |
|
365 |
*/ |
|
366 |
struct dw_apb_clocksource * |
2f529f
|
367 |
__init dw_apb_clocksource_init(unsigned rating, const char *name, void __iomem *base, |
a07526
|
368 |
unsigned long freq) |
H |
369 |
{ |
|
370 |
struct dw_apb_clocksource *dw_cs = kzalloc(sizeof(*dw_cs), GFP_KERNEL); |
|
371 |
|
|
372 |
if (!dw_cs) |
|
373 |
return NULL; |
|
374 |
|
|
375 |
dw_cs->timer.base = base; |
|
376 |
dw_cs->timer.freq = freq; |
2f529f
|
377 |
dw_cs->ummio.mmio.clksrc.name = name; |
H |
378 |
dw_cs->ummio.mmio.clksrc.rating = rating; |
|
379 |
dw_cs->ummio.mmio.clksrc.read = clocksource_mmio_readl_down; |
|
380 |
dw_cs->ummio.mmio.clksrc.mask = CLOCKSOURCE_MASK(32); |
|
381 |
dw_cs->ummio.mmio.clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; |
|
382 |
dw_cs->ummio.mmio.clksrc.resume = apbt_restart_clocksource; |
a07526
|
383 |
|
H |
384 |
return dw_cs; |
|
385 |
} |
|
386 |
|
|
387 |
/** |
|
388 |
* dw_apb_clocksource_register() - register the APB clocksource. |
|
389 |
* |
|
390 |
* @dw_cs: The clocksource to register. |
|
391 |
*/ |
2f529f
|
392 |
void __init dw_apb_clocksource_register(struct dw_apb_clocksource *dw_cs) |
a07526
|
393 |
{ |
2f529f
|
394 |
struct clocksource_mmio_regs mmr; |
H |
395 |
|
|
396 |
mmr.reg_lower = dw_cs->timer.base + APBTMR_N_CURRENT_VALUE; |
|
397 |
mmr.bits_lower = 32; |
|
398 |
mmr.reg_upper = 0; |
|
399 |
mmr.bits_upper = 0; |
|
400 |
mmr.revmap = NULL; |
|
401 |
|
|
402 |
clocksource_user_mmio_init(&dw_cs->ummio, &mmr, dw_cs->timer.freq); |
a07526
|
403 |
} |
H |
404 |
|
|
405 |
/** |
|
406 |
* dw_apb_clocksource_read() - read the current value of a clocksource. |
|
407 |
* |
|
408 |
* @dw_cs: The clocksource to read. |
|
409 |
*/ |
|
410 |
u64 dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs) |
|
411 |
{ |
|
412 |
return (u64)~apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE); |
|
413 |
} |