qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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 
     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 
     25 static void gptm_stop(gptm_state *s, int n)
     26 {
     27     timer_del(s->timer[n]);
     28 }
     29 
     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 
     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 
    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 
    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 = (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 
    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 
    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 
    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 
    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)