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