xref: /qemu/hw/timer/cmsdk-apb-timer.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
15dd85b4bSPeter Maydell /*
25dd85b4bSPeter Maydell  * ARM CMSDK APB timer emulation
35dd85b4bSPeter Maydell  *
45dd85b4bSPeter Maydell  * Copyright (c) 2017 Linaro Limited
55dd85b4bSPeter Maydell  * Written by Peter Maydell
65dd85b4bSPeter Maydell  *
75dd85b4bSPeter Maydell  *  This program is free software; you can redistribute it and/or modify
85dd85b4bSPeter Maydell  *  it under the terms of the GNU General Public License version 2 or
95dd85b4bSPeter Maydell  *  (at your option) any later version.
105dd85b4bSPeter Maydell  */
115dd85b4bSPeter Maydell 
125dd85b4bSPeter Maydell /* This is a model of the "APB timer" which is part of the Cortex-M
135dd85b4bSPeter Maydell  * System Design Kit (CMSDK) and documented in the Cortex-M System
145dd85b4bSPeter Maydell  * Design Kit Technical Reference Manual (ARM DDI0479C):
155dd85b4bSPeter Maydell  * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
165dd85b4bSPeter Maydell  *
175dd85b4bSPeter Maydell  * The hardware has an EXTIN input wire, which can be configured
185dd85b4bSPeter Maydell  * by the guest to act either as a 'timer enable' (timer does not run
195dd85b4bSPeter Maydell  * when EXTIN is low), or as a 'timer clock' (timer runs at frequency
205dd85b4bSPeter Maydell  * of EXTIN clock, not PCLK frequency). We don't model this.
215dd85b4bSPeter Maydell  *
225dd85b4bSPeter Maydell  * The documentation is not very clear about the exact behaviour;
235dd85b4bSPeter Maydell  * we choose to implement that the interrupt is triggered when
245dd85b4bSPeter Maydell  * the counter goes from 1 to 0, that the counter then holds at 0
255dd85b4bSPeter Maydell  * for one clock cycle before reloading from the RELOAD register,
265dd85b4bSPeter Maydell  * and that if the RELOAD register is 0 this does not cause an
275dd85b4bSPeter Maydell  * interrupt (as there is no further 1->0 transition).
285dd85b4bSPeter Maydell  */
295dd85b4bSPeter Maydell 
305dd85b4bSPeter Maydell #include "qemu/osdep.h"
315dd85b4bSPeter Maydell #include "qemu/log.h"
325dd85b4bSPeter Maydell #include "qemu/main-loop.h"
33*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
345dd85b4bSPeter Maydell #include "qapi/error.h"
355dd85b4bSPeter Maydell #include "trace.h"
365dd85b4bSPeter Maydell #include "hw/sysbus.h"
375dd85b4bSPeter Maydell #include "hw/registerfields.h"
385dd85b4bSPeter Maydell #include "hw/timer/cmsdk-apb-timer.h"
395dd85b4bSPeter Maydell 
405dd85b4bSPeter Maydell REG32(CTRL, 0)
415dd85b4bSPeter Maydell     FIELD(CTRL, EN, 0, 1)
425dd85b4bSPeter Maydell     FIELD(CTRL, SELEXTEN, 1, 1)
435dd85b4bSPeter Maydell     FIELD(CTRL, SELEXTCLK, 2, 1)
445dd85b4bSPeter Maydell     FIELD(CTRL, IRQEN, 3, 1)
455dd85b4bSPeter Maydell REG32(VALUE, 4)
465dd85b4bSPeter Maydell REG32(RELOAD, 8)
475dd85b4bSPeter Maydell REG32(INTSTATUS, 0xc)
485dd85b4bSPeter Maydell     FIELD(INTSTATUS, IRQ, 0, 1)
495dd85b4bSPeter Maydell REG32(PID4, 0xFD0)
505dd85b4bSPeter Maydell REG32(PID5, 0xFD4)
515dd85b4bSPeter Maydell REG32(PID6, 0xFD8)
525dd85b4bSPeter Maydell REG32(PID7, 0xFDC)
535dd85b4bSPeter Maydell REG32(PID0, 0xFE0)
545dd85b4bSPeter Maydell REG32(PID1, 0xFE4)
555dd85b4bSPeter Maydell REG32(PID2, 0xFE8)
565dd85b4bSPeter Maydell REG32(PID3, 0xFEC)
575dd85b4bSPeter Maydell REG32(CID0, 0xFF0)
585dd85b4bSPeter Maydell REG32(CID1, 0xFF4)
595dd85b4bSPeter Maydell REG32(CID2, 0xFF8)
605dd85b4bSPeter Maydell REG32(CID3, 0xFFC)
615dd85b4bSPeter Maydell 
625dd85b4bSPeter Maydell /* PID/CID values */
635dd85b4bSPeter Maydell static const int timer_id[] = {
645dd85b4bSPeter Maydell     0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
655dd85b4bSPeter Maydell     0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
665dd85b4bSPeter Maydell     0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
675dd85b4bSPeter Maydell };
685dd85b4bSPeter Maydell 
695dd85b4bSPeter Maydell static void cmsdk_apb_timer_update(CMSDKAPBTIMER *s)
705dd85b4bSPeter Maydell {
715dd85b4bSPeter Maydell     qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
725dd85b4bSPeter Maydell }
735dd85b4bSPeter Maydell 
745dd85b4bSPeter Maydell static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
755dd85b4bSPeter Maydell {
765dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
775dd85b4bSPeter Maydell     uint64_t r;
785dd85b4bSPeter Maydell 
795dd85b4bSPeter Maydell     switch (offset) {
805dd85b4bSPeter Maydell     case A_CTRL:
815dd85b4bSPeter Maydell         r = s->ctrl;
825dd85b4bSPeter Maydell         break;
835dd85b4bSPeter Maydell     case A_VALUE:
845dd85b4bSPeter Maydell         r = ptimer_get_count(s->timer);
855dd85b4bSPeter Maydell         break;
865dd85b4bSPeter Maydell     case A_RELOAD:
875dd85b4bSPeter Maydell         r = ptimer_get_limit(s->timer);
885dd85b4bSPeter Maydell         break;
895dd85b4bSPeter Maydell     case A_INTSTATUS:
905dd85b4bSPeter Maydell         r = s->intstatus;
915dd85b4bSPeter Maydell         break;
925dd85b4bSPeter Maydell     case A_PID4 ... A_CID3:
935dd85b4bSPeter Maydell         r = timer_id[(offset - A_PID4) / 4];
945dd85b4bSPeter Maydell         break;
955dd85b4bSPeter Maydell     default:
965dd85b4bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
975dd85b4bSPeter Maydell                       "CMSDK APB timer read: bad offset %x\n", (int) offset);
985dd85b4bSPeter Maydell         r = 0;
995dd85b4bSPeter Maydell         break;
1005dd85b4bSPeter Maydell     }
1015dd85b4bSPeter Maydell     trace_cmsdk_apb_timer_read(offset, r, size);
1025dd85b4bSPeter Maydell     return r;
1035dd85b4bSPeter Maydell }
1045dd85b4bSPeter Maydell 
1055dd85b4bSPeter Maydell static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
1065dd85b4bSPeter Maydell                                   unsigned size)
1075dd85b4bSPeter Maydell {
1085dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
1095dd85b4bSPeter Maydell 
1105dd85b4bSPeter Maydell     trace_cmsdk_apb_timer_write(offset, value, size);
1115dd85b4bSPeter Maydell 
1125dd85b4bSPeter Maydell     switch (offset) {
1135dd85b4bSPeter Maydell     case A_CTRL:
1145dd85b4bSPeter Maydell         if (value & 6) {
1155dd85b4bSPeter Maydell             /* Bits [1] and [2] enable using EXTIN as either clock or
1165dd85b4bSPeter Maydell              * an enable line. We don't model this.
1175dd85b4bSPeter Maydell              */
1185dd85b4bSPeter Maydell             qemu_log_mask(LOG_UNIMP,
1195dd85b4bSPeter Maydell                           "CMSDK APB timer: EXTIN input not supported\n");
1205dd85b4bSPeter Maydell         }
1215dd85b4bSPeter Maydell         s->ctrl = value & 0xf;
1225dd85b4bSPeter Maydell         if (s->ctrl & R_CTRL_EN_MASK) {
1230e256833SGuenter Roeck             ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
1245dd85b4bSPeter Maydell         } else {
1255dd85b4bSPeter Maydell             ptimer_stop(s->timer);
1265dd85b4bSPeter Maydell         }
1275dd85b4bSPeter Maydell         break;
1285dd85b4bSPeter Maydell     case A_RELOAD:
1295dd85b4bSPeter Maydell         /* Writing to reload also sets the current timer value */
1301a9b3064SPeter Maydell         if (!value) {
1311a9b3064SPeter Maydell             ptimer_stop(s->timer);
1321a9b3064SPeter Maydell         }
1335dd85b4bSPeter Maydell         ptimer_set_limit(s->timer, value, 1);
1341a9b3064SPeter Maydell         if (value && (s->ctrl & R_CTRL_EN_MASK)) {
1351a9b3064SPeter Maydell             /*
1361a9b3064SPeter Maydell              * Make sure timer is running (it might have stopped if this
1371a9b3064SPeter Maydell              * was an expired one-shot timer)
1381a9b3064SPeter Maydell              */
1391a9b3064SPeter Maydell             ptimer_run(s->timer, 0);
1401a9b3064SPeter Maydell         }
1415dd85b4bSPeter Maydell         break;
1425dd85b4bSPeter Maydell     case A_VALUE:
1431a9b3064SPeter Maydell         if (!value && !ptimer_get_limit(s->timer)) {
1441a9b3064SPeter Maydell             ptimer_stop(s->timer);
1451a9b3064SPeter Maydell         }
1465dd85b4bSPeter Maydell         ptimer_set_count(s->timer, value);
1471a9b3064SPeter Maydell         if (value && (s->ctrl & R_CTRL_EN_MASK)) {
1481a9b3064SPeter Maydell             ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
1491a9b3064SPeter Maydell         }
1505dd85b4bSPeter Maydell         break;
1515dd85b4bSPeter Maydell     case A_INTSTATUS:
1525dd85b4bSPeter Maydell         /* Just one bit, which is W1C. */
1535dd85b4bSPeter Maydell         value &= 1;
1545dd85b4bSPeter Maydell         s->intstatus &= ~value;
1555dd85b4bSPeter Maydell         cmsdk_apb_timer_update(s);
1565dd85b4bSPeter Maydell         break;
1575dd85b4bSPeter Maydell     case A_PID4 ... A_CID3:
1585dd85b4bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
1595dd85b4bSPeter Maydell                       "CMSDK APB timer write: write to RO offset 0x%x\n",
1605dd85b4bSPeter Maydell                       (int)offset);
1615dd85b4bSPeter Maydell         break;
1625dd85b4bSPeter Maydell     default:
1635dd85b4bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
1645dd85b4bSPeter Maydell                       "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
1655dd85b4bSPeter Maydell         break;
1665dd85b4bSPeter Maydell     }
1675dd85b4bSPeter Maydell }
1685dd85b4bSPeter Maydell 
1695dd85b4bSPeter Maydell static const MemoryRegionOps cmsdk_apb_timer_ops = {
1705dd85b4bSPeter Maydell     .read = cmsdk_apb_timer_read,
1715dd85b4bSPeter Maydell     .write = cmsdk_apb_timer_write,
1725dd85b4bSPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
1735dd85b4bSPeter Maydell };
1745dd85b4bSPeter Maydell 
1755dd85b4bSPeter Maydell static void cmsdk_apb_timer_tick(void *opaque)
1765dd85b4bSPeter Maydell {
1775dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
1785dd85b4bSPeter Maydell 
1795dd85b4bSPeter Maydell     if (s->ctrl & R_CTRL_IRQEN_MASK) {
1805dd85b4bSPeter Maydell         s->intstatus |= R_INTSTATUS_IRQ_MASK;
1815dd85b4bSPeter Maydell         cmsdk_apb_timer_update(s);
1825dd85b4bSPeter Maydell     }
1835dd85b4bSPeter Maydell }
1845dd85b4bSPeter Maydell 
1855dd85b4bSPeter Maydell static void cmsdk_apb_timer_reset(DeviceState *dev)
1865dd85b4bSPeter Maydell {
1875dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
1885dd85b4bSPeter Maydell 
1895dd85b4bSPeter Maydell     trace_cmsdk_apb_timer_reset();
1905dd85b4bSPeter Maydell     s->ctrl = 0;
1915dd85b4bSPeter Maydell     s->intstatus = 0;
1925dd85b4bSPeter Maydell     ptimer_stop(s->timer);
1935dd85b4bSPeter Maydell     /* Set the limit and the count */
1945dd85b4bSPeter Maydell     ptimer_set_limit(s->timer, 0, 1);
1955dd85b4bSPeter Maydell }
1965dd85b4bSPeter Maydell 
1975dd85b4bSPeter Maydell static void cmsdk_apb_timer_init(Object *obj)
1985dd85b4bSPeter Maydell {
1995dd85b4bSPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
2005dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(obj);
2015dd85b4bSPeter Maydell 
2025dd85b4bSPeter Maydell     memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
2035dd85b4bSPeter Maydell                           s, "cmsdk-apb-timer", 0x1000);
2045dd85b4bSPeter Maydell     sysbus_init_mmio(sbd, &s->iomem);
2055dd85b4bSPeter Maydell     sysbus_init_irq(sbd, &s->timerint);
2065dd85b4bSPeter Maydell }
2075dd85b4bSPeter Maydell 
2085dd85b4bSPeter Maydell static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
2095dd85b4bSPeter Maydell {
2105dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
2115dd85b4bSPeter Maydell     QEMUBH *bh;
2125dd85b4bSPeter Maydell 
2135dd85b4bSPeter Maydell     if (s->pclk_frq == 0) {
2145dd85b4bSPeter Maydell         error_setg(errp, "CMSDK APB timer: pclk-frq property must be set");
2155dd85b4bSPeter Maydell         return;
2165dd85b4bSPeter Maydell     }
2175dd85b4bSPeter Maydell 
2185dd85b4bSPeter Maydell     bh = qemu_bh_new(cmsdk_apb_timer_tick, s);
2195dd85b4bSPeter Maydell     s->timer = ptimer_init(bh,
2205dd85b4bSPeter Maydell                            PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
2216583080eSPeter Maydell                            PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
2225dd85b4bSPeter Maydell                            PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
2235dd85b4bSPeter Maydell                            PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
2245dd85b4bSPeter Maydell 
2255dd85b4bSPeter Maydell     ptimer_set_freq(s->timer, s->pclk_frq);
2265dd85b4bSPeter Maydell }
2275dd85b4bSPeter Maydell 
2285dd85b4bSPeter Maydell static const VMStateDescription cmsdk_apb_timer_vmstate = {
2295dd85b4bSPeter Maydell     .name = "cmsdk-apb-timer",
2305dd85b4bSPeter Maydell     .version_id = 1,
2315dd85b4bSPeter Maydell     .minimum_version_id = 1,
2325dd85b4bSPeter Maydell     .fields = (VMStateField[]) {
2335dd85b4bSPeter Maydell         VMSTATE_PTIMER(timer, CMSDKAPBTIMER),
2345dd85b4bSPeter Maydell         VMSTATE_UINT32(ctrl, CMSDKAPBTIMER),
2355dd85b4bSPeter Maydell         VMSTATE_UINT32(value, CMSDKAPBTIMER),
2365dd85b4bSPeter Maydell         VMSTATE_UINT32(reload, CMSDKAPBTIMER),
2375dd85b4bSPeter Maydell         VMSTATE_UINT32(intstatus, CMSDKAPBTIMER),
2385dd85b4bSPeter Maydell         VMSTATE_END_OF_LIST()
2395dd85b4bSPeter Maydell     }
2405dd85b4bSPeter Maydell };
2415dd85b4bSPeter Maydell 
2425dd85b4bSPeter Maydell static Property cmsdk_apb_timer_properties[] = {
2435dd85b4bSPeter Maydell     DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBTIMER, pclk_frq, 0),
2445dd85b4bSPeter Maydell     DEFINE_PROP_END_OF_LIST(),
2455dd85b4bSPeter Maydell };
2465dd85b4bSPeter Maydell 
2475dd85b4bSPeter Maydell static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
2485dd85b4bSPeter Maydell {
2495dd85b4bSPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
2505dd85b4bSPeter Maydell 
2515dd85b4bSPeter Maydell     dc->realize = cmsdk_apb_timer_realize;
2525dd85b4bSPeter Maydell     dc->vmsd = &cmsdk_apb_timer_vmstate;
2535dd85b4bSPeter Maydell     dc->reset = cmsdk_apb_timer_reset;
2545dd85b4bSPeter Maydell     dc->props = cmsdk_apb_timer_properties;
2555dd85b4bSPeter Maydell }
2565dd85b4bSPeter Maydell 
2575dd85b4bSPeter Maydell static const TypeInfo cmsdk_apb_timer_info = {
2585dd85b4bSPeter Maydell     .name = TYPE_CMSDK_APB_TIMER,
2595dd85b4bSPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
2605dd85b4bSPeter Maydell     .instance_size = sizeof(CMSDKAPBTIMER),
2615dd85b4bSPeter Maydell     .instance_init = cmsdk_apb_timer_init,
2625dd85b4bSPeter Maydell     .class_init = cmsdk_apb_timer_class_init,
2635dd85b4bSPeter Maydell };
2645dd85b4bSPeter Maydell 
2655dd85b4bSPeter Maydell static void cmsdk_apb_timer_register_types(void)
2665dd85b4bSPeter Maydell {
2675dd85b4bSPeter Maydell     type_register_static(&cmsdk_apb_timer_info);
2685dd85b4bSPeter Maydell }
2695dd85b4bSPeter Maydell 
2705dd85b4bSPeter Maydell type_init(cmsdk_apb_timer_register_types);
271