]>
Commit | Line | Data |
---|---|---|
a50c0d6f JCD |
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 | |
95669e69 | 8 | * Updated by Jean-Christophe Dubois |
a50c0d6f JCD |
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" | |
6a1751b7 | 21 | #include "qemu/main-loop.h" |
a50c0d6f | 22 | |
95669e69 JCD |
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 | ||
a50c0d6f | 46 | # define DPRINTF(fmt, args...) \ |
23005810 | 47 | do { fprintf(stderr, "%s: " fmt , __func__, ##args); } while (0) |
a50c0d6f JCD |
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 | |
95669e69 JCD |
58 | # define IPRINTF(fmt, args...) \ |
59 | do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0) | |
a50c0d6f JCD |
60 | #else |
61 | # define IPRINTF(fmt, args...) do {} while (0) | |
62 | #endif | |
63 | ||
95669e69 JCD |
64 | #define IMX_EPIT(obj) \ |
65 | OBJECT_CHECK(IMXEPITState, (obj), TYPE_IMX_EPIT) | |
66 | ||
a50c0d6f JCD |
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 | */ | |
95669e69 | 92 | static const IMXClk imx_epit_clocks[] = { |
a50c0d6f JCD |
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; | |
95669e69 | 114 | } IMXEPITState; |
a50c0d6f JCD |
115 | |
116 | /* | |
117 | * Update interrupt status | |
118 | */ | |
95669e69 | 119 | static void imx_epit_update_int(IMXEPITState *s) |
a50c0d6f | 120 | { |
95669e69 | 121 | if (s->sr && (s->cr & CR_OCIEN) && (s->cr & CR_EN)) { |
a50c0d6f JCD |
122 | qemu_irq_raise(s->irq); |
123 | } else { | |
124 | qemu_irq_lower(s->irq); | |
125 | } | |
126 | } | |
127 | ||
95669e69 | 128 | static void imx_epit_set_freq(IMXEPITState *s) |
a50c0d6f | 129 | { |
95669e69 JCD |
130 | uint32_t clksrc; |
131 | uint32_t prescaler; | |
a50c0d6f JCD |
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 | ||
95669e69 | 137 | freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler; |
a50c0d6f JCD |
138 | |
139 | s->freq = freq; | |
95669e69 | 140 | |
a50c0d6f JCD |
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 | ||
95669e69 | 149 | static void imx_epit_reset(DeviceState *dev) |
a50c0d6f | 150 | { |
95669e69 | 151 | IMXEPITState *s = IMX_EPIT(dev); |
a50c0d6f JCD |
152 | |
153 | /* | |
154 | * Soft reset doesn't touch some bits; hard reset clears them | |
155 | */ | |
23005810 | 156 | s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN); |
a50c0d6f JCD |
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 */ | |
95669e69 | 165 | imx_epit_set_freq(s); |
a50c0d6f JCD |
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 */ | |
23005810 | 171 | ptimer_run(s->timer_reload, 0); |
a50c0d6f JCD |
172 | } |
173 | } | |
174 | ||
95669e69 | 175 | static uint32_t imx_epit_update_count(IMXEPITState *s) |
a50c0d6f JCD |
176 | { |
177 | s->cnt = ptimer_get_count(s->timer_reload); | |
178 | ||
179 | return s->cnt; | |
180 | } | |
181 | ||
95669e69 | 182 | static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) |
a50c0d6f | 183 | { |
95669e69 JCD |
184 | IMXEPITState *s = IMX_EPIT(opaque); |
185 | uint32_t reg_value = 0; | |
186 | uint32_t reg = offset >> 2; | |
a50c0d6f | 187 | |
95669e69 | 188 | switch (reg) { |
a50c0d6f | 189 | case 0: /* Control Register */ |
95669e69 JCD |
190 | reg_value = s->cr; |
191 | break; | |
a50c0d6f JCD |
192 | |
193 | case 1: /* Status Register */ | |
95669e69 JCD |
194 | reg_value = s->sr; |
195 | break; | |
a50c0d6f JCD |
196 | |
197 | case 2: /* LR - ticks*/ | |
95669e69 JCD |
198 | reg_value = s->lr; |
199 | break; | |
a50c0d6f JCD |
200 | |
201 | case 3: /* CMP */ | |
95669e69 JCD |
202 | reg_value = s->cmp; |
203 | break; | |
a50c0d6f JCD |
204 | |
205 | case 4: /* CNT */ | |
95669e69 JCD |
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; | |
a50c0d6f JCD |
213 | } |
214 | ||
95669e69 JCD |
215 | DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(reg), reg_value); |
216 | ||
217 | return reg_value; | |
a50c0d6f JCD |
218 | } |
219 | ||
95669e69 | 220 | static void imx_epit_reload_compare_timer(IMXEPITState *s) |
a50c0d6f | 221 | { |
23005810 PC |
222 | if ((s->cr & (CR_EN | CR_OCIEN)) == (CR_EN | CR_OCIEN)) { |
223 | /* if the compare feature is on and timers are running */ | |
95669e69 | 224 | uint32_t tmp = imx_epit_update_count(s); |
23005810 | 225 | uint64_t next; |
a50c0d6f | 226 | if (tmp > s->cmp) { |
23005810 PC |
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); | |
a50c0d6f | 231 | } |
23005810 | 232 | ptimer_set_count(s->timer_cmp, next); |
a50c0d6f JCD |
233 | } |
234 | } | |
235 | ||
95669e69 JCD |
236 | static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, |
237 | unsigned size) | |
a50c0d6f | 238 | { |
95669e69 JCD |
239 | IMXEPITState *s = IMX_EPIT(opaque); |
240 | uint32_t reg = offset >> 2; | |
23005810 | 241 | uint64_t oldcr; |
95669e69 JCD |
242 | |
243 | DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(reg), (uint32_t)value); | |
a50c0d6f | 244 | |
95669e69 | 245 | switch (reg) { |
a50c0d6f | 246 | case 0: /* CR */ |
23005810 PC |
247 | |
248 | oldcr = s->cr; | |
a50c0d6f JCD |
249 | s->cr = value & 0x03ffffff; |
250 | if (s->cr & CR_SWR) { | |
251 | /* handle the reset */ | |
95669e69 | 252 | imx_epit_reset(DEVICE(s)); |
a50c0d6f | 253 | } else { |
95669e69 | 254 | imx_epit_set_freq(s); |
a50c0d6f JCD |
255 | } |
256 | ||
23005810 | 257 | if (s->freq && (s->cr & CR_EN) && !(oldcr & CR_EN)) { |
a50c0d6f JCD |
258 | if (s->cr & CR_ENMOD) { |
259 | if (s->cr & CR_RLD) { | |
260 | ptimer_set_limit(s->timer_reload, s->lr, 1); | |
23005810 | 261 | ptimer_set_limit(s->timer_cmp, s->lr, 1); |
a50c0d6f JCD |
262 | } else { |
263 | ptimer_set_limit(s->timer_reload, TIMER_MAX, 1); | |
23005810 | 264 | ptimer_set_limit(s->timer_cmp, TIMER_MAX, 1); |
a50c0d6f JCD |
265 | } |
266 | } | |
267 | ||
95669e69 | 268 | imx_epit_reload_compare_timer(s); |
23005810 PC |
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)) { | |
a50c0d6f JCD |
276 | /* stop both timers */ |
277 | ptimer_stop(s->timer_reload); | |
278 | ptimer_stop(s->timer_cmp); | |
23005810 PC |
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); | |
a50c0d6f JCD |
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; | |
95669e69 | 293 | imx_epit_update_int(s); |
a50c0d6f JCD |
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); | |
23005810 | 304 | ptimer_set_limit(s->timer_cmp, s->lr, 0); |
a50c0d6f JCD |
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 | ||
95669e69 | 310 | imx_epit_reload_compare_timer(s); |
a50c0d6f JCD |
311 | break; |
312 | ||
313 | case 3: /* CMP */ | |
314 | s->cmp = value; | |
315 | ||
95669e69 | 316 | imx_epit_reload_compare_timer(s); |
a50c0d6f JCD |
317 | |
318 | break; | |
319 | ||
320 | default: | |
95669e69 JCD |
321 | IPRINTF("Bad offset %x\n", reg); |
322 | ||
323 | break; | |
a50c0d6f JCD |
324 | } |
325 | } | |
95669e69 | 326 | static void imx_epit_cmp(void *opaque) |
a50c0d6f | 327 | { |
95669e69 | 328 | IMXEPITState *s = IMX_EPIT(opaque); |
a50c0d6f | 329 | |
23005810 | 330 | DPRINTF("sr was %d\n", s->sr); |
a50c0d6f | 331 | |
23005810 PC |
332 | s->sr = 1; |
333 | imx_epit_update_int(s); | |
a50c0d6f JCD |
334 | } |
335 | ||
95669e69 | 336 | void imx_timerp_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm) |
a50c0d6f | 337 | { |
95669e69 | 338 | IMXEPITState *pp; |
a50c0d6f JCD |
339 | DeviceState *dev; |
340 | ||
95669e69 JCD |
341 | dev = sysbus_create_simple(TYPE_IMX_EPIT, addr, irq); |
342 | pp = IMX_EPIT(dev); | |
a50c0d6f JCD |
343 | pp->ccm = ccm; |
344 | } | |
345 | ||
95669e69 JCD |
346 | static const MemoryRegionOps imx_epit_ops = { |
347 | .read = imx_epit_read, | |
348 | .write = imx_epit_write, | |
a50c0d6f JCD |
349 | .endianness = DEVICE_NATIVE_ENDIAN, |
350 | }; | |
351 | ||
95669e69 | 352 | static const VMStateDescription vmstate_imx_timer_epit = { |
6783ecf1 | 353 | .name = "imx.epit", |
a50c0d6f JCD |
354 | .version_id = 2, |
355 | .minimum_version_id = 2, | |
8f1e884b | 356 | .fields = (VMStateField[]) { |
95669e69 JCD |
357 | VMSTATE_UINT32(cr, IMXEPITState), |
358 | VMSTATE_UINT32(sr, IMXEPITState), | |
359 | VMSTATE_UINT32(lr, IMXEPITState), | |
360 | VMSTATE_UINT32(cmp, IMXEPITState), | |
361 | VMSTATE_UINT32(cnt, IMXEPITState), | |
362 | VMSTATE_UINT32(freq, IMXEPITState), | |
363 | VMSTATE_PTIMER(timer_reload, IMXEPITState), | |
364 | VMSTATE_PTIMER(timer_cmp, IMXEPITState), | |
a50c0d6f JCD |
365 | VMSTATE_END_OF_LIST() |
366 | } | |
367 | }; | |
368 | ||
95669e69 | 369 | static void imx_epit_realize(DeviceState *dev, Error **errp) |
a50c0d6f | 370 | { |
95669e69 JCD |
371 | IMXEPITState *s = IMX_EPIT(dev); |
372 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | |
a50c0d6f JCD |
373 | QEMUBH *bh; |
374 | ||
95669e69 JCD |
375 | DPRINTF("\n"); |
376 | ||
377 | sysbus_init_irq(sbd, &s->irq); | |
853dca12 | 378 | memory_region_init_io(&s->iomem, OBJECT(s), &imx_epit_ops, s, TYPE_IMX_EPIT, |
a50c0d6f | 379 | 0x00001000); |
95669e69 | 380 | sysbus_init_mmio(sbd, &s->iomem); |
a50c0d6f | 381 | |
23005810 | 382 | s->timer_reload = ptimer_init(NULL); |
a50c0d6f | 383 | |
95669e69 | 384 | bh = qemu_bh_new(imx_epit_cmp, s); |
a50c0d6f | 385 | s->timer_cmp = ptimer_init(bh); |
a50c0d6f JCD |
386 | } |
387 | ||
95669e69 | 388 | static void imx_epit_class_init(ObjectClass *klass, void *data) |
a50c0d6f JCD |
389 | { |
390 | DeviceClass *dc = DEVICE_CLASS(klass); | |
95669e69 JCD |
391 | |
392 | dc->realize = imx_epit_realize; | |
393 | dc->reset = imx_epit_reset; | |
394 | dc->vmsd = &vmstate_imx_timer_epit; | |
a50c0d6f JCD |
395 | dc->desc = "i.MX periodic timer"; |
396 | } | |
397 | ||
95669e69 JCD |
398 | static const TypeInfo imx_epit_info = { |
399 | .name = TYPE_IMX_EPIT, | |
a50c0d6f | 400 | .parent = TYPE_SYS_BUS_DEVICE, |
95669e69 JCD |
401 | .instance_size = sizeof(IMXEPITState), |
402 | .class_init = imx_epit_class_init, | |
a50c0d6f JCD |
403 | }; |
404 | ||
95669e69 | 405 | static void imx_epit_register_types(void) |
a50c0d6f | 406 | { |
95669e69 | 407 | type_register_static(&imx_epit_info); |
a50c0d6f JCD |
408 | } |
409 | ||
95669e69 | 410 | type_init(imx_epit_register_types) |