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