stellaris-gptm.c (9237B)
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 18static 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 25static void gptm_stop(gptm_state *s, int n) 26{ 27 timer_del(s->timer[n]); 28} 29 30static 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 59static 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 101static 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 158static 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 243static const MemoryRegionOps gptm_ops = { 244 .read = gptm_read, 245 .write = gptm_write, 246 .endianness = DEVICE_NATIVE_ENDIAN, 247}; 248 249static const VMStateDescription vmstate_stellaris_gptm = { 250 .name = "stellaris_gptm", 251 .version_id = 2, 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), 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 272static 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 298static 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 311static 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 319static 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 327static void stellaris_gptm_register_types(void) 328{ 329 type_register_static(&stellaris_gptm_info); 330} 331 332type_init(stellaris_gptm_register_types)