xref: /qemu/hw/timer/cmsdk-apb-timer.c (revision 19c12fe93ab9c65b56c7dfd467799a63c28d4bbd)
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"
320b8fa32fSMarkus Armbruster #include "qemu/module.h"
335dd85b4bSPeter Maydell #include "qapi/error.h"
345dd85b4bSPeter Maydell #include "trace.h"
355dd85b4bSPeter Maydell #include "hw/sysbus.h"
3664552b6bSMarkus Armbruster #include "hw/irq.h"
375dd85b4bSPeter Maydell #include "hw/registerfields.h"
385dd85b4bSPeter Maydell #include "hw/timer/cmsdk-apb-timer.h"
39d6454270SMarkus Armbruster #include "migration/vmstate.h"
405dd85b4bSPeter Maydell 
415dd85b4bSPeter Maydell REG32(CTRL, 0)
425dd85b4bSPeter Maydell     FIELD(CTRL, EN, 0, 1)
435dd85b4bSPeter Maydell     FIELD(CTRL, SELEXTEN, 1, 1)
445dd85b4bSPeter Maydell     FIELD(CTRL, SELEXTCLK, 2, 1)
455dd85b4bSPeter Maydell     FIELD(CTRL, IRQEN, 3, 1)
465dd85b4bSPeter Maydell REG32(VALUE, 4)
475dd85b4bSPeter Maydell REG32(RELOAD, 8)
485dd85b4bSPeter Maydell REG32(INTSTATUS, 0xc)
495dd85b4bSPeter Maydell     FIELD(INTSTATUS, IRQ, 0, 1)
505dd85b4bSPeter Maydell REG32(PID4, 0xFD0)
515dd85b4bSPeter Maydell REG32(PID5, 0xFD4)
525dd85b4bSPeter Maydell REG32(PID6, 0xFD8)
535dd85b4bSPeter Maydell REG32(PID7, 0xFDC)
545dd85b4bSPeter Maydell REG32(PID0, 0xFE0)
555dd85b4bSPeter Maydell REG32(PID1, 0xFE4)
565dd85b4bSPeter Maydell REG32(PID2, 0xFE8)
575dd85b4bSPeter Maydell REG32(PID3, 0xFEC)
585dd85b4bSPeter Maydell REG32(CID0, 0xFF0)
595dd85b4bSPeter Maydell REG32(CID1, 0xFF4)
605dd85b4bSPeter Maydell REG32(CID2, 0xFF8)
615dd85b4bSPeter Maydell REG32(CID3, 0xFFC)
625dd85b4bSPeter Maydell 
635dd85b4bSPeter Maydell /* PID/CID values */
645dd85b4bSPeter Maydell static const int timer_id[] = {
655dd85b4bSPeter Maydell     0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
665dd85b4bSPeter Maydell     0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
675dd85b4bSPeter Maydell     0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
685dd85b4bSPeter Maydell };
695dd85b4bSPeter Maydell 
705dd85b4bSPeter Maydell static void cmsdk_apb_timer_update(CMSDKAPBTIMER *s)
715dd85b4bSPeter Maydell {
725dd85b4bSPeter Maydell     qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
735dd85b4bSPeter Maydell }
745dd85b4bSPeter Maydell 
755dd85b4bSPeter Maydell static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
765dd85b4bSPeter Maydell {
775dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
785dd85b4bSPeter Maydell     uint64_t r;
795dd85b4bSPeter Maydell 
805dd85b4bSPeter Maydell     switch (offset) {
815dd85b4bSPeter Maydell     case A_CTRL:
825dd85b4bSPeter Maydell         r = s->ctrl;
835dd85b4bSPeter Maydell         break;
845dd85b4bSPeter Maydell     case A_VALUE:
855dd85b4bSPeter Maydell         r = ptimer_get_count(s->timer);
865dd85b4bSPeter Maydell         break;
875dd85b4bSPeter Maydell     case A_RELOAD:
885dd85b4bSPeter Maydell         r = ptimer_get_limit(s->timer);
895dd85b4bSPeter Maydell         break;
905dd85b4bSPeter Maydell     case A_INTSTATUS:
915dd85b4bSPeter Maydell         r = s->intstatus;
925dd85b4bSPeter Maydell         break;
935dd85b4bSPeter Maydell     case A_PID4 ... A_CID3:
945dd85b4bSPeter Maydell         r = timer_id[(offset - A_PID4) / 4];
955dd85b4bSPeter Maydell         break;
965dd85b4bSPeter Maydell     default:
975dd85b4bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
985dd85b4bSPeter Maydell                       "CMSDK APB timer read: bad offset %x\n", (int) offset);
995dd85b4bSPeter Maydell         r = 0;
1005dd85b4bSPeter Maydell         break;
1015dd85b4bSPeter Maydell     }
1025dd85b4bSPeter Maydell     trace_cmsdk_apb_timer_read(offset, r, size);
1035dd85b4bSPeter Maydell     return r;
1045dd85b4bSPeter Maydell }
1055dd85b4bSPeter Maydell 
1065dd85b4bSPeter Maydell static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
1075dd85b4bSPeter Maydell                                   unsigned size)
1085dd85b4bSPeter Maydell {
1095dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
1105dd85b4bSPeter Maydell 
1115dd85b4bSPeter Maydell     trace_cmsdk_apb_timer_write(offset, value, size);
1125dd85b4bSPeter Maydell 
1135dd85b4bSPeter Maydell     switch (offset) {
1145dd85b4bSPeter Maydell     case A_CTRL:
1155dd85b4bSPeter Maydell         if (value & 6) {
1165dd85b4bSPeter Maydell             /* Bits [1] and [2] enable using EXTIN as either clock or
1175dd85b4bSPeter Maydell              * an enable line. We don't model this.
1185dd85b4bSPeter Maydell              */
1195dd85b4bSPeter Maydell             qemu_log_mask(LOG_UNIMP,
1205dd85b4bSPeter Maydell                           "CMSDK APB timer: EXTIN input not supported\n");
1215dd85b4bSPeter Maydell         }
1225dd85b4bSPeter Maydell         s->ctrl = value & 0xf;
123*19c12fe9SPeter Maydell         ptimer_transaction_begin(s->timer);
1245dd85b4bSPeter Maydell         if (s->ctrl & R_CTRL_EN_MASK) {
1250e256833SGuenter Roeck             ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
1265dd85b4bSPeter Maydell         } else {
1275dd85b4bSPeter Maydell             ptimer_stop(s->timer);
1285dd85b4bSPeter Maydell         }
129*19c12fe9SPeter Maydell         ptimer_transaction_commit(s->timer);
1305dd85b4bSPeter Maydell         break;
1315dd85b4bSPeter Maydell     case A_RELOAD:
1325dd85b4bSPeter Maydell         /* Writing to reload also sets the current timer value */
133*19c12fe9SPeter Maydell         ptimer_transaction_begin(s->timer);
1341a9b3064SPeter Maydell         if (!value) {
1351a9b3064SPeter Maydell             ptimer_stop(s->timer);
1361a9b3064SPeter Maydell         }
1375dd85b4bSPeter Maydell         ptimer_set_limit(s->timer, value, 1);
1381a9b3064SPeter Maydell         if (value && (s->ctrl & R_CTRL_EN_MASK)) {
1391a9b3064SPeter Maydell             /*
1401a9b3064SPeter Maydell              * Make sure timer is running (it might have stopped if this
1411a9b3064SPeter Maydell              * was an expired one-shot timer)
1421a9b3064SPeter Maydell              */
1431a9b3064SPeter Maydell             ptimer_run(s->timer, 0);
1441a9b3064SPeter Maydell         }
145*19c12fe9SPeter Maydell         ptimer_transaction_commit(s->timer);
1465dd85b4bSPeter Maydell         break;
1475dd85b4bSPeter Maydell     case A_VALUE:
148*19c12fe9SPeter Maydell         ptimer_transaction_begin(s->timer);
1491a9b3064SPeter Maydell         if (!value && !ptimer_get_limit(s->timer)) {
1501a9b3064SPeter Maydell             ptimer_stop(s->timer);
1511a9b3064SPeter Maydell         }
1525dd85b4bSPeter Maydell         ptimer_set_count(s->timer, value);
1531a9b3064SPeter Maydell         if (value && (s->ctrl & R_CTRL_EN_MASK)) {
1541a9b3064SPeter Maydell             ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
1551a9b3064SPeter Maydell         }
156*19c12fe9SPeter Maydell         ptimer_transaction_commit(s->timer);
1575dd85b4bSPeter Maydell         break;
1585dd85b4bSPeter Maydell     case A_INTSTATUS:
1595dd85b4bSPeter Maydell         /* Just one bit, which is W1C. */
1605dd85b4bSPeter Maydell         value &= 1;
1615dd85b4bSPeter Maydell         s->intstatus &= ~value;
1625dd85b4bSPeter Maydell         cmsdk_apb_timer_update(s);
1635dd85b4bSPeter Maydell         break;
1645dd85b4bSPeter Maydell     case A_PID4 ... A_CID3:
1655dd85b4bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
1665dd85b4bSPeter Maydell                       "CMSDK APB timer write: write to RO offset 0x%x\n",
1675dd85b4bSPeter Maydell                       (int)offset);
1685dd85b4bSPeter Maydell         break;
1695dd85b4bSPeter Maydell     default:
1705dd85b4bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
1715dd85b4bSPeter Maydell                       "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
1725dd85b4bSPeter Maydell         break;
1735dd85b4bSPeter Maydell     }
1745dd85b4bSPeter Maydell }
1755dd85b4bSPeter Maydell 
1765dd85b4bSPeter Maydell static const MemoryRegionOps cmsdk_apb_timer_ops = {
1775dd85b4bSPeter Maydell     .read = cmsdk_apb_timer_read,
1785dd85b4bSPeter Maydell     .write = cmsdk_apb_timer_write,
1795dd85b4bSPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
1805dd85b4bSPeter Maydell };
1815dd85b4bSPeter Maydell 
1825dd85b4bSPeter Maydell static void cmsdk_apb_timer_tick(void *opaque)
1835dd85b4bSPeter Maydell {
1845dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
1855dd85b4bSPeter Maydell 
1865dd85b4bSPeter Maydell     if (s->ctrl & R_CTRL_IRQEN_MASK) {
1875dd85b4bSPeter Maydell         s->intstatus |= R_INTSTATUS_IRQ_MASK;
1885dd85b4bSPeter Maydell         cmsdk_apb_timer_update(s);
1895dd85b4bSPeter Maydell     }
1905dd85b4bSPeter Maydell }
1915dd85b4bSPeter Maydell 
1925dd85b4bSPeter Maydell static void cmsdk_apb_timer_reset(DeviceState *dev)
1935dd85b4bSPeter Maydell {
1945dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
1955dd85b4bSPeter Maydell 
1965dd85b4bSPeter Maydell     trace_cmsdk_apb_timer_reset();
1975dd85b4bSPeter Maydell     s->ctrl = 0;
1985dd85b4bSPeter Maydell     s->intstatus = 0;
199*19c12fe9SPeter Maydell     ptimer_transaction_begin(s->timer);
2005dd85b4bSPeter Maydell     ptimer_stop(s->timer);
2015dd85b4bSPeter Maydell     /* Set the limit and the count */
2025dd85b4bSPeter Maydell     ptimer_set_limit(s->timer, 0, 1);
203*19c12fe9SPeter Maydell     ptimer_transaction_commit(s->timer);
2045dd85b4bSPeter Maydell }
2055dd85b4bSPeter Maydell 
2065dd85b4bSPeter Maydell static void cmsdk_apb_timer_init(Object *obj)
2075dd85b4bSPeter Maydell {
2085dd85b4bSPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
2095dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(obj);
2105dd85b4bSPeter Maydell 
2115dd85b4bSPeter Maydell     memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
2125dd85b4bSPeter Maydell                           s, "cmsdk-apb-timer", 0x1000);
2135dd85b4bSPeter Maydell     sysbus_init_mmio(sbd, &s->iomem);
2145dd85b4bSPeter Maydell     sysbus_init_irq(sbd, &s->timerint);
2155dd85b4bSPeter Maydell }
2165dd85b4bSPeter Maydell 
2175dd85b4bSPeter Maydell static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
2185dd85b4bSPeter Maydell {
2195dd85b4bSPeter Maydell     CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
2205dd85b4bSPeter Maydell 
2215dd85b4bSPeter Maydell     if (s->pclk_frq == 0) {
2225dd85b4bSPeter Maydell         error_setg(errp, "CMSDK APB timer: pclk-frq property must be set");
2235dd85b4bSPeter Maydell         return;
2245dd85b4bSPeter Maydell     }
2255dd85b4bSPeter Maydell 
226*19c12fe9SPeter Maydell     s->timer = ptimer_init(cmsdk_apb_timer_tick, s,
2275dd85b4bSPeter Maydell                            PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
2286583080eSPeter Maydell                            PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
2295dd85b4bSPeter Maydell                            PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
2305dd85b4bSPeter Maydell                            PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
2315dd85b4bSPeter Maydell 
232*19c12fe9SPeter Maydell     ptimer_transaction_begin(s->timer);
2335dd85b4bSPeter Maydell     ptimer_set_freq(s->timer, s->pclk_frq);
234*19c12fe9SPeter Maydell     ptimer_transaction_commit(s->timer);
2355dd85b4bSPeter Maydell }
2365dd85b4bSPeter Maydell 
2375dd85b4bSPeter Maydell static const VMStateDescription cmsdk_apb_timer_vmstate = {
2385dd85b4bSPeter Maydell     .name = "cmsdk-apb-timer",
2395dd85b4bSPeter Maydell     .version_id = 1,
2405dd85b4bSPeter Maydell     .minimum_version_id = 1,
2415dd85b4bSPeter Maydell     .fields = (VMStateField[]) {
2425dd85b4bSPeter Maydell         VMSTATE_PTIMER(timer, CMSDKAPBTIMER),
2435dd85b4bSPeter Maydell         VMSTATE_UINT32(ctrl, CMSDKAPBTIMER),
2445dd85b4bSPeter Maydell         VMSTATE_UINT32(value, CMSDKAPBTIMER),
2455dd85b4bSPeter Maydell         VMSTATE_UINT32(reload, CMSDKAPBTIMER),
2465dd85b4bSPeter Maydell         VMSTATE_UINT32(intstatus, CMSDKAPBTIMER),
2475dd85b4bSPeter Maydell         VMSTATE_END_OF_LIST()
2485dd85b4bSPeter Maydell     }
2495dd85b4bSPeter Maydell };
2505dd85b4bSPeter Maydell 
2515dd85b4bSPeter Maydell static Property cmsdk_apb_timer_properties[] = {
2525dd85b4bSPeter Maydell     DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBTIMER, pclk_frq, 0),
2535dd85b4bSPeter Maydell     DEFINE_PROP_END_OF_LIST(),
2545dd85b4bSPeter Maydell };
2555dd85b4bSPeter Maydell 
2565dd85b4bSPeter Maydell static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
2575dd85b4bSPeter Maydell {
2585dd85b4bSPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
2595dd85b4bSPeter Maydell 
2605dd85b4bSPeter Maydell     dc->realize = cmsdk_apb_timer_realize;
2615dd85b4bSPeter Maydell     dc->vmsd = &cmsdk_apb_timer_vmstate;
2625dd85b4bSPeter Maydell     dc->reset = cmsdk_apb_timer_reset;
2635dd85b4bSPeter Maydell     dc->props = cmsdk_apb_timer_properties;
2645dd85b4bSPeter Maydell }
2655dd85b4bSPeter Maydell 
2665dd85b4bSPeter Maydell static const TypeInfo cmsdk_apb_timer_info = {
2675dd85b4bSPeter Maydell     .name = TYPE_CMSDK_APB_TIMER,
2685dd85b4bSPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
2695dd85b4bSPeter Maydell     .instance_size = sizeof(CMSDKAPBTIMER),
2705dd85b4bSPeter Maydell     .instance_init = cmsdk_apb_timer_init,
2715dd85b4bSPeter Maydell     .class_init = cmsdk_apb_timer_class_init,
2725dd85b4bSPeter Maydell };
2735dd85b4bSPeter Maydell 
2745dd85b4bSPeter Maydell static void cmsdk_apb_timer_register_types(void)
2755dd85b4bSPeter Maydell {
2765dd85b4bSPeter Maydell     type_register_static(&cmsdk_apb_timer_info);
2775dd85b4bSPeter Maydell }
2785dd85b4bSPeter Maydell 
2795dd85b4bSPeter Maydell type_init(cmsdk_apb_timer_register_types);
280