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