xref: /qemu/hw/timer/stellaris-gptm.c (revision 12d1a768bdfea6e27a3a829228840d72507613a1)
1f3eb7557SPeter Maydell /*
2f3eb7557SPeter Maydell  * Luminary Micro Stellaris General Purpose Timer Module
3f3eb7557SPeter Maydell  *
4f3eb7557SPeter Maydell  * Copyright (c) 2006 CodeSourcery.
5f3eb7557SPeter Maydell  * Written by Paul Brook
6f3eb7557SPeter Maydell  *
7f3eb7557SPeter Maydell  * This code is licensed under the GPL.
8f3eb7557SPeter Maydell  */
9f3eb7557SPeter Maydell 
10f3eb7557SPeter Maydell #include "qemu/osdep.h"
11f3eb7557SPeter Maydell #include "qemu/log.h"
12f3eb7557SPeter Maydell #include "qemu/timer.h"
13d18fdd69SPeter Maydell #include "qapi/error.h"
14f3eb7557SPeter Maydell #include "migration/vmstate.h"
15d18fdd69SPeter Maydell #include "hw/qdev-clock.h"
16f3eb7557SPeter Maydell #include "hw/timer/stellaris-gptm.h"
17f3eb7557SPeter Maydell 
18f3eb7557SPeter Maydell static void gptm_update_irq(gptm_state *s)
19f3eb7557SPeter Maydell {
20f3eb7557SPeter Maydell     int level;
21f3eb7557SPeter Maydell     level = (s->state & s->mask) != 0;
22f3eb7557SPeter Maydell     qemu_set_irq(s->irq, level);
23f3eb7557SPeter Maydell }
24f3eb7557SPeter Maydell 
25f3eb7557SPeter Maydell static void gptm_stop(gptm_state *s, int n)
26f3eb7557SPeter Maydell {
27f3eb7557SPeter Maydell     timer_del(s->timer[n]);
28f3eb7557SPeter Maydell }
29f3eb7557SPeter Maydell 
30f3eb7557SPeter Maydell static void gptm_reload(gptm_state *s, int n, int reset)
31f3eb7557SPeter Maydell {
32f3eb7557SPeter Maydell     int64_t tick;
33f3eb7557SPeter Maydell     if (reset) {
34f3eb7557SPeter Maydell         tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
35f3eb7557SPeter Maydell     } else {
36f3eb7557SPeter Maydell         tick = s->tick[n];
37f3eb7557SPeter Maydell     }
38f3eb7557SPeter Maydell 
39f3eb7557SPeter Maydell     if (s->config == 0) {
40f3eb7557SPeter Maydell         /* 32-bit CountDown.  */
41f3eb7557SPeter Maydell         uint32_t count;
42f3eb7557SPeter Maydell         count = s->load[0] | (s->load[1] << 16);
43d18fdd69SPeter Maydell         tick += clock_ticks_to_ns(s->clk, count);
44f3eb7557SPeter Maydell     } else if (s->config == 1) {
45f3eb7557SPeter Maydell         /* 32-bit RTC.  1Hz tick.  */
46f3eb7557SPeter Maydell         tick += NANOSECONDS_PER_SECOND;
47f3eb7557SPeter Maydell     } else if (s->mode[n] == 0xa) {
48f3eb7557SPeter Maydell         /* PWM mode.  Not implemented.  */
49f3eb7557SPeter Maydell     } else {
50f3eb7557SPeter Maydell         qemu_log_mask(LOG_UNIMP,
51f3eb7557SPeter Maydell                       "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
52f3eb7557SPeter Maydell                       s->mode[n]);
53f3eb7557SPeter Maydell         return;
54f3eb7557SPeter Maydell     }
55f3eb7557SPeter Maydell     s->tick[n] = tick;
56f3eb7557SPeter Maydell     timer_mod(s->timer[n], tick);
57f3eb7557SPeter Maydell }
58f3eb7557SPeter Maydell 
59f3eb7557SPeter Maydell static void gptm_tick(void *opaque)
60f3eb7557SPeter Maydell {
61f3eb7557SPeter Maydell     gptm_state **p = (gptm_state **)opaque;
62f3eb7557SPeter Maydell     gptm_state *s;
63f3eb7557SPeter Maydell     int n;
64f3eb7557SPeter Maydell 
65f3eb7557SPeter Maydell     s = *p;
66f3eb7557SPeter Maydell     n = p - s->opaque;
67f3eb7557SPeter Maydell     if (s->config == 0) {
68f3eb7557SPeter Maydell         s->state |= 1;
69f3eb7557SPeter Maydell         if ((s->control & 0x20)) {
70f3eb7557SPeter Maydell             /* Output trigger.  */
71f3eb7557SPeter Maydell             qemu_irq_pulse(s->trigger);
72f3eb7557SPeter Maydell         }
73f3eb7557SPeter Maydell         if (s->mode[0] & 1) {
74f3eb7557SPeter Maydell             /* One-shot.  */
75f3eb7557SPeter Maydell             s->control &= ~1;
76f3eb7557SPeter Maydell         } else {
77f3eb7557SPeter Maydell             /* Periodic.  */
78f3eb7557SPeter Maydell             gptm_reload(s, 0, 0);
79f3eb7557SPeter Maydell         }
80f3eb7557SPeter Maydell     } else if (s->config == 1) {
81f3eb7557SPeter Maydell         /* RTC.  */
82f3eb7557SPeter Maydell         uint32_t match;
83f3eb7557SPeter Maydell         s->rtc++;
84f3eb7557SPeter Maydell         match = s->match[0] | (s->match[1] << 16);
85f3eb7557SPeter Maydell         if (s->rtc > match)
86f3eb7557SPeter Maydell             s->rtc = 0;
87f3eb7557SPeter Maydell         if (s->rtc == 0) {
88f3eb7557SPeter Maydell             s->state |= 8;
89f3eb7557SPeter Maydell         }
90f3eb7557SPeter Maydell         gptm_reload(s, 0, 0);
91f3eb7557SPeter Maydell     } else if (s->mode[n] == 0xa) {
92f3eb7557SPeter Maydell         /* PWM mode.  Not implemented.  */
93f3eb7557SPeter Maydell     } else {
94f3eb7557SPeter Maydell         qemu_log_mask(LOG_UNIMP,
95f3eb7557SPeter Maydell                       "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
96f3eb7557SPeter Maydell                       s->mode[n]);
97f3eb7557SPeter Maydell     }
98f3eb7557SPeter Maydell     gptm_update_irq(s);
99f3eb7557SPeter Maydell }
100f3eb7557SPeter Maydell 
101f3eb7557SPeter Maydell static uint64_t gptm_read(void *opaque, hwaddr offset,
102f3eb7557SPeter Maydell                           unsigned size)
103f3eb7557SPeter Maydell {
104f3eb7557SPeter Maydell     gptm_state *s = (gptm_state *)opaque;
105f3eb7557SPeter Maydell 
106f3eb7557SPeter Maydell     switch (offset) {
107f3eb7557SPeter Maydell     case 0x00: /* CFG */
108f3eb7557SPeter Maydell         return s->config;
109f3eb7557SPeter Maydell     case 0x04: /* TAMR */
110f3eb7557SPeter Maydell         return s->mode[0];
111f3eb7557SPeter Maydell     case 0x08: /* TBMR */
112f3eb7557SPeter Maydell         return s->mode[1];
113f3eb7557SPeter Maydell     case 0x0c: /* CTL */
114f3eb7557SPeter Maydell         return s->control;
115f3eb7557SPeter Maydell     case 0x18: /* IMR */
116f3eb7557SPeter Maydell         return s->mask;
117f3eb7557SPeter Maydell     case 0x1c: /* RIS */
118f3eb7557SPeter Maydell         return s->state;
119f3eb7557SPeter Maydell     case 0x20: /* MIS */
120f3eb7557SPeter Maydell         return s->state & s->mask;
121f3eb7557SPeter Maydell     case 0x24: /* CR */
122f3eb7557SPeter Maydell         return 0;
123f3eb7557SPeter Maydell     case 0x28: /* TAILR */
124f3eb7557SPeter Maydell         return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
125f3eb7557SPeter Maydell     case 0x2c: /* TBILR */
126f3eb7557SPeter Maydell         return s->load[1];
127f3eb7557SPeter Maydell     case 0x30: /* TAMARCHR */
128f3eb7557SPeter Maydell         return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
129f3eb7557SPeter Maydell     case 0x34: /* TBMATCHR */
130f3eb7557SPeter Maydell         return s->match[1];
131f3eb7557SPeter Maydell     case 0x38: /* TAPR */
132f3eb7557SPeter Maydell         return s->prescale[0];
133f3eb7557SPeter Maydell     case 0x3c: /* TBPR */
134f3eb7557SPeter Maydell         return s->prescale[1];
135f3eb7557SPeter Maydell     case 0x40: /* TAPMR */
136f3eb7557SPeter Maydell         return s->match_prescale[0];
137f3eb7557SPeter Maydell     case 0x44: /* TBPMR */
138f3eb7557SPeter Maydell         return s->match_prescale[1];
139f3eb7557SPeter Maydell     case 0x48: /* TAR */
140f3eb7557SPeter Maydell         if (s->config == 1) {
141f3eb7557SPeter Maydell             return s->rtc;
142f3eb7557SPeter Maydell         }
143f3eb7557SPeter Maydell         qemu_log_mask(LOG_UNIMP,
144f3eb7557SPeter Maydell                       "GPTM: read of TAR but timer read not supported\n");
145f3eb7557SPeter Maydell         return 0;
146f3eb7557SPeter Maydell     case 0x4c: /* TBR */
147f3eb7557SPeter Maydell         qemu_log_mask(LOG_UNIMP,
148f3eb7557SPeter Maydell                       "GPTM: read of TBR but timer read not supported\n");
149f3eb7557SPeter Maydell         return 0;
150f3eb7557SPeter Maydell     default:
151f3eb7557SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
152f3eb7557SPeter Maydell                       "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n",
153f3eb7557SPeter Maydell                       offset);
154f3eb7557SPeter Maydell         return 0;
155f3eb7557SPeter Maydell     }
156f3eb7557SPeter Maydell }
157f3eb7557SPeter Maydell 
158f3eb7557SPeter Maydell static void gptm_write(void *opaque, hwaddr offset,
159f3eb7557SPeter Maydell                        uint64_t value, unsigned size)
160f3eb7557SPeter Maydell {
161f3eb7557SPeter Maydell     gptm_state *s = (gptm_state *)opaque;
162f3eb7557SPeter Maydell     uint32_t oldval;
163f3eb7557SPeter Maydell 
164f3eb7557SPeter Maydell     /*
165f3eb7557SPeter Maydell      * The timers should be disabled before changing the configuration.
166f3eb7557SPeter Maydell      * We take advantage of this and defer everything until the timer
167f3eb7557SPeter Maydell      * is enabled.
168f3eb7557SPeter Maydell      */
169f3eb7557SPeter Maydell     switch (offset) {
170f3eb7557SPeter Maydell     case 0x00: /* CFG */
171f3eb7557SPeter Maydell         s->config = value;
172f3eb7557SPeter Maydell         break;
173f3eb7557SPeter Maydell     case 0x04: /* TAMR */
174f3eb7557SPeter Maydell         s->mode[0] = value;
175f3eb7557SPeter Maydell         break;
176f3eb7557SPeter Maydell     case 0x08: /* TBMR */
177f3eb7557SPeter Maydell         s->mode[1] = value;
178f3eb7557SPeter Maydell         break;
179f3eb7557SPeter Maydell     case 0x0c: /* CTL */
180f3eb7557SPeter Maydell         oldval = s->control;
181f3eb7557SPeter Maydell         s->control = value;
182f3eb7557SPeter Maydell         /* TODO: Implement pause.  */
183f3eb7557SPeter Maydell         if ((oldval ^ value) & 1) {
184f3eb7557SPeter Maydell             if (value & 1) {
185f3eb7557SPeter Maydell                 gptm_reload(s, 0, 1);
186f3eb7557SPeter Maydell             } else {
187f3eb7557SPeter Maydell                 gptm_stop(s, 0);
188f3eb7557SPeter Maydell             }
189f3eb7557SPeter Maydell         }
190f3eb7557SPeter Maydell         if (((oldval ^ value) & 0x100) && s->config >= 4) {
191f3eb7557SPeter Maydell             if (value & 0x100) {
192f3eb7557SPeter Maydell                 gptm_reload(s, 1, 1);
193f3eb7557SPeter Maydell             } else {
194f3eb7557SPeter Maydell                 gptm_stop(s, 1);
195f3eb7557SPeter Maydell             }
196f3eb7557SPeter Maydell         }
197f3eb7557SPeter Maydell         break;
198f3eb7557SPeter Maydell     case 0x18: /* IMR */
199f3eb7557SPeter Maydell         s->mask = value & 0x77;
200f3eb7557SPeter Maydell         gptm_update_irq(s);
201f3eb7557SPeter Maydell         break;
202f3eb7557SPeter Maydell     case 0x24: /* CR */
203f3eb7557SPeter Maydell         s->state &= ~value;
204f3eb7557SPeter Maydell         break;
205f3eb7557SPeter Maydell     case 0x28: /* TAILR */
206f3eb7557SPeter Maydell         s->load[0] = value & 0xffff;
207f3eb7557SPeter Maydell         if (s->config < 4) {
208f3eb7557SPeter Maydell             s->load[1] = value >> 16;
209f3eb7557SPeter Maydell         }
210f3eb7557SPeter Maydell         break;
211f3eb7557SPeter Maydell     case 0x2c: /* TBILR */
212f3eb7557SPeter Maydell         s->load[1] = value & 0xffff;
213f3eb7557SPeter Maydell         break;
214f3eb7557SPeter Maydell     case 0x30: /* TAMARCHR */
215f3eb7557SPeter Maydell         s->match[0] = value & 0xffff;
216f3eb7557SPeter Maydell         if (s->config < 4) {
217f3eb7557SPeter Maydell             s->match[1] = value >> 16;
218f3eb7557SPeter Maydell         }
219f3eb7557SPeter Maydell         break;
220f3eb7557SPeter Maydell     case 0x34: /* TBMATCHR */
221f3eb7557SPeter Maydell         s->match[1] = value >> 16;
222f3eb7557SPeter Maydell         break;
223f3eb7557SPeter Maydell     case 0x38: /* TAPR */
224f3eb7557SPeter Maydell         s->prescale[0] = value;
225f3eb7557SPeter Maydell         break;
226f3eb7557SPeter Maydell     case 0x3c: /* TBPR */
227f3eb7557SPeter Maydell         s->prescale[1] = value;
228f3eb7557SPeter Maydell         break;
229f3eb7557SPeter Maydell     case 0x40: /* TAPMR */
230f3eb7557SPeter Maydell         s->match_prescale[0] = value;
231f3eb7557SPeter Maydell         break;
232f3eb7557SPeter Maydell     case 0x44: /* TBPMR */
233f3eb7557SPeter Maydell         s->match_prescale[0] = value;
234f3eb7557SPeter Maydell         break;
235f3eb7557SPeter Maydell     default:
236f3eb7557SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
237f3eb7557SPeter Maydell                       "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n",
238f3eb7557SPeter Maydell                       offset);
239f3eb7557SPeter Maydell     }
240f3eb7557SPeter Maydell     gptm_update_irq(s);
241f3eb7557SPeter Maydell }
242f3eb7557SPeter Maydell 
243f3eb7557SPeter Maydell static const MemoryRegionOps gptm_ops = {
244f3eb7557SPeter Maydell     .read = gptm_read,
245f3eb7557SPeter Maydell     .write = gptm_write,
246f3eb7557SPeter Maydell     .endianness = DEVICE_NATIVE_ENDIAN,
247f3eb7557SPeter Maydell };
248f3eb7557SPeter Maydell 
249f3eb7557SPeter Maydell static const VMStateDescription vmstate_stellaris_gptm = {
250f3eb7557SPeter Maydell     .name = "stellaris_gptm",
251d18fdd69SPeter Maydell     .version_id = 2,
252d18fdd69SPeter Maydell     .minimum_version_id = 2,
253ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
254f3eb7557SPeter Maydell         VMSTATE_UINT32(config, gptm_state),
255f3eb7557SPeter Maydell         VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
256f3eb7557SPeter Maydell         VMSTATE_UINT32(control, gptm_state),
257f3eb7557SPeter Maydell         VMSTATE_UINT32(state, gptm_state),
258f3eb7557SPeter Maydell         VMSTATE_UINT32(mask, gptm_state),
259f3eb7557SPeter Maydell         VMSTATE_UNUSED(8),
260f3eb7557SPeter Maydell         VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
261f3eb7557SPeter Maydell         VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
262f3eb7557SPeter Maydell         VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
263f3eb7557SPeter Maydell         VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
264f3eb7557SPeter Maydell         VMSTATE_UINT32(rtc, gptm_state),
265f3eb7557SPeter Maydell         VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
266f3eb7557SPeter Maydell         VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2),
267d18fdd69SPeter Maydell         VMSTATE_CLOCK(clk, gptm_state),
268f3eb7557SPeter Maydell         VMSTATE_END_OF_LIST()
269f3eb7557SPeter Maydell     }
270f3eb7557SPeter Maydell };
271f3eb7557SPeter Maydell 
272f3eb7557SPeter Maydell static void stellaris_gptm_init(Object *obj)
273f3eb7557SPeter Maydell {
274f3eb7557SPeter Maydell     DeviceState *dev = DEVICE(obj);
275f3eb7557SPeter Maydell     gptm_state *s = STELLARIS_GPTM(obj);
276f3eb7557SPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
277f3eb7557SPeter Maydell 
278f3eb7557SPeter Maydell     sysbus_init_irq(sbd, &s->irq);
279f3eb7557SPeter Maydell     qdev_init_gpio_out(dev, &s->trigger, 1);
280f3eb7557SPeter Maydell 
281f3eb7557SPeter Maydell     memory_region_init_io(&s->iomem, obj, &gptm_ops, s,
282f3eb7557SPeter Maydell                           "gptm", 0x1000);
283f3eb7557SPeter Maydell     sysbus_init_mmio(sbd, &s->iomem);
284f3eb7557SPeter Maydell 
285f3eb7557SPeter Maydell     s->opaque[0] = s->opaque[1] = s;
286d18fdd69SPeter Maydell 
287d18fdd69SPeter Maydell     /*
288d18fdd69SPeter Maydell      * TODO: in an ideal world we would model the effects of changing
289d18fdd69SPeter Maydell      * the input clock frequency while the countdown timer is active.
290d18fdd69SPeter Maydell      * The best way to do this would be to convert the device to use
291d18fdd69SPeter Maydell      * ptimer instead of hand-rolling its own timer. This would also
292d18fdd69SPeter Maydell      * make it easy to implement reading the current count from the
293d18fdd69SPeter Maydell      * TAR and TBR registers.
294d18fdd69SPeter Maydell      */
295d18fdd69SPeter Maydell     s->clk = qdev_init_clock_in(dev, "clk", NULL, NULL, 0);
296f3eb7557SPeter Maydell }
297f3eb7557SPeter Maydell 
298f3eb7557SPeter Maydell static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
299f3eb7557SPeter Maydell {
300f3eb7557SPeter Maydell     gptm_state *s = STELLARIS_GPTM(dev);
301d18fdd69SPeter Maydell 
302d18fdd69SPeter Maydell     if (!clock_has_source(s->clk)) {
303d18fdd69SPeter Maydell         error_setg(errp, "stellaris-gptm: clk must be connected");
304d18fdd69SPeter Maydell         return;
305d18fdd69SPeter Maydell     }
306d18fdd69SPeter Maydell 
307f3eb7557SPeter Maydell     s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]);
308f3eb7557SPeter Maydell     s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]);
309f3eb7557SPeter Maydell }
310f3eb7557SPeter Maydell 
311*12d1a768SPhilippe Mathieu-Daudé static void stellaris_gptm_class_init(ObjectClass *klass, const void *data)
312f3eb7557SPeter Maydell {
313f3eb7557SPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
314f3eb7557SPeter Maydell 
315f3eb7557SPeter Maydell     dc->vmsd = &vmstate_stellaris_gptm;
316f3eb7557SPeter Maydell     dc->realize = stellaris_gptm_realize;
317f3eb7557SPeter Maydell }
318f3eb7557SPeter Maydell 
319f3eb7557SPeter Maydell static const TypeInfo stellaris_gptm_info = {
320f3eb7557SPeter Maydell     .name          = TYPE_STELLARIS_GPTM,
321f3eb7557SPeter Maydell     .parent        = TYPE_SYS_BUS_DEVICE,
322f3eb7557SPeter Maydell     .instance_size = sizeof(gptm_state),
323f3eb7557SPeter Maydell     .instance_init = stellaris_gptm_init,
324f3eb7557SPeter Maydell     .class_init    = stellaris_gptm_class_init,
325f3eb7557SPeter Maydell };
326f3eb7557SPeter Maydell 
327f3eb7557SPeter Maydell static void stellaris_gptm_register_types(void)
328f3eb7557SPeter Maydell {
329f3eb7557SPeter Maydell     type_register_static(&stellaris_gptm_info);
330f3eb7557SPeter Maydell }
331f3eb7557SPeter Maydell 
332f3eb7557SPeter Maydell type_init(stellaris_gptm_register_types)
333