2 * Luminary Micro Stellaris General Purpose Timer Module
4 * Copyright (c) 2006 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licensed under the GPL.
10 #include "qemu/osdep.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"
18 static void gptm_update_irq(gptm_state
*s
)
21 level
= (s
->state
& s
->mask
) != 0;
22 qemu_set_irq(s
->irq
, level
);
25 static void gptm_stop(gptm_state
*s
, int n
)
27 timer_del(s
->timer
[n
]);
30 static void gptm_reload(gptm_state
*s
, int n
, int reset
)
34 tick
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
40 /* 32-bit CountDown. */
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. */
50 qemu_log_mask(LOG_UNIMP
,
51 "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
56 timer_mod(s
->timer
[n
], tick
);
59 static void gptm_tick(void *opaque
)
61 gptm_state
**p
= (gptm_state
**)opaque
;
69 if ((s
->control
& 0x20)) {
71 qemu_irq_pulse(s
->trigger
);
80 } else if (s
->config
== 1) {
84 match
= s
->match
[0] | (s
->match
[1] << 16);
91 } else if (s
->mode
[n
] == 0xa) {
92 /* PWM mode. Not implemented. */
94 qemu_log_mask(LOG_UNIMP
,
95 "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
101 static uint64_t gptm_read(void *opaque
, hwaddr offset
,
104 gptm_state
*s
= (gptm_state
*)opaque
;
109 case 0x04: /* TAMR */
111 case 0x08: /* TBMR */
120 return s
->state
& s
->mask
;
123 case 0x28: /* TAILR */
124 return s
->load
[0] | ((s
->config
< 4) ? (s
->load
[1] << 16) : 0);
125 case 0x2c: /* TBILR */
127 case 0x30: /* TAMARCHR */
128 return s
->match
[0] | ((s
->config
< 4) ? (s
->match
[1] << 16) : 0);
129 case 0x34: /* TBMATCHR */
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];
140 if (s
->config
== 1) {
143 qemu_log_mask(LOG_UNIMP
,
144 "GPTM: read of TAR but timer read not supported\n");
147 qemu_log_mask(LOG_UNIMP
,
148 "GPTM: read of TBR but timer read not supported\n");
151 qemu_log_mask(LOG_GUEST_ERROR
,
152 "GPTM: read at bad offset 0x02%" HWADDR_PRIx
"\n",
158 static void gptm_write(void *opaque
, hwaddr offset
,
159 uint64_t value
, unsigned size
)
161 gptm_state
*s
= (gptm_state
*)opaque
;
165 * The timers should be disabled before changing the configuration.
166 * We take advantage of this and defer everything until the timer
173 case 0x04: /* TAMR */
176 case 0x08: /* TBMR */
182 /* TODO: Implement pause. */
183 if ((oldval
^ value
) & 1) {
185 gptm_reload(s
, 0, 1);
190 if (((oldval
^ value
) & 0x100) && s
->config
>= 4) {
192 gptm_reload(s
, 1, 1);
199 s
->mask
= value
& 0x77;
205 case 0x28: /* TAILR */
206 s
->load
[0] = value
& 0xffff;
208 s
->load
[1] = value
>> 16;
211 case 0x2c: /* TBILR */
212 s
->load
[1] = value
& 0xffff;
214 case 0x30: /* TAMARCHR */
215 s
->match
[0] = value
& 0xffff;
217 s
->match
[1] = value
>> 16;
220 case 0x34: /* TBMATCHR */
221 s
->match
[1] = value
>> 16;
223 case 0x38: /* TAPR */
224 s
->prescale
[0] = value
;
226 case 0x3c: /* TBPR */
227 s
->prescale
[1] = value
;
229 case 0x40: /* TAPMR */
230 s
->match_prescale
[0] = value
;
232 case 0x44: /* TBPMR */
233 s
->match_prescale
[0] = value
;
236 qemu_log_mask(LOG_GUEST_ERROR
,
237 "GPTM: write at bad offset 0x02%" HWADDR_PRIx
"\n",
243 static const MemoryRegionOps gptm_ops
= {
246 .endianness
= DEVICE_NATIVE_ENDIAN
,
249 static const VMStateDescription vmstate_stellaris_gptm
= {
250 .name
= "stellaris_gptm",
252 .minimum_version_id
= 2,
253 .fields
= (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
),
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()
272 static void stellaris_gptm_init(Object
*obj
)
274 DeviceState
*dev
= DEVICE(obj
);
275 gptm_state
*s
= STELLARIS_GPTM(obj
);
276 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
278 sysbus_init_irq(sbd
, &s
->irq
);
279 qdev_init_gpio_out(dev
, &s
->trigger
, 1);
281 memory_region_init_io(&s
->iomem
, obj
, &gptm_ops
, s
,
283 sysbus_init_mmio(sbd
, &s
->iomem
);
285 s
->opaque
[0] = s
->opaque
[1] = s
;
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.
295 s
->clk
= qdev_init_clock_in(dev
, "clk", NULL
, NULL
, 0);
298 static void stellaris_gptm_realize(DeviceState
*dev
, Error
**errp
)
300 gptm_state
*s
= STELLARIS_GPTM(dev
);
302 if (!clock_has_source(s
->clk
)) {
303 error_setg(errp
, "stellaris-gptm: clk must be connected");
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]);
311 static void stellaris_gptm_class_init(ObjectClass
*klass
, void *data
)
313 DeviceClass
*dc
= DEVICE_CLASS(klass
);
315 dc
->vmsd
= &vmstate_stellaris_gptm
;
316 dc
->realize
= stellaris_gptm_realize
;
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
,
327 static void stellaris_gptm_register_types(void)
329 type_register_static(&stellaris_gptm_info
);
332 type_init(stellaris_gptm_register_types
)