qemu

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

cmsdk-apb-timer.c (8699B)


      1 /*
      2  * ARM CMSDK APB timer emulation
      3  *
      4  * Copyright (c) 2017 Linaro Limited
      5  * Written by Peter Maydell
      6  *
      7  *  This program is free software; you can redistribute it and/or modify
      8  *  it under the terms of the GNU General Public License version 2 or
      9  *  (at your option) any later version.
     10  */
     11 
     12 /* This is a model of the "APB timer" which is part of the Cortex-M
     13  * System Design Kit (CMSDK) and documented in the Cortex-M System
     14  * Design Kit Technical Reference Manual (ARM DDI0479C):
     15  * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
     16  *
     17  * The hardware has an EXTIN input wire, which can be configured
     18  * by the guest to act either as a 'timer enable' (timer does not run
     19  * when EXTIN is low), or as a 'timer clock' (timer runs at frequency
     20  * of EXTIN clock, not PCLK frequency). We don't model this.
     21  *
     22  * The documentation is not very clear about the exact behaviour;
     23  * we choose to implement that the interrupt is triggered when
     24  * the counter goes from 1 to 0, that the counter then holds at 0
     25  * for one clock cycle before reloading from the RELOAD register,
     26  * and that if the RELOAD register is 0 this does not cause an
     27  * interrupt (as there is no further 1->0 transition).
     28  */
     29 
     30 #include "qemu/osdep.h"
     31 #include "qemu/log.h"
     32 #include "qemu/module.h"
     33 #include "qapi/error.h"
     34 #include "trace.h"
     35 #include "hw/sysbus.h"
     36 #include "hw/irq.h"
     37 #include "hw/registerfields.h"
     38 #include "hw/qdev-clock.h"
     39 #include "hw/timer/cmsdk-apb-timer.h"
     40 #include "migration/vmstate.h"
     41 
     42 REG32(CTRL, 0)
     43     FIELD(CTRL, EN, 0, 1)
     44     FIELD(CTRL, SELEXTEN, 1, 1)
     45     FIELD(CTRL, SELEXTCLK, 2, 1)
     46     FIELD(CTRL, IRQEN, 3, 1)
     47 REG32(VALUE, 4)
     48 REG32(RELOAD, 8)
     49 REG32(INTSTATUS, 0xc)
     50     FIELD(INTSTATUS, IRQ, 0, 1)
     51 REG32(PID4, 0xFD0)
     52 REG32(PID5, 0xFD4)
     53 REG32(PID6, 0xFD8)
     54 REG32(PID7, 0xFDC)
     55 REG32(PID0, 0xFE0)
     56 REG32(PID1, 0xFE4)
     57 REG32(PID2, 0xFE8)
     58 REG32(PID3, 0xFEC)
     59 REG32(CID0, 0xFF0)
     60 REG32(CID1, 0xFF4)
     61 REG32(CID2, 0xFF8)
     62 REG32(CID3, 0xFFC)
     63 
     64 /* PID/CID values */
     65 static const int timer_id[] = {
     66     0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
     67     0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
     68     0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
     69 };
     70 
     71 static void cmsdk_apb_timer_update(CMSDKAPBTimer *s)
     72 {
     73     qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
     74 }
     75 
     76 static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
     77 {
     78     CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
     79     uint64_t r;
     80 
     81     switch (offset) {
     82     case A_CTRL:
     83         r = s->ctrl;
     84         break;
     85     case A_VALUE:
     86         r = ptimer_get_count(s->timer);
     87         break;
     88     case A_RELOAD:
     89         r = ptimer_get_limit(s->timer);
     90         break;
     91     case A_INTSTATUS:
     92         r = s->intstatus;
     93         break;
     94     case A_PID4 ... A_CID3:
     95         r = timer_id[(offset - A_PID4) / 4];
     96         break;
     97     default:
     98         qemu_log_mask(LOG_GUEST_ERROR,
     99                       "CMSDK APB timer read: bad offset %x\n", (int) offset);
    100         r = 0;
    101         break;
    102     }
    103     trace_cmsdk_apb_timer_read(offset, r, size);
    104     return r;
    105 }
    106 
    107 static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
    108                                   unsigned size)
    109 {
    110     CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
    111 
    112     trace_cmsdk_apb_timer_write(offset, value, size);
    113 
    114     switch (offset) {
    115     case A_CTRL:
    116         if (value & 6) {
    117             /* Bits [1] and [2] enable using EXTIN as either clock or
    118              * an enable line. We don't model this.
    119              */
    120             qemu_log_mask(LOG_UNIMP,
    121                           "CMSDK APB timer: EXTIN input not supported\n");
    122         }
    123         s->ctrl = value & 0xf;
    124         ptimer_transaction_begin(s->timer);
    125         if (s->ctrl & R_CTRL_EN_MASK) {
    126             ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
    127         } else {
    128             ptimer_stop(s->timer);
    129         }
    130         ptimer_transaction_commit(s->timer);
    131         break;
    132     case A_RELOAD:
    133         /* Writing to reload also sets the current timer value */
    134         ptimer_transaction_begin(s->timer);
    135         if (!value) {
    136             ptimer_stop(s->timer);
    137         }
    138         ptimer_set_limit(s->timer, value, 1);
    139         if (value && (s->ctrl & R_CTRL_EN_MASK)) {
    140             /*
    141              * Make sure timer is running (it might have stopped if this
    142              * was an expired one-shot timer)
    143              */
    144             ptimer_run(s->timer, 0);
    145         }
    146         ptimer_transaction_commit(s->timer);
    147         break;
    148     case A_VALUE:
    149         ptimer_transaction_begin(s->timer);
    150         if (!value && !ptimer_get_limit(s->timer)) {
    151             ptimer_stop(s->timer);
    152         }
    153         ptimer_set_count(s->timer, value);
    154         if (value && (s->ctrl & R_CTRL_EN_MASK)) {
    155             ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
    156         }
    157         ptimer_transaction_commit(s->timer);
    158         break;
    159     case A_INTSTATUS:
    160         /* Just one bit, which is W1C. */
    161         value &= 1;
    162         s->intstatus &= ~value;
    163         cmsdk_apb_timer_update(s);
    164         break;
    165     case A_PID4 ... A_CID3:
    166         qemu_log_mask(LOG_GUEST_ERROR,
    167                       "CMSDK APB timer write: write to RO offset 0x%x\n",
    168                       (int)offset);
    169         break;
    170     default:
    171         qemu_log_mask(LOG_GUEST_ERROR,
    172                       "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
    173         break;
    174     }
    175 }
    176 
    177 static const MemoryRegionOps cmsdk_apb_timer_ops = {
    178     .read = cmsdk_apb_timer_read,
    179     .write = cmsdk_apb_timer_write,
    180     .endianness = DEVICE_LITTLE_ENDIAN,
    181 };
    182 
    183 static void cmsdk_apb_timer_tick(void *opaque)
    184 {
    185     CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
    186 
    187     if (s->ctrl & R_CTRL_IRQEN_MASK) {
    188         s->intstatus |= R_INTSTATUS_IRQ_MASK;
    189         cmsdk_apb_timer_update(s);
    190     }
    191 }
    192 
    193 static void cmsdk_apb_timer_reset(DeviceState *dev)
    194 {
    195     CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
    196 
    197     trace_cmsdk_apb_timer_reset();
    198     s->ctrl = 0;
    199     s->intstatus = 0;
    200     ptimer_transaction_begin(s->timer);
    201     ptimer_stop(s->timer);
    202     /* Set the limit and the count */
    203     ptimer_set_limit(s->timer, 0, 1);
    204     ptimer_transaction_commit(s->timer);
    205 }
    206 
    207 static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
    208 {
    209     CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
    210 
    211     ptimer_transaction_begin(s->timer);
    212     ptimer_set_period_from_clock(s->timer, s->pclk, 1);
    213     ptimer_transaction_commit(s->timer);
    214 }
    215 
    216 static void cmsdk_apb_timer_init(Object *obj)
    217 {
    218     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    219     CMSDKAPBTimer *s = CMSDK_APB_TIMER(obj);
    220 
    221     memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
    222                           s, "cmsdk-apb-timer", 0x1000);
    223     sysbus_init_mmio(sbd, &s->iomem);
    224     sysbus_init_irq(sbd, &s->timerint);
    225     s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
    226                                  cmsdk_apb_timer_clk_update, s, ClockUpdate);
    227 }
    228 
    229 static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
    230 {
    231     CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
    232 
    233     if (!clock_has_source(s->pclk)) {
    234         error_setg(errp, "CMSDK APB timer: pclk clock must be connected");
    235         return;
    236     }
    237 
    238     s->timer = ptimer_init(cmsdk_apb_timer_tick, s,
    239                            PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
    240                            PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
    241                            PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
    242                            PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
    243 
    244     ptimer_transaction_begin(s->timer);
    245     ptimer_set_period_from_clock(s->timer, s->pclk, 1);
    246     ptimer_transaction_commit(s->timer);
    247 }
    248 
    249 static const VMStateDescription cmsdk_apb_timer_vmstate = {
    250     .name = "cmsdk-apb-timer",
    251     .version_id = 2,
    252     .minimum_version_id = 2,
    253     .fields = (VMStateField[]) {
    254         VMSTATE_PTIMER(timer, CMSDKAPBTimer),
    255         VMSTATE_CLOCK(pclk, CMSDKAPBTimer),
    256         VMSTATE_UINT32(ctrl, CMSDKAPBTimer),
    257         VMSTATE_UINT32(value, CMSDKAPBTimer),
    258         VMSTATE_UINT32(reload, CMSDKAPBTimer),
    259         VMSTATE_UINT32(intstatus, CMSDKAPBTimer),
    260         VMSTATE_END_OF_LIST()
    261     }
    262 };
    263 
    264 static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
    265 {
    266     DeviceClass *dc = DEVICE_CLASS(klass);
    267 
    268     dc->realize = cmsdk_apb_timer_realize;
    269     dc->vmsd = &cmsdk_apb_timer_vmstate;
    270     dc->reset = cmsdk_apb_timer_reset;
    271 }
    272 
    273 static const TypeInfo cmsdk_apb_timer_info = {
    274     .name = TYPE_CMSDK_APB_TIMER,
    275     .parent = TYPE_SYS_BUS_DEVICE,
    276     .instance_size = sizeof(CMSDKAPBTimer),
    277     .instance_init = cmsdk_apb_timer_init,
    278     .class_init = cmsdk_apb_timer_class_init,
    279 };
    280 
    281 static void cmsdk_apb_timer_register_types(void)
    282 {
    283     type_register_static(&cmsdk_apb_timer_info);
    284 }
    285 
    286 type_init(cmsdk_apb_timer_register_types);