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