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