1
2
3
4
5
6
7
8
9
10
11
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
28static 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
54
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
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 EPIT_TIMER_MAX 0XFFFFFFFFUL
87
88
89
90
91
92static const IMXClk imx_epit_clocks[] = {
93 0,
94 IPG,
95 IPG,
96 CLK_32k,
97};
98
99typedef 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
118
119static 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
128static 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
149static void imx_epit_reset(DeviceState *dev)
150{
151 IMXEPITState *s = IMX_EPIT(dev);
152
153
154
155
156 s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN);
157 s->sr = 0;
158 s->lr = EPIT_TIMER_MAX;
159 s->cmp = 0;
160 s->cnt = 0;
161
162 ptimer_stop(s->timer_cmp);
163 ptimer_stop(s->timer_reload);
164
165 imx_epit_set_freq(s);
166
167 ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1);
168 ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1);
169 if (s->freq && (s->cr & CR_EN)) {
170
171 ptimer_run(s->timer_reload, 0);
172 }
173}
174
175static 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
182static 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:
190 reg_value = s->cr;
191 break;
192
193 case 1:
194 reg_value = s->sr;
195 break;
196
197 case 2:
198 reg_value = s->lr;
199 break;
200
201 case 3:
202 reg_value = s->cmp;
203 break;
204
205 case 4:
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
220static void imx_epit_reload_compare_timer(IMXEPITState *s)
221{
222 if ((s->cr & (CR_EN | CR_OCIEN)) == (CR_EN | CR_OCIEN)) {
223
224 uint32_t tmp = imx_epit_update_count(s);
225 uint64_t next;
226 if (tmp > s->cmp) {
227
228 next = tmp - s->cmp;
229 } else {
230 next = tmp - s->cmp + ((s->cr & CR_RLD) ? EPIT_TIMER_MAX : s->lr);
231 }
232 ptimer_set_count(s->timer_cmp, next);
233 }
234}
235
236static 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:
247
248 oldcr = s->cr;
249 s->cr = value & 0x03ffffff;
250 if (s->cr & CR_SWR) {
251
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, EPIT_TIMER_MAX, 1);
264 ptimer_set_limit(s->timer_cmp, EPIT_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
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:
290
291 if (value & 0x01) {
292 s->sr = 0;
293 imx_epit_update_int(s);
294 }
295 break;
296
297 case 2:
298 s->lr = value;
299
300 if (s->cr & CR_RLD) {
301
302
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
307 ptimer_set_count(s->timer_reload, s->lr);
308 }
309
310 imx_epit_reload_compare_timer(s);
311 break;
312
313 case 3:
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}
326static 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
336void 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
346static const MemoryRegionOps imx_epit_ops = {
347 .read = imx_epit_read,
348 .write = imx_epit_write,
349 .endianness = DEVICE_NATIVE_ENDIAN,
350};
351
352static const VMStateDescription vmstate_imx_timer_epit = {
353 .name = "imx.epit",
354 .version_id = 2,
355 .minimum_version_id = 2,
356 .fields = (VMStateField[]) {
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),
365 VMSTATE_END_OF_LIST()
366 }
367};
368
369static void imx_epit_realize(DeviceState *dev, Error **errp)
370{
371 IMXEPITState *s = IMX_EPIT(dev);
372 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
373 QEMUBH *bh;
374
375 DPRINTF("\n");
376
377 sysbus_init_irq(sbd, &s->irq);
378 memory_region_init_io(&s->iomem, OBJECT(s), &imx_epit_ops, s, TYPE_IMX_EPIT,
379 0x00001000);
380 sysbus_init_mmio(sbd, &s->iomem);
381
382 s->timer_reload = ptimer_init(NULL);
383
384 bh = qemu_bh_new(imx_epit_cmp, s);
385 s->timer_cmp = ptimer_init(bh);
386}
387
388static void imx_epit_class_init(ObjectClass *klass, void *data)
389{
390 DeviceClass *dc = DEVICE_CLASS(klass);
391
392 dc->realize = imx_epit_realize;
393 dc->reset = imx_epit_reset;
394 dc->vmsd = &vmstate_imx_timer_epit;
395 dc->desc = "i.MX periodic timer";
396}
397
398static const TypeInfo imx_epit_info = {
399 .name = TYPE_IMX_EPIT,
400 .parent = TYPE_SYS_BUS_DEVICE,
401 .instance_size = sizeof(IMXEPITState),
402 .class_init = imx_epit_class_init,
403};
404
405static void imx_epit_register_types(void)
406{
407 type_register_static(&imx_epit_info);
408}
409
410type_init(imx_epit_register_types)
411