]>
Commit | Line | Data |
---|---|---|
78d1404d | 1 | /* |
a50c0d6f | 2 | * IMX GPT Timer |
78d1404d PC |
3 | * |
4 | * Copyright (c) 2008 OK Labs | |
5 | * Copyright (c) 2011 NICTA Pty Ltd | |
aade7b91 | 6 | * Originally written by Hans Jiang |
78d1404d | 7 | * Updated by Peter Chubb |
d647b26d | 8 | * Updated by Jean-Christophe Dubois <[email protected]> |
78d1404d | 9 | * |
aade7b91 | 10 | * This code is licensed under GPL version 2 or later. See |
78d1404d PC |
11 | * the COPYING file in the top-level directory. |
12 | * | |
13 | */ | |
14 | ||
d647b26d JCD |
15 | #include "hw/timer/imx_gpt.h" |
16 | #include "hw/misc/imx_ccm.h" | |
6a1751b7 | 17 | #include "qemu/main-loop.h" |
78d1404d | 18 | |
5ec694b5 JCD |
19 | /* |
20 | * Define to 1 for debug messages | |
21 | */ | |
22 | #define DEBUG_TIMER 0 | |
23 | #if DEBUG_TIMER | |
24 | ||
67110c3e | 25 | static char const *imx_gpt_reg_name(uint32_t reg) |
5ec694b5 JCD |
26 | { |
27 | switch (reg) { | |
28 | case 0: | |
29 | return "CR"; | |
30 | case 1: | |
31 | return "PR"; | |
32 | case 2: | |
33 | return "SR"; | |
34 | case 3: | |
35 | return "IR"; | |
36 | case 4: | |
37 | return "OCR1"; | |
38 | case 5: | |
39 | return "OCR2"; | |
40 | case 6: | |
41 | return "OCR3"; | |
42 | case 7: | |
43 | return "ICR1"; | |
44 | case 8: | |
45 | return "ICR2"; | |
46 | case 9: | |
47 | return "CNT"; | |
48 | default: | |
49 | return "[?]"; | |
50 | } | |
51 | } | |
52 | ||
78d1404d | 53 | # define DPRINTF(fmt, args...) \ |
5ec694b5 | 54 | do { printf("%s: " fmt , __func__, ##args); } while (0) |
78d1404d PC |
55 | #else |
56 | # define DPRINTF(fmt, args...) do {} while (0) | |
57 | #endif | |
58 | ||
59 | /* | |
60 | * Define to 1 for messages about attempts to | |
61 | * access unimplemented registers or similar. | |
62 | */ | |
63 | #define DEBUG_IMPLEMENTATION 1 | |
64 | #if DEBUG_IMPLEMENTATION | |
67110c3e | 65 | # define IPRINTF(fmt, args...) \ |
5ec694b5 | 66 | do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0) |
78d1404d PC |
67 | #else |
68 | # define IPRINTF(fmt, args...) do {} while (0) | |
69 | #endif | |
70 | ||
67110c3e | 71 | static const VMStateDescription vmstate_imx_timer_gpt = { |
68b85290 | 72 | .name = TYPE_IMX_GPT, |
5ec694b5 JCD |
73 | .version_id = 3, |
74 | .minimum_version_id = 3, | |
8f1e884b | 75 | .fields = (VMStateField[]) { |
67110c3e JCD |
76 | VMSTATE_UINT32(cr, IMXGPTState), |
77 | VMSTATE_UINT32(pr, IMXGPTState), | |
78 | VMSTATE_UINT32(sr, IMXGPTState), | |
79 | VMSTATE_UINT32(ir, IMXGPTState), | |
80 | VMSTATE_UINT32(ocr1, IMXGPTState), | |
81 | VMSTATE_UINT32(ocr2, IMXGPTState), | |
82 | VMSTATE_UINT32(ocr3, IMXGPTState), | |
83 | VMSTATE_UINT32(icr1, IMXGPTState), | |
84 | VMSTATE_UINT32(icr2, IMXGPTState), | |
85 | VMSTATE_UINT32(cnt, IMXGPTState), | |
86 | VMSTATE_UINT32(next_timeout, IMXGPTState), | |
87 | VMSTATE_UINT32(next_int, IMXGPTState), | |
88 | VMSTATE_UINT32(freq, IMXGPTState), | |
89 | VMSTATE_PTIMER(timer, IMXGPTState), | |
78d1404d PC |
90 | VMSTATE_END_OF_LIST() |
91 | } | |
92 | }; | |
93 | ||
67110c3e | 94 | static const IMXClk imx_gpt_clocks[] = { |
78d1404d PC |
95 | NOCLK, /* 000 No clock source */ |
96 | IPG, /* 001 ipg_clk, 532MHz*/ | |
97 | IPG, /* 010 ipg_clk_highfreq */ | |
98 | NOCLK, /* 011 not defined */ | |
99 | CLK_32k, /* 100 ipg_clk_32k */ | |
100 | NOCLK, /* 101 not defined */ | |
101 | NOCLK, /* 110 not defined */ | |
102 | NOCLK, /* 111 not defined */ | |
103 | }; | |
104 | ||
67110c3e | 105 | static void imx_gpt_set_freq(IMXGPTState *s) |
78d1404d | 106 | { |
5ec694b5 | 107 | uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3); |
67110c3e | 108 | uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc]) |
68b85290 | 109 | / (1 + s->pr); |
5ec694b5 | 110 | s->freq = freq; |
78d1404d | 111 | |
5ec694b5 | 112 | DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq); |
a50c0d6f | 113 | |
78d1404d PC |
114 | if (freq) { |
115 | ptimer_set_freq(s->timer, freq); | |
116 | } | |
117 | } | |
118 | ||
67110c3e | 119 | static void imx_gpt_update_int(IMXGPTState *s) |
78d1404d | 120 | { |
5ec694b5 JCD |
121 | if ((s->sr & s->ir) && (s->cr & GPT_CR_EN)) { |
122 | qemu_irq_raise(s->irq); | |
123 | } else { | |
124 | qemu_irq_lower(s->irq); | |
125 | } | |
78d1404d PC |
126 | } |
127 | ||
67110c3e | 128 | static uint32_t imx_gpt_update_count(IMXGPTState *s) |
78d1404d | 129 | { |
5ec694b5 JCD |
130 | s->cnt = s->next_timeout - (uint32_t)ptimer_get_count(s->timer); |
131 | ||
78d1404d PC |
132 | return s->cnt; |
133 | } | |
134 | ||
67110c3e | 135 | static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg, |
68b85290 | 136 | uint32_t timeout) |
78d1404d | 137 | { |
5ec694b5 JCD |
138 | if ((count < reg) && (timeout > reg)) { |
139 | timeout = reg; | |
140 | } | |
141 | ||
142 | return timeout; | |
143 | } | |
78d1404d | 144 | |
67110c3e | 145 | static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event) |
5ec694b5 | 146 | { |
203d65a4 | 147 | uint32_t timeout = GPT_TIMER_MAX; |
5ec694b5 JCD |
148 | uint32_t count = 0; |
149 | long long limit; | |
150 | ||
151 | if (!(s->cr & GPT_CR_EN)) { | |
152 | /* if not enabled just return */ | |
78d1404d PC |
153 | return; |
154 | } | |
155 | ||
5ec694b5 JCD |
156 | if (event) { |
157 | /* This is a timer event */ | |
158 | ||
203d65a4 | 159 | if ((s->cr & GPT_CR_FRR) && (s->next_timeout != GPT_TIMER_MAX)) { |
5ec694b5 JCD |
160 | /* |
161 | * if we are in free running mode and we have not reached | |
203d65a4 | 162 | * the GPT_TIMER_MAX limit, then update the count |
5ec694b5 | 163 | */ |
67110c3e | 164 | count = imx_gpt_update_count(s); |
5ec694b5 | 165 | } |
78d1404d | 166 | } else { |
5ec694b5 JCD |
167 | /* not a timer event, then just update the count */ |
168 | ||
67110c3e | 169 | count = imx_gpt_update_count(s); |
5ec694b5 JCD |
170 | } |
171 | ||
172 | /* now, find the next timeout related to count */ | |
173 | ||
174 | if (s->ir & GPT_IR_OF1IE) { | |
67110c3e | 175 | timeout = imx_gpt_find_limit(count, s->ocr1, timeout); |
5ec694b5 JCD |
176 | } |
177 | if (s->ir & GPT_IR_OF2IE) { | |
67110c3e | 178 | timeout = imx_gpt_find_limit(count, s->ocr2, timeout); |
5ec694b5 JCD |
179 | } |
180 | if (s->ir & GPT_IR_OF3IE) { | |
67110c3e | 181 | timeout = imx_gpt_find_limit(count, s->ocr3, timeout); |
5ec694b5 JCD |
182 | } |
183 | ||
184 | /* find the next set of interrupts to raise for next timer event */ | |
185 | ||
186 | s->next_int = 0; | |
187 | if ((s->ir & GPT_IR_OF1IE) && (timeout == s->ocr1)) { | |
188 | s->next_int |= GPT_SR_OF1; | |
189 | } | |
190 | if ((s->ir & GPT_IR_OF2IE) && (timeout == s->ocr2)) { | |
191 | s->next_int |= GPT_SR_OF2; | |
192 | } | |
193 | if ((s->ir & GPT_IR_OF3IE) && (timeout == s->ocr3)) { | |
194 | s->next_int |= GPT_SR_OF3; | |
195 | } | |
203d65a4 | 196 | if ((s->ir & GPT_IR_ROVIE) && (timeout == GPT_TIMER_MAX)) { |
5ec694b5 JCD |
197 | s->next_int |= GPT_SR_ROV; |
198 | } | |
199 | ||
200 | /* the new range to count down from */ | |
67110c3e | 201 | limit = timeout - imx_gpt_update_count(s); |
5ec694b5 JCD |
202 | |
203 | if (limit < 0) { | |
204 | /* | |
205 | * if we reach here, then QEMU is running too slow and we pass the | |
206 | * timeout limit while computing it. Let's deliver the interrupt | |
207 | * and compute a new limit. | |
208 | */ | |
209 | s->sr |= s->next_int; | |
210 | ||
67110c3e | 211 | imx_gpt_compute_next_timeout(s, event); |
5ec694b5 | 212 | |
67110c3e | 213 | imx_gpt_update_int(s); |
5ec694b5 JCD |
214 | } else { |
215 | /* New timeout value */ | |
216 | s->next_timeout = timeout; | |
217 | ||
218 | /* reset the limit to the computed range */ | |
219 | ptimer_set_limit(s->timer, limit, 1); | |
78d1404d | 220 | } |
78d1404d PC |
221 | } |
222 | ||
67110c3e | 223 | static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) |
78d1404d | 224 | { |
67110c3e | 225 | IMXGPTState *s = IMX_GPT(opaque); |
5ec694b5 JCD |
226 | uint32_t reg_value = 0; |
227 | uint32_t reg = offset >> 2; | |
78d1404d | 228 | |
5ec694b5 | 229 | switch (reg) { |
78d1404d | 230 | case 0: /* Control Register */ |
5ec694b5 JCD |
231 | reg_value = s->cr; |
232 | break; | |
78d1404d PC |
233 | |
234 | case 1: /* prescaler */ | |
5ec694b5 JCD |
235 | reg_value = s->pr; |
236 | break; | |
78d1404d PC |
237 | |
238 | case 2: /* Status Register */ | |
5ec694b5 JCD |
239 | reg_value = s->sr; |
240 | break; | |
78d1404d PC |
241 | |
242 | case 3: /* Interrupt Register */ | |
5ec694b5 JCD |
243 | reg_value = s->ir; |
244 | break; | |
78d1404d PC |
245 | |
246 | case 4: /* Output Compare Register 1 */ | |
5ec694b5 JCD |
247 | reg_value = s->ocr1; |
248 | break; | |
78d1404d | 249 | |
462566fc | 250 | case 5: /* Output Compare Register 2 */ |
5ec694b5 JCD |
251 | reg_value = s->ocr2; |
252 | break; | |
462566fc JCD |
253 | |
254 | case 6: /* Output Compare Register 3 */ | |
5ec694b5 JCD |
255 | reg_value = s->ocr3; |
256 | break; | |
462566fc JCD |
257 | |
258 | case 7: /* input Capture Register 1 */ | |
5ec694b5 JCD |
259 | qemu_log_mask(LOG_UNIMP, "icr1 feature is not implemented\n"); |
260 | reg_value = s->icr1; | |
261 | break; | |
462566fc JCD |
262 | |
263 | case 8: /* input Capture Register 2 */ | |
5ec694b5 JCD |
264 | qemu_log_mask(LOG_UNIMP, "icr2 feature is not implemented\n"); |
265 | reg_value = s->icr2; | |
266 | break; | |
78d1404d PC |
267 | |
268 | case 9: /* cnt */ | |
67110c3e | 269 | imx_gpt_update_count(s); |
5ec694b5 JCD |
270 | reg_value = s->cnt; |
271 | break; | |
272 | ||
273 | default: | |
274 | IPRINTF("Bad offset %x\n", reg); | |
275 | break; | |
78d1404d PC |
276 | } |
277 | ||
67110c3e | 278 | DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(reg), reg_value); |
462566fc | 279 | |
5ec694b5 | 280 | return reg_value; |
78d1404d PC |
281 | } |
282 | ||
67110c3e | 283 | static void imx_gpt_reset(DeviceState *dev) |
78d1404d | 284 | { |
67110c3e | 285 | IMXGPTState *s = IMX_GPT(dev); |
78d1404d | 286 | |
5ec694b5 JCD |
287 | /* stop timer */ |
288 | ptimer_stop(s->timer); | |
289 | ||
78d1404d PC |
290 | /* |
291 | * Soft reset doesn't touch some bits; hard reset clears them | |
292 | */ | |
462566fc JCD |
293 | s->cr &= ~(GPT_CR_EN|GPT_CR_ENMOD|GPT_CR_STOPEN|GPT_CR_DOZEN| |
294 | GPT_CR_WAITEN|GPT_CR_DBGEN); | |
78d1404d PC |
295 | s->sr = 0; |
296 | s->pr = 0; | |
297 | s->ir = 0; | |
298 | s->cnt = 0; | |
203d65a4 MT |
299 | s->ocr1 = GPT_TIMER_MAX; |
300 | s->ocr2 = GPT_TIMER_MAX; | |
301 | s->ocr3 = GPT_TIMER_MAX; | |
462566fc JCD |
302 | s->icr1 = 0; |
303 | s->icr2 = 0; | |
5ec694b5 | 304 | |
203d65a4 | 305 | s->next_timeout = GPT_TIMER_MAX; |
5ec694b5 JCD |
306 | s->next_int = 0; |
307 | ||
308 | /* compute new freq */ | |
67110c3e | 309 | imx_gpt_set_freq(s); |
5ec694b5 | 310 | |
203d65a4 MT |
311 | /* reset the limit to GPT_TIMER_MAX */ |
312 | ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1); | |
5ec694b5 JCD |
313 | |
314 | /* if the timer is still enabled, restart it */ | |
315 | if (s->freq && (s->cr & GPT_CR_EN)) { | |
316 | ptimer_run(s->timer, 1); | |
317 | } | |
78d1404d PC |
318 | } |
319 | ||
67110c3e JCD |
320 | static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, |
321 | unsigned size) | |
78d1404d | 322 | { |
67110c3e | 323 | IMXGPTState *s = IMX_GPT(opaque); |
5ec694b5 JCD |
324 | uint32_t oldreg; |
325 | uint32_t reg = offset >> 2; | |
326 | ||
67110c3e | 327 | DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(reg), |
5ec694b5 JCD |
328 | (uint32_t)value); |
329 | ||
330 | switch (reg) { | |
331 | case 0: | |
332 | oldreg = s->cr; | |
333 | s->cr = value & ~0x7c14; | |
334 | if (s->cr & GPT_CR_SWR) { /* force reset */ | |
335 | /* handle the reset */ | |
67110c3e | 336 | imx_gpt_reset(DEVICE(s)); |
5ec694b5 JCD |
337 | } else { |
338 | /* set our freq, as the source might have changed */ | |
67110c3e | 339 | imx_gpt_set_freq(s); |
5ec694b5 JCD |
340 | |
341 | if ((oldreg ^ s->cr) & GPT_CR_EN) { | |
342 | if (s->cr & GPT_CR_EN) { | |
343 | if (s->cr & GPT_CR_ENMOD) { | |
203d65a4 MT |
344 | s->next_timeout = GPT_TIMER_MAX; |
345 | ptimer_set_count(s->timer, GPT_TIMER_MAX); | |
67110c3e | 346 | imx_gpt_compute_next_timeout(s, false); |
5ec694b5 JCD |
347 | } |
348 | ptimer_run(s->timer, 1); | |
349 | } else { | |
350 | /* stop timer */ | |
351 | ptimer_stop(s->timer); | |
78d1404d | 352 | } |
5ec694b5 | 353 | } |
78d1404d | 354 | } |
5ec694b5 | 355 | break; |
78d1404d PC |
356 | |
357 | case 1: /* Prescaler */ | |
358 | s->pr = value & 0xfff; | |
67110c3e | 359 | imx_gpt_set_freq(s); |
5ec694b5 | 360 | break; |
78d1404d PC |
361 | |
362 | case 2: /* SR */ | |
5ec694b5 | 363 | s->sr &= ~(value & 0x3f); |
67110c3e | 364 | imx_gpt_update_int(s); |
5ec694b5 | 365 | break; |
78d1404d PC |
366 | |
367 | case 3: /* IR -- interrupt register */ | |
368 | s->ir = value & 0x3f; | |
67110c3e | 369 | imx_gpt_update_int(s); |
5ec694b5 | 370 | |
67110c3e | 371 | imx_gpt_compute_next_timeout(s, false); |
5ec694b5 JCD |
372 | |
373 | break; | |
78d1404d PC |
374 | |
375 | case 4: /* OCR1 -- output compare register */ | |
5ec694b5 JCD |
376 | s->ocr1 = value; |
377 | ||
78d1404d PC |
378 | /* In non-freerun mode, reset count when this register is written */ |
379 | if (!(s->cr & GPT_CR_FRR)) { | |
203d65a4 MT |
380 | s->next_timeout = GPT_TIMER_MAX; |
381 | ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1); | |
78d1404d | 382 | } |
5ec694b5 JCD |
383 | |
384 | /* compute the new timeout */ | |
67110c3e | 385 | imx_gpt_compute_next_timeout(s, false); |
5ec694b5 JCD |
386 | |
387 | break; | |
78d1404d | 388 | |
462566fc | 389 | case 5: /* OCR2 -- output compare register */ |
5ec694b5 JCD |
390 | s->ocr2 = value; |
391 | ||
392 | /* compute the new timeout */ | |
67110c3e | 393 | imx_gpt_compute_next_timeout(s, false); |
5ec694b5 JCD |
394 | |
395 | break; | |
396 | ||
462566fc | 397 | case 6: /* OCR3 -- output compare register */ |
5ec694b5 JCD |
398 | s->ocr3 = value; |
399 | ||
400 | /* compute the new timeout */ | |
67110c3e | 401 | imx_gpt_compute_next_timeout(s, false); |
5ec694b5 JCD |
402 | |
403 | break; | |
404 | ||
78d1404d | 405 | default: |
5ec694b5 JCD |
406 | IPRINTF("Bad offset %x\n", reg); |
407 | break; | |
78d1404d PC |
408 | } |
409 | } | |
410 | ||
67110c3e | 411 | static void imx_gpt_timeout(void *opaque) |
78d1404d | 412 | { |
67110c3e | 413 | IMXGPTState *s = IMX_GPT(opaque); |
78d1404d | 414 | |
5ec694b5 | 415 | DPRINTF("\n"); |
78d1404d | 416 | |
5ec694b5 JCD |
417 | s->sr |= s->next_int; |
418 | s->next_int = 0; | |
419 | ||
67110c3e | 420 | imx_gpt_compute_next_timeout(s, true); |
78d1404d | 421 | |
67110c3e | 422 | imx_gpt_update_int(s); |
5ec694b5 JCD |
423 | |
424 | if (s->freq && (s->cr & GPT_CR_EN)) { | |
425 | ptimer_run(s->timer, 1); | |
426 | } | |
78d1404d PC |
427 | } |
428 | ||
67110c3e JCD |
429 | static const MemoryRegionOps imx_gpt_ops = { |
430 | .read = imx_gpt_read, | |
431 | .write = imx_gpt_write, | |
78d1404d PC |
432 | .endianness = DEVICE_NATIVE_ENDIAN, |
433 | }; | |
434 | ||
435 | ||
67110c3e | 436 | static void imx_gpt_realize(DeviceState *dev, Error **errp) |
78d1404d | 437 | { |
67110c3e JCD |
438 | IMXGPTState *s = IMX_GPT(dev); |
439 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | |
78d1404d PC |
440 | QEMUBH *bh; |
441 | ||
67110c3e | 442 | sysbus_init_irq(sbd, &s->irq); |
853dca12 | 443 | memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpt_ops, s, TYPE_IMX_GPT, |
78d1404d | 444 | 0x00001000); |
67110c3e | 445 | sysbus_init_mmio(sbd, &s->iomem); |
78d1404d | 446 | |
67110c3e | 447 | bh = qemu_bh_new(imx_gpt_timeout, s); |
78d1404d | 448 | s->timer = ptimer_init(bh); |
78d1404d PC |
449 | } |
450 | ||
67110c3e | 451 | static void imx_gpt_class_init(ObjectClass *klass, void *data) |
78d1404d | 452 | { |
67110c3e JCD |
453 | DeviceClass *dc = DEVICE_CLASS(klass); |
454 | ||
455 | dc->realize = imx_gpt_realize; | |
456 | dc->reset = imx_gpt_reset; | |
457 | dc->vmsd = &vmstate_imx_timer_gpt; | |
78d1404d PC |
458 | dc->desc = "i.MX general timer"; |
459 | } | |
460 | ||
67110c3e | 461 | static const TypeInfo imx_gpt_info = { |
5ec694b5 | 462 | .name = TYPE_IMX_GPT, |
78d1404d | 463 | .parent = TYPE_SYS_BUS_DEVICE, |
67110c3e JCD |
464 | .instance_size = sizeof(IMXGPTState), |
465 | .class_init = imx_gpt_class_init, | |
78d1404d PC |
466 | }; |
467 | ||
67110c3e | 468 | static void imx_gpt_register_types(void) |
78d1404d | 469 | { |
67110c3e | 470 | type_register_static(&imx_gpt_info); |
78d1404d PC |
471 | } |
472 | ||
67110c3e | 473 | type_init(imx_gpt_register_types) |