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