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