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"
387cc378edSPeter Maydell #include "hw/qdev-clock.h"
395dd85b4bSPeter Maydell #include "hw/timer/cmsdk-apb-timer.h"
40d6454270SMarkus Armbruster #include "migration/vmstate.h"
415dd85b4bSPeter Maydell
425dd85b4bSPeter Maydell REG32(CTRL, 0)
435dd85b4bSPeter Maydell FIELD(CTRL, EN, 0, 1)
445dd85b4bSPeter Maydell FIELD(CTRL, SELEXTEN, 1, 1)
455dd85b4bSPeter Maydell FIELD(CTRL, SELEXTCLK, 2, 1)
465dd85b4bSPeter Maydell FIELD(CTRL, IRQEN, 3, 1)
475dd85b4bSPeter Maydell REG32(VALUE, 4)
485dd85b4bSPeter Maydell REG32(RELOAD, 8)
495dd85b4bSPeter Maydell REG32(INTSTATUS, 0xc)
505dd85b4bSPeter Maydell FIELD(INTSTATUS, IRQ, 0, 1)
515dd85b4bSPeter Maydell REG32(PID4, 0xFD0)
525dd85b4bSPeter Maydell REG32(PID5, 0xFD4)
535dd85b4bSPeter Maydell REG32(PID6, 0xFD8)
545dd85b4bSPeter Maydell REG32(PID7, 0xFDC)
555dd85b4bSPeter Maydell REG32(PID0, 0xFE0)
565dd85b4bSPeter Maydell REG32(PID1, 0xFE4)
575dd85b4bSPeter Maydell REG32(PID2, 0xFE8)
585dd85b4bSPeter Maydell REG32(PID3, 0xFEC)
595dd85b4bSPeter Maydell REG32(CID0, 0xFF0)
605dd85b4bSPeter Maydell REG32(CID1, 0xFF4)
615dd85b4bSPeter Maydell REG32(CID2, 0xFF8)
625dd85b4bSPeter Maydell REG32(CID3, 0xFFC)
635dd85b4bSPeter Maydell
645dd85b4bSPeter Maydell /* PID/CID values */
655dd85b4bSPeter Maydell static const int timer_id[] = {
665dd85b4bSPeter Maydell 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
675dd85b4bSPeter Maydell 0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
685dd85b4bSPeter Maydell 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
695dd85b4bSPeter Maydell };
705dd85b4bSPeter Maydell
cmsdk_apb_timer_update(CMSDKAPBTimer * s)71b56d351eSPeter Maydell static void cmsdk_apb_timer_update(CMSDKAPBTimer *s)
725dd85b4bSPeter Maydell {
735dd85b4bSPeter Maydell qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
745dd85b4bSPeter Maydell }
755dd85b4bSPeter Maydell
cmsdk_apb_timer_read(void * opaque,hwaddr offset,unsigned size)765dd85b4bSPeter Maydell static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
775dd85b4bSPeter Maydell {
78b56d351eSPeter Maydell CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
795dd85b4bSPeter Maydell uint64_t r;
805dd85b4bSPeter Maydell
815dd85b4bSPeter Maydell switch (offset) {
825dd85b4bSPeter Maydell case A_CTRL:
835dd85b4bSPeter Maydell r = s->ctrl;
845dd85b4bSPeter Maydell break;
855dd85b4bSPeter Maydell case A_VALUE:
865dd85b4bSPeter Maydell r = ptimer_get_count(s->timer);
875dd85b4bSPeter Maydell break;
885dd85b4bSPeter Maydell case A_RELOAD:
895dd85b4bSPeter Maydell r = ptimer_get_limit(s->timer);
905dd85b4bSPeter Maydell break;
915dd85b4bSPeter Maydell case A_INTSTATUS:
925dd85b4bSPeter Maydell r = s->intstatus;
935dd85b4bSPeter Maydell break;
945dd85b4bSPeter Maydell case A_PID4 ... A_CID3:
955dd85b4bSPeter Maydell r = timer_id[(offset - A_PID4) / 4];
965dd85b4bSPeter Maydell break;
975dd85b4bSPeter Maydell default:
985dd85b4bSPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
995dd85b4bSPeter Maydell "CMSDK APB timer read: bad offset %x\n", (int) offset);
1005dd85b4bSPeter Maydell r = 0;
1015dd85b4bSPeter Maydell break;
1025dd85b4bSPeter Maydell }
1035dd85b4bSPeter Maydell trace_cmsdk_apb_timer_read(offset, r, size);
1045dd85b4bSPeter Maydell return r;
1055dd85b4bSPeter Maydell }
1065dd85b4bSPeter Maydell
cmsdk_apb_timer_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)1075dd85b4bSPeter Maydell static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
1085dd85b4bSPeter Maydell unsigned size)
1095dd85b4bSPeter Maydell {
110b56d351eSPeter Maydell CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
1115dd85b4bSPeter Maydell
1125dd85b4bSPeter Maydell trace_cmsdk_apb_timer_write(offset, value, size);
1135dd85b4bSPeter Maydell
1145dd85b4bSPeter Maydell switch (offset) {
1155dd85b4bSPeter Maydell case A_CTRL:
1165dd85b4bSPeter Maydell if (value & 6) {
1175dd85b4bSPeter Maydell /* Bits [1] and [2] enable using EXTIN as either clock or
1185dd85b4bSPeter Maydell * an enable line. We don't model this.
1195dd85b4bSPeter Maydell */
1205dd85b4bSPeter Maydell qemu_log_mask(LOG_UNIMP,
1215dd85b4bSPeter Maydell "CMSDK APB timer: EXTIN input not supported\n");
1225dd85b4bSPeter Maydell }
1235dd85b4bSPeter Maydell s->ctrl = value & 0xf;
12419c12fe9SPeter Maydell ptimer_transaction_begin(s->timer);
1255dd85b4bSPeter Maydell if (s->ctrl & R_CTRL_EN_MASK) {
1260e256833SGuenter Roeck ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
1275dd85b4bSPeter Maydell } else {
1285dd85b4bSPeter Maydell ptimer_stop(s->timer);
1295dd85b4bSPeter Maydell }
13019c12fe9SPeter Maydell ptimer_transaction_commit(s->timer);
1315dd85b4bSPeter Maydell break;
1325dd85b4bSPeter Maydell case A_RELOAD:
1335dd85b4bSPeter Maydell /* Writing to reload also sets the current timer value */
13419c12fe9SPeter Maydell ptimer_transaction_begin(s->timer);
1351a9b3064SPeter Maydell if (!value) {
1361a9b3064SPeter Maydell ptimer_stop(s->timer);
1371a9b3064SPeter Maydell }
1385dd85b4bSPeter Maydell ptimer_set_limit(s->timer, value, 1);
1391a9b3064SPeter Maydell if (value && (s->ctrl & R_CTRL_EN_MASK)) {
1401a9b3064SPeter Maydell /*
1411a9b3064SPeter Maydell * Make sure timer is running (it might have stopped if this
1421a9b3064SPeter Maydell * was an expired one-shot timer)
1431a9b3064SPeter Maydell */
1441a9b3064SPeter Maydell ptimer_run(s->timer, 0);
1451a9b3064SPeter Maydell }
14619c12fe9SPeter Maydell ptimer_transaction_commit(s->timer);
1475dd85b4bSPeter Maydell break;
1485dd85b4bSPeter Maydell case A_VALUE:
14919c12fe9SPeter Maydell ptimer_transaction_begin(s->timer);
1501a9b3064SPeter Maydell if (!value && !ptimer_get_limit(s->timer)) {
1511a9b3064SPeter Maydell ptimer_stop(s->timer);
1521a9b3064SPeter Maydell }
1535dd85b4bSPeter Maydell ptimer_set_count(s->timer, value);
1541a9b3064SPeter Maydell if (value && (s->ctrl & R_CTRL_EN_MASK)) {
1551a9b3064SPeter Maydell ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
1561a9b3064SPeter Maydell }
15719c12fe9SPeter Maydell ptimer_transaction_commit(s->timer);
1585dd85b4bSPeter Maydell break;
1595dd85b4bSPeter Maydell case A_INTSTATUS:
1605dd85b4bSPeter Maydell /* Just one bit, which is W1C. */
1615dd85b4bSPeter Maydell value &= 1;
1625dd85b4bSPeter Maydell s->intstatus &= ~value;
1635dd85b4bSPeter Maydell cmsdk_apb_timer_update(s);
1645dd85b4bSPeter Maydell break;
1655dd85b4bSPeter Maydell case A_PID4 ... A_CID3:
1665dd85b4bSPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
1675dd85b4bSPeter Maydell "CMSDK APB timer write: write to RO offset 0x%x\n",
1685dd85b4bSPeter Maydell (int)offset);
1695dd85b4bSPeter Maydell break;
1705dd85b4bSPeter Maydell default:
1715dd85b4bSPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
1725dd85b4bSPeter Maydell "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
1735dd85b4bSPeter Maydell break;
1745dd85b4bSPeter Maydell }
1755dd85b4bSPeter Maydell }
1765dd85b4bSPeter Maydell
1775dd85b4bSPeter Maydell static const MemoryRegionOps cmsdk_apb_timer_ops = {
1785dd85b4bSPeter Maydell .read = cmsdk_apb_timer_read,
1795dd85b4bSPeter Maydell .write = cmsdk_apb_timer_write,
1805dd85b4bSPeter Maydell .endianness = DEVICE_LITTLE_ENDIAN,
1815dd85b4bSPeter Maydell };
1825dd85b4bSPeter Maydell
cmsdk_apb_timer_tick(void * opaque)1835dd85b4bSPeter Maydell static void cmsdk_apb_timer_tick(void *opaque)
1845dd85b4bSPeter Maydell {
185b56d351eSPeter Maydell CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
1865dd85b4bSPeter Maydell
1875dd85b4bSPeter Maydell if (s->ctrl & R_CTRL_IRQEN_MASK) {
1885dd85b4bSPeter Maydell s->intstatus |= R_INTSTATUS_IRQ_MASK;
1895dd85b4bSPeter Maydell cmsdk_apb_timer_update(s);
1905dd85b4bSPeter Maydell }
1915dd85b4bSPeter Maydell }
1925dd85b4bSPeter Maydell
cmsdk_apb_timer_reset(DeviceState * dev)1935dd85b4bSPeter Maydell static void cmsdk_apb_timer_reset(DeviceState *dev)
1945dd85b4bSPeter Maydell {
195b56d351eSPeter Maydell CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
1965dd85b4bSPeter Maydell
1975dd85b4bSPeter Maydell trace_cmsdk_apb_timer_reset();
1985dd85b4bSPeter Maydell s->ctrl = 0;
1995dd85b4bSPeter Maydell s->intstatus = 0;
20019c12fe9SPeter Maydell ptimer_transaction_begin(s->timer);
2015dd85b4bSPeter Maydell ptimer_stop(s->timer);
2025dd85b4bSPeter Maydell /* Set the limit and the count */
2035dd85b4bSPeter Maydell ptimer_set_limit(s->timer, 0, 1);
20419c12fe9SPeter Maydell ptimer_transaction_commit(s->timer);
2055dd85b4bSPeter Maydell }
2065dd85b4bSPeter Maydell
cmsdk_apb_timer_clk_update(void * opaque,ClockEvent event)2075ee0abedSPeter Maydell static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
2085e066562SPeter Maydell {
2095e066562SPeter Maydell CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
2105e066562SPeter Maydell
2115e066562SPeter Maydell ptimer_transaction_begin(s->timer);
2125e066562SPeter Maydell ptimer_set_period_from_clock(s->timer, s->pclk, 1);
2135e066562SPeter Maydell ptimer_transaction_commit(s->timer);
2145e066562SPeter Maydell }
2155e066562SPeter Maydell
cmsdk_apb_timer_init(Object * obj)2165dd85b4bSPeter Maydell static void cmsdk_apb_timer_init(Object *obj)
2175dd85b4bSPeter Maydell {
2185dd85b4bSPeter Maydell SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
219b56d351eSPeter Maydell CMSDKAPBTimer *s = CMSDK_APB_TIMER(obj);
2205dd85b4bSPeter Maydell
2215dd85b4bSPeter Maydell memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
2225dd85b4bSPeter Maydell s, "cmsdk-apb-timer", 0x1000);
2235dd85b4bSPeter Maydell sysbus_init_mmio(sbd, &s->iomem);
2245dd85b4bSPeter Maydell sysbus_init_irq(sbd, &s->timerint);
2255e066562SPeter Maydell s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
2265ee0abedSPeter Maydell cmsdk_apb_timer_clk_update, s, ClockUpdate);
2275dd85b4bSPeter Maydell }
2285dd85b4bSPeter Maydell
cmsdk_apb_timer_realize(DeviceState * dev,Error ** errp)2295dd85b4bSPeter Maydell static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
2305dd85b4bSPeter Maydell {
231b56d351eSPeter Maydell CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
2325dd85b4bSPeter Maydell
2335e066562SPeter Maydell if (!clock_has_source(s->pclk)) {
2345e066562SPeter Maydell error_setg(errp, "CMSDK APB timer: pclk clock must be connected");
2355dd85b4bSPeter Maydell return;
2365dd85b4bSPeter Maydell }
2375dd85b4bSPeter Maydell
23819c12fe9SPeter Maydell s->timer = ptimer_init(cmsdk_apb_timer_tick, s,
2395dd85b4bSPeter Maydell PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
2406583080eSPeter Maydell PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
2415dd85b4bSPeter Maydell PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
2425dd85b4bSPeter Maydell PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
2435dd85b4bSPeter Maydell
24419c12fe9SPeter Maydell ptimer_transaction_begin(s->timer);
2455e066562SPeter Maydell ptimer_set_period_from_clock(s->timer, s->pclk, 1);
24619c12fe9SPeter Maydell ptimer_transaction_commit(s->timer);
2475dd85b4bSPeter Maydell }
2485dd85b4bSPeter Maydell
2495dd85b4bSPeter Maydell static const VMStateDescription cmsdk_apb_timer_vmstate = {
2505dd85b4bSPeter Maydell .name = "cmsdk-apb-timer",
2517cc378edSPeter Maydell .version_id = 2,
2527cc378edSPeter Maydell .minimum_version_id = 2,
253ba324b3fSRichard Henderson .fields = (const VMStateField[]) {
254b56d351eSPeter Maydell VMSTATE_PTIMER(timer, CMSDKAPBTimer),
2557cc378edSPeter Maydell VMSTATE_CLOCK(pclk, CMSDKAPBTimer),
256b56d351eSPeter Maydell VMSTATE_UINT32(ctrl, CMSDKAPBTimer),
257b56d351eSPeter Maydell VMSTATE_UINT32(value, CMSDKAPBTimer),
258b56d351eSPeter Maydell VMSTATE_UINT32(reload, CMSDKAPBTimer),
259b56d351eSPeter Maydell VMSTATE_UINT32(intstatus, CMSDKAPBTimer),
2605dd85b4bSPeter Maydell VMSTATE_END_OF_LIST()
2615dd85b4bSPeter Maydell }
2625dd85b4bSPeter Maydell };
2635dd85b4bSPeter Maydell
cmsdk_apb_timer_class_init(ObjectClass * klass,void * data)2645dd85b4bSPeter Maydell static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
2655dd85b4bSPeter Maydell {
2665dd85b4bSPeter Maydell DeviceClass *dc = DEVICE_CLASS(klass);
2675dd85b4bSPeter Maydell
2685dd85b4bSPeter Maydell dc->realize = cmsdk_apb_timer_realize;
2695dd85b4bSPeter Maydell dc->vmsd = &cmsdk_apb_timer_vmstate;
270*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, cmsdk_apb_timer_reset);
2715dd85b4bSPeter Maydell }
2725dd85b4bSPeter Maydell
2735dd85b4bSPeter Maydell static const TypeInfo cmsdk_apb_timer_info = {
2745dd85b4bSPeter Maydell .name = TYPE_CMSDK_APB_TIMER,
2755dd85b4bSPeter Maydell .parent = TYPE_SYS_BUS_DEVICE,
276b56d351eSPeter Maydell .instance_size = sizeof(CMSDKAPBTimer),
2775dd85b4bSPeter Maydell .instance_init = cmsdk_apb_timer_init,
2785dd85b4bSPeter Maydell .class_init = cmsdk_apb_timer_class_init,
2795dd85b4bSPeter Maydell };
2805dd85b4bSPeter Maydell
cmsdk_apb_timer_register_types(void)2815dd85b4bSPeter Maydell static void cmsdk_apb_timer_register_types(void)
2825dd85b4bSPeter Maydell {
2835dd85b4bSPeter Maydell type_register_static(&cmsdk_apb_timer_info);
2845dd85b4bSPeter Maydell }
2855dd85b4bSPeter Maydell
2865dd85b4bSPeter Maydell type_init(cmsdk_apb_timer_register_types);
287