root / hw / timer / imx_epit.c @ 6a1751b7
History | View | Annotate | Download (10 kB)
1 |
/*
|
---|---|
2 |
* IMX EPIT Timer
|
3 |
*
|
4 |
* Copyright (c) 2008 OK Labs
|
5 |
* Copyright (c) 2011 NICTA Pty Ltd
|
6 |
* Originally written by Hans Jiang
|
7 |
* Updated by Peter Chubb
|
8 |
* Updated by Jean-Christophe Dubois
|
9 |
*
|
10 |
* This code is licensed under GPL version 2 or later. See
|
11 |
* the COPYING file in the top-level directory.
|
12 |
*
|
13 |
*/
|
14 |
|
15 |
#include "hw/hw.h" |
16 |
#include "qemu/bitops.h" |
17 |
#include "qemu/timer.h" |
18 |
#include "hw/ptimer.h" |
19 |
#include "hw/sysbus.h" |
20 |
#include "hw/arm/imx.h" |
21 |
#include "qemu/main-loop.h" |
22 |
|
23 |
#define TYPE_IMX_EPIT "imx.epit" |
24 |
|
25 |
#define DEBUG_TIMER 0 |
26 |
#if DEBUG_TIMER
|
27 |
|
28 |
static char const *imx_epit_reg_name(uint32_t reg) |
29 |
{ |
30 |
switch (reg) {
|
31 |
case 0: |
32 |
return "CR"; |
33 |
case 1: |
34 |
return "SR"; |
35 |
case 2: |
36 |
return "LR"; |
37 |
case 3: |
38 |
return "CMP"; |
39 |
case 4: |
40 |
return "CNT"; |
41 |
default:
|
42 |
return "[?]"; |
43 |
} |
44 |
} |
45 |
|
46 |
# define DPRINTF(fmt, args...) \
|
47 |
do { fprintf(stderr, "%s: " fmt , __func__, ##args); } while (0) |
48 |
#else
|
49 |
# define DPRINTF(fmt, args...) do {} while (0) |
50 |
#endif
|
51 |
|
52 |
/*
|
53 |
* Define to 1 for messages about attempts to
|
54 |
* access unimplemented registers or similar.
|
55 |
*/
|
56 |
#define DEBUG_IMPLEMENTATION 1 |
57 |
#if DEBUG_IMPLEMENTATION
|
58 |
# define IPRINTF(fmt, args...) \
|
59 |
do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0) |
60 |
#else
|
61 |
# define IPRINTF(fmt, args...) do {} while (0) |
62 |
#endif
|
63 |
|
64 |
#define IMX_EPIT(obj) \
|
65 |
OBJECT_CHECK(IMXEPITState, (obj), TYPE_IMX_EPIT) |
66 |
|
67 |
/*
|
68 |
* EPIT: Enhanced periodic interrupt timer
|
69 |
*/
|
70 |
|
71 |
#define CR_EN (1 << 0) |
72 |
#define CR_ENMOD (1 << 1) |
73 |
#define CR_OCIEN (1 << 2) |
74 |
#define CR_RLD (1 << 3) |
75 |
#define CR_PRESCALE_SHIFT (4) |
76 |
#define CR_PRESCALE_MASK (0xfff) |
77 |
#define CR_SWR (1 << 16) |
78 |
#define CR_IOVW (1 << 17) |
79 |
#define CR_DBGEN (1 << 18) |
80 |
#define CR_WAITEN (1 << 19) |
81 |
#define CR_DOZEN (1 << 20) |
82 |
#define CR_STOPEN (1 << 21) |
83 |
#define CR_CLKSRC_SHIFT (24) |
84 |
#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT) |
85 |
|
86 |
#define TIMER_MAX 0XFFFFFFFFUL |
87 |
|
88 |
/*
|
89 |
* Exact clock frequencies vary from board to board.
|
90 |
* These are typical.
|
91 |
*/
|
92 |
static const IMXClk imx_epit_clocks[] = { |
93 |
0, /* 00 disabled */ |
94 |
IPG, /* 01 ipg_clk, ~532MHz */
|
95 |
IPG, /* 10 ipg_clk_highfreq */
|
96 |
CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
|
97 |
}; |
98 |
|
99 |
typedef struct { |
100 |
SysBusDevice busdev; |
101 |
ptimer_state *timer_reload; |
102 |
ptimer_state *timer_cmp; |
103 |
MemoryRegion iomem; |
104 |
DeviceState *ccm; |
105 |
|
106 |
uint32_t cr; |
107 |
uint32_t sr; |
108 |
uint32_t lr; |
109 |
uint32_t cmp; |
110 |
uint32_t cnt; |
111 |
|
112 |
uint32_t freq; |
113 |
qemu_irq irq; |
114 |
} IMXEPITState; |
115 |
|
116 |
/*
|
117 |
* Update interrupt status
|
118 |
*/
|
119 |
static void imx_epit_update_int(IMXEPITState *s) |
120 |
{ |
121 |
if (s->sr && (s->cr & CR_OCIEN) && (s->cr & CR_EN)) {
|
122 |
qemu_irq_raise(s->irq); |
123 |
} else {
|
124 |
qemu_irq_lower(s->irq); |
125 |
} |
126 |
} |
127 |
|
128 |
static void imx_epit_set_freq(IMXEPITState *s) |
129 |
{ |
130 |
uint32_t clksrc; |
131 |
uint32_t prescaler; |
132 |
uint32_t freq; |
133 |
|
134 |
clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2);
|
135 |
prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12); |
136 |
|
137 |
freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler; |
138 |
|
139 |
s->freq = freq; |
140 |
|
141 |
DPRINTF("Setting ptimer frequency to %u\n", freq);
|
142 |
|
143 |
if (freq) {
|
144 |
ptimer_set_freq(s->timer_reload, freq); |
145 |
ptimer_set_freq(s->timer_cmp, freq); |
146 |
} |
147 |
} |
148 |
|
149 |
static void imx_epit_reset(DeviceState *dev) |
150 |
{ |
151 |
IMXEPITState *s = IMX_EPIT(dev); |
152 |
|
153 |
/*
|
154 |
* Soft reset doesn't touch some bits; hard reset clears them
|
155 |
*/
|
156 |
s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN); |
157 |
s->sr = 0;
|
158 |
s->lr = TIMER_MAX; |
159 |
s->cmp = 0;
|
160 |
s->cnt = 0;
|
161 |
/* stop both timers */
|
162 |
ptimer_stop(s->timer_cmp); |
163 |
ptimer_stop(s->timer_reload); |
164 |
/* compute new frequency */
|
165 |
imx_epit_set_freq(s); |
166 |
/* init both timers to TIMER_MAX */
|
167 |
ptimer_set_limit(s->timer_cmp, TIMER_MAX, 1);
|
168 |
ptimer_set_limit(s->timer_reload, TIMER_MAX, 1);
|
169 |
if (s->freq && (s->cr & CR_EN)) {
|
170 |
/* if the timer is still enabled, restart it */
|
171 |
ptimer_run(s->timer_reload, 0);
|
172 |
} |
173 |
} |
174 |
|
175 |
static uint32_t imx_epit_update_count(IMXEPITState *s)
|
176 |
{ |
177 |
s->cnt = ptimer_get_count(s->timer_reload); |
178 |
|
179 |
return s->cnt;
|
180 |
} |
181 |
|
182 |
static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) |
183 |
{ |
184 |
IMXEPITState *s = IMX_EPIT(opaque); |
185 |
uint32_t reg_value = 0;
|
186 |
uint32_t reg = offset >> 2;
|
187 |
|
188 |
switch (reg) {
|
189 |
case 0: /* Control Register */ |
190 |
reg_value = s->cr; |
191 |
break;
|
192 |
|
193 |
case 1: /* Status Register */ |
194 |
reg_value = s->sr; |
195 |
break;
|
196 |
|
197 |
case 2: /* LR - ticks*/ |
198 |
reg_value = s->lr; |
199 |
break;
|
200 |
|
201 |
case 3: /* CMP */ |
202 |
reg_value = s->cmp; |
203 |
break;
|
204 |
|
205 |
case 4: /* CNT */ |
206 |
imx_epit_update_count(s); |
207 |
reg_value = s->cnt; |
208 |
break;
|
209 |
|
210 |
default:
|
211 |
IPRINTF("Bad offset %x\n", reg);
|
212 |
break;
|
213 |
} |
214 |
|
215 |
DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(reg), reg_value);
|
216 |
|
217 |
return reg_value;
|
218 |
} |
219 |
|
220 |
static void imx_epit_reload_compare_timer(IMXEPITState *s) |
221 |
{ |
222 |
if ((s->cr & (CR_EN | CR_OCIEN)) == (CR_EN | CR_OCIEN)) {
|
223 |
/* if the compare feature is on and timers are running */
|
224 |
uint32_t tmp = imx_epit_update_count(s); |
225 |
uint64_t next; |
226 |
if (tmp > s->cmp) {
|
227 |
/* It'll fire in this round of the timer */
|
228 |
next = tmp - s->cmp; |
229 |
} else { /* catch it next time around */ |
230 |
next = tmp - s->cmp + ((s->cr & CR_RLD) ? TIMER_MAX : s->lr); |
231 |
} |
232 |
ptimer_set_count(s->timer_cmp, next); |
233 |
} |
234 |
} |
235 |
|
236 |
static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, |
237 |
unsigned size)
|
238 |
{ |
239 |
IMXEPITState *s = IMX_EPIT(opaque); |
240 |
uint32_t reg = offset >> 2;
|
241 |
uint64_t oldcr; |
242 |
|
243 |
DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(reg), (uint32_t)value);
|
244 |
|
245 |
switch (reg) {
|
246 |
case 0: /* CR */ |
247 |
|
248 |
oldcr = s->cr; |
249 |
s->cr = value & 0x03ffffff;
|
250 |
if (s->cr & CR_SWR) {
|
251 |
/* handle the reset */
|
252 |
imx_epit_reset(DEVICE(s)); |
253 |
} else {
|
254 |
imx_epit_set_freq(s); |
255 |
} |
256 |
|
257 |
if (s->freq && (s->cr & CR_EN) && !(oldcr & CR_EN)) {
|
258 |
if (s->cr & CR_ENMOD) {
|
259 |
if (s->cr & CR_RLD) {
|
260 |
ptimer_set_limit(s->timer_reload, s->lr, 1);
|
261 |
ptimer_set_limit(s->timer_cmp, s->lr, 1);
|
262 |
} else {
|
263 |
ptimer_set_limit(s->timer_reload, TIMER_MAX, 1);
|
264 |
ptimer_set_limit(s->timer_cmp, TIMER_MAX, 1);
|
265 |
} |
266 |
} |
267 |
|
268 |
imx_epit_reload_compare_timer(s); |
269 |
ptimer_run(s->timer_reload, 0);
|
270 |
if (s->cr & CR_OCIEN) {
|
271 |
ptimer_run(s->timer_cmp, 0);
|
272 |
} else {
|
273 |
ptimer_stop(s->timer_cmp); |
274 |
} |
275 |
} else if (!(s->cr & CR_EN)) { |
276 |
/* stop both timers */
|
277 |
ptimer_stop(s->timer_reload); |
278 |
ptimer_stop(s->timer_cmp); |
279 |
} else if (s->cr & CR_OCIEN) { |
280 |
if (!(oldcr & CR_OCIEN)) {
|
281 |
imx_epit_reload_compare_timer(s); |
282 |
ptimer_run(s->timer_cmp, 0);
|
283 |
} |
284 |
} else {
|
285 |
ptimer_stop(s->timer_cmp); |
286 |
} |
287 |
break;
|
288 |
|
289 |
case 1: /* SR - ACK*/ |
290 |
/* writing 1 to OCIF clear the OCIF bit */
|
291 |
if (value & 0x01) { |
292 |
s->sr = 0;
|
293 |
imx_epit_update_int(s); |
294 |
} |
295 |
break;
|
296 |
|
297 |
case 2: /* LR - set ticks */ |
298 |
s->lr = value; |
299 |
|
300 |
if (s->cr & CR_RLD) {
|
301 |
/* Also set the limit if the LRD bit is set */
|
302 |
/* If IOVW bit is set then set the timer value */
|
303 |
ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW); |
304 |
ptimer_set_limit(s->timer_cmp, s->lr, 0);
|
305 |
} else if (s->cr & CR_IOVW) { |
306 |
/* If IOVW bit is set then set the timer value */
|
307 |
ptimer_set_count(s->timer_reload, s->lr); |
308 |
} |
309 |
|
310 |
imx_epit_reload_compare_timer(s); |
311 |
break;
|
312 |
|
313 |
case 3: /* CMP */ |
314 |
s->cmp = value; |
315 |
|
316 |
imx_epit_reload_compare_timer(s); |
317 |
|
318 |
break;
|
319 |
|
320 |
default:
|
321 |
IPRINTF("Bad offset %x\n", reg);
|
322 |
|
323 |
break;
|
324 |
} |
325 |
} |
326 |
static void imx_epit_cmp(void *opaque) |
327 |
{ |
328 |
IMXEPITState *s = IMX_EPIT(opaque); |
329 |
|
330 |
DPRINTF("sr was %d\n", s->sr);
|
331 |
|
332 |
s->sr = 1;
|
333 |
imx_epit_update_int(s); |
334 |
} |
335 |
|
336 |
void imx_timerp_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm) |
337 |
{ |
338 |
IMXEPITState *pp; |
339 |
DeviceState *dev; |
340 |
|
341 |
dev = sysbus_create_simple(TYPE_IMX_EPIT, addr, irq); |
342 |
pp = IMX_EPIT(dev); |
343 |
pp->ccm = ccm; |
344 |
} |
345 |
|
346 |
static const MemoryRegionOps imx_epit_ops = { |
347 |
.read = imx_epit_read, |
348 |
.write = imx_epit_write, |
349 |
.endianness = DEVICE_NATIVE_ENDIAN, |
350 |
}; |
351 |
|
352 |
static const VMStateDescription vmstate_imx_timer_epit = { |
353 |
.name = "imx.epit",
|
354 |
.version_id = 2,
|
355 |
.minimum_version_id = 2,
|
356 |
.minimum_version_id_old = 2,
|
357 |
.fields = (VMStateField[]) { |
358 |
VMSTATE_UINT32(cr, IMXEPITState), |
359 |
VMSTATE_UINT32(sr, IMXEPITState), |
360 |
VMSTATE_UINT32(lr, IMXEPITState), |
361 |
VMSTATE_UINT32(cmp, IMXEPITState), |
362 |
VMSTATE_UINT32(cnt, IMXEPITState), |
363 |
VMSTATE_UINT32(freq, IMXEPITState), |
364 |
VMSTATE_PTIMER(timer_reload, IMXEPITState), |
365 |
VMSTATE_PTIMER(timer_cmp, IMXEPITState), |
366 |
VMSTATE_END_OF_LIST() |
367 |
} |
368 |
}; |
369 |
|
370 |
static void imx_epit_realize(DeviceState *dev, Error **errp) |
371 |
{ |
372 |
IMXEPITState *s = IMX_EPIT(dev); |
373 |
SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
374 |
QEMUBH *bh; |
375 |
|
376 |
DPRINTF("\n");
|
377 |
|
378 |
sysbus_init_irq(sbd, &s->irq); |
379 |
memory_region_init_io(&s->iomem, OBJECT(s), &imx_epit_ops, s, TYPE_IMX_EPIT, |
380 |
0x00001000);
|
381 |
sysbus_init_mmio(sbd, &s->iomem); |
382 |
|
383 |
s->timer_reload = ptimer_init(NULL);
|
384 |
|
385 |
bh = qemu_bh_new(imx_epit_cmp, s); |
386 |
s->timer_cmp = ptimer_init(bh); |
387 |
} |
388 |
|
389 |
static void imx_epit_class_init(ObjectClass *klass, void *data) |
390 |
{ |
391 |
DeviceClass *dc = DEVICE_CLASS(klass); |
392 |
|
393 |
dc->realize = imx_epit_realize; |
394 |
dc->reset = imx_epit_reset; |
395 |
dc->vmsd = &vmstate_imx_timer_epit; |
396 |
dc->desc = "i.MX periodic timer";
|
397 |
} |
398 |
|
399 |
static const TypeInfo imx_epit_info = { |
400 |
.name = TYPE_IMX_EPIT, |
401 |
.parent = TYPE_SYS_BUS_DEVICE, |
402 |
.instance_size = sizeof(IMXEPITState),
|
403 |
.class_init = imx_epit_class_init, |
404 |
}; |
405 |
|
406 |
static void imx_epit_register_types(void) |
407 |
{ |
408 |
type_register_static(&imx_epit_info); |
409 |
} |
410 |
|
411 |
type_init(imx_epit_register_types) |