14f4c6206SPeter Maydell /* 24f4c6206SPeter Maydell * ARM CMSDK APB dual-timer emulation 34f4c6206SPeter Maydell * 44f4c6206SPeter Maydell * Copyright (c) 2018 Linaro Limited 54f4c6206SPeter Maydell * Written by Peter Maydell 64f4c6206SPeter Maydell * 74f4c6206SPeter Maydell * This program is free software; you can redistribute it and/or modify 84f4c6206SPeter Maydell * it under the terms of the GNU General Public License version 2 or 94f4c6206SPeter Maydell * (at your option) any later version. 104f4c6206SPeter Maydell */ 114f4c6206SPeter Maydell 124f4c6206SPeter Maydell /* 134f4c6206SPeter Maydell * This is a model of the "APB dual-input timer" which is part of the Cortex-M 144f4c6206SPeter Maydell * System Design Kit (CMSDK) and documented in the Cortex-M System 154f4c6206SPeter Maydell * Design Kit Technical Reference Manual (ARM DDI0479C): 164f4c6206SPeter Maydell * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit 174f4c6206SPeter Maydell */ 184f4c6206SPeter Maydell 194f4c6206SPeter Maydell #include "qemu/osdep.h" 204f4c6206SPeter Maydell #include "qemu/log.h" 214f4c6206SPeter Maydell #include "trace.h" 224f4c6206SPeter Maydell #include "qapi/error.h" 230b8fa32fSMarkus Armbruster #include "qemu/module.h" 244f4c6206SPeter Maydell #include "hw/sysbus.h" 2564552b6bSMarkus Armbruster #include "hw/irq.h" 26a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 274f4c6206SPeter Maydell #include "hw/registerfields.h" 28*55fd0f84SPeter Maydell #include "hw/qdev-clock.h" 294f4c6206SPeter Maydell #include "hw/timer/cmsdk-apb-dualtimer.h" 30d6454270SMarkus Armbruster #include "migration/vmstate.h" 314f4c6206SPeter Maydell 324f4c6206SPeter Maydell REG32(TIMER1LOAD, 0x0) 334f4c6206SPeter Maydell REG32(TIMER1VALUE, 0x4) 344f4c6206SPeter Maydell REG32(TIMER1CONTROL, 0x8) 354f4c6206SPeter Maydell FIELD(CONTROL, ONESHOT, 0, 1) 364f4c6206SPeter Maydell FIELD(CONTROL, SIZE, 1, 1) 374f4c6206SPeter Maydell FIELD(CONTROL, PRESCALE, 2, 2) 384f4c6206SPeter Maydell FIELD(CONTROL, INTEN, 5, 1) 394f4c6206SPeter Maydell FIELD(CONTROL, MODE, 6, 1) 404f4c6206SPeter Maydell FIELD(CONTROL, ENABLE, 7, 1) 414f4c6206SPeter Maydell #define R_CONTROL_VALID_MASK (R_CONTROL_ONESHOT_MASK | R_CONTROL_SIZE_MASK | \ 424f4c6206SPeter Maydell R_CONTROL_PRESCALE_MASK | R_CONTROL_INTEN_MASK | \ 434f4c6206SPeter Maydell R_CONTROL_MODE_MASK | R_CONTROL_ENABLE_MASK) 444f4c6206SPeter Maydell REG32(TIMER1INTCLR, 0xc) 454f4c6206SPeter Maydell REG32(TIMER1RIS, 0x10) 464f4c6206SPeter Maydell REG32(TIMER1MIS, 0x14) 474f4c6206SPeter Maydell REG32(TIMER1BGLOAD, 0x18) 484f4c6206SPeter Maydell REG32(TIMER2LOAD, 0x20) 494f4c6206SPeter Maydell REG32(TIMER2VALUE, 0x24) 504f4c6206SPeter Maydell REG32(TIMER2CONTROL, 0x28) 514f4c6206SPeter Maydell REG32(TIMER2INTCLR, 0x2c) 524f4c6206SPeter Maydell REG32(TIMER2RIS, 0x30) 534f4c6206SPeter Maydell REG32(TIMER2MIS, 0x34) 544f4c6206SPeter Maydell REG32(TIMER2BGLOAD, 0x38) 554f4c6206SPeter Maydell REG32(TIMERITCR, 0xf00) 564f4c6206SPeter Maydell FIELD(TIMERITCR, ENABLE, 0, 1) 574f4c6206SPeter Maydell #define R_TIMERITCR_VALID_MASK R_TIMERITCR_ENABLE_MASK 584f4c6206SPeter Maydell REG32(TIMERITOP, 0xf04) 594f4c6206SPeter Maydell FIELD(TIMERITOP, TIMINT1, 0, 1) 604f4c6206SPeter Maydell FIELD(TIMERITOP, TIMINT2, 1, 1) 614f4c6206SPeter Maydell #define R_TIMERITOP_VALID_MASK (R_TIMERITOP_TIMINT1_MASK | \ 624f4c6206SPeter Maydell R_TIMERITOP_TIMINT2_MASK) 634f4c6206SPeter Maydell REG32(PID4, 0xfd0) 644f4c6206SPeter Maydell REG32(PID5, 0xfd4) 654f4c6206SPeter Maydell REG32(PID6, 0xfd8) 664f4c6206SPeter Maydell REG32(PID7, 0xfdc) 674f4c6206SPeter Maydell REG32(PID0, 0xfe0) 684f4c6206SPeter Maydell REG32(PID1, 0xfe4) 694f4c6206SPeter Maydell REG32(PID2, 0xfe8) 704f4c6206SPeter Maydell REG32(PID3, 0xfec) 714f4c6206SPeter Maydell REG32(CID0, 0xff0) 724f4c6206SPeter Maydell REG32(CID1, 0xff4) 734f4c6206SPeter Maydell REG32(CID2, 0xff8) 744f4c6206SPeter Maydell REG32(CID3, 0xffc) 754f4c6206SPeter Maydell 764f4c6206SPeter Maydell /* PID/CID values */ 774f4c6206SPeter Maydell static const int timer_id[] = { 784f4c6206SPeter Maydell 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ 794f4c6206SPeter Maydell 0x23, 0xb8, 0x1b, 0x00, /* PID0..PID3 */ 804f4c6206SPeter Maydell 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ 814f4c6206SPeter Maydell }; 824f4c6206SPeter Maydell 834f4c6206SPeter Maydell static bool cmsdk_dualtimermod_intstatus(CMSDKAPBDualTimerModule *m) 844f4c6206SPeter Maydell { 854f4c6206SPeter Maydell /* Return masked interrupt status for the timer module */ 864f4c6206SPeter Maydell return m->intstatus && (m->control & R_CONTROL_INTEN_MASK); 874f4c6206SPeter Maydell } 884f4c6206SPeter Maydell 894f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_update(CMSDKAPBDualTimer *s) 904f4c6206SPeter Maydell { 914f4c6206SPeter Maydell bool timint1, timint2, timintc; 924f4c6206SPeter Maydell 934f4c6206SPeter Maydell if (s->timeritcr) { 944f4c6206SPeter Maydell /* Integration test mode: outputs driven directly from TIMERITOP bits */ 954f4c6206SPeter Maydell timint1 = s->timeritop & R_TIMERITOP_TIMINT1_MASK; 964f4c6206SPeter Maydell timint2 = s->timeritop & R_TIMERITOP_TIMINT2_MASK; 974f4c6206SPeter Maydell } else { 984f4c6206SPeter Maydell timint1 = cmsdk_dualtimermod_intstatus(&s->timermod[0]); 994f4c6206SPeter Maydell timint2 = cmsdk_dualtimermod_intstatus(&s->timermod[1]); 1004f4c6206SPeter Maydell } 1014f4c6206SPeter Maydell 1024f4c6206SPeter Maydell timintc = timint1 || timint2; 1034f4c6206SPeter Maydell 1044f4c6206SPeter Maydell qemu_set_irq(s->timermod[0].timerint, timint1); 1054f4c6206SPeter Maydell qemu_set_irq(s->timermod[1].timerint, timint2); 1064f4c6206SPeter Maydell qemu_set_irq(s->timerintc, timintc); 1074f4c6206SPeter Maydell } 1084f4c6206SPeter Maydell 1094f4c6206SPeter Maydell static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m, 1104f4c6206SPeter Maydell uint32_t newctrl) 1114f4c6206SPeter Maydell { 1124f4c6206SPeter Maydell /* Handle a write to the CONTROL register */ 1134f4c6206SPeter Maydell uint32_t changed; 1144f4c6206SPeter Maydell 115da38e068SPeter Maydell ptimer_transaction_begin(m->timer); 116da38e068SPeter Maydell 1174f4c6206SPeter Maydell newctrl &= R_CONTROL_VALID_MASK; 1184f4c6206SPeter Maydell 1194f4c6206SPeter Maydell changed = m->control ^ newctrl; 1204f4c6206SPeter Maydell 1214f4c6206SPeter Maydell if (changed & ~newctrl & R_CONTROL_ENABLE_MASK) { 1224f4c6206SPeter Maydell /* ENABLE cleared, stop timer before any further changes */ 1234f4c6206SPeter Maydell ptimer_stop(m->timer); 1244f4c6206SPeter Maydell } 1254f4c6206SPeter Maydell 1264f4c6206SPeter Maydell if (changed & R_CONTROL_PRESCALE_MASK) { 1274f4c6206SPeter Maydell int divisor; 1284f4c6206SPeter Maydell 1294f4c6206SPeter Maydell switch (FIELD_EX32(newctrl, CONTROL, PRESCALE)) { 1304f4c6206SPeter Maydell case 0: 1314f4c6206SPeter Maydell divisor = 1; 1324f4c6206SPeter Maydell break; 1334f4c6206SPeter Maydell case 1: 1344f4c6206SPeter Maydell divisor = 16; 1354f4c6206SPeter Maydell break; 1364f4c6206SPeter Maydell case 2: 1374f4c6206SPeter Maydell divisor = 256; 1384f4c6206SPeter Maydell break; 1394f4c6206SPeter Maydell case 3: 1404f4c6206SPeter Maydell /* UNDEFINED; complain, and arbitrarily treat like 2 */ 1414f4c6206SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 1424f4c6206SPeter Maydell "CMSDK APB dual-timer: CONTROL.PRESCALE==0b11" 1434f4c6206SPeter Maydell " is undefined behaviour\n"); 1444f4c6206SPeter Maydell divisor = 256; 1454f4c6206SPeter Maydell break; 1464f4c6206SPeter Maydell default: 1474f4c6206SPeter Maydell g_assert_not_reached(); 1484f4c6206SPeter Maydell } 1494f4c6206SPeter Maydell ptimer_set_freq(m->timer, m->parent->pclk_frq / divisor); 1504f4c6206SPeter Maydell } 1514f4c6206SPeter Maydell 1524f4c6206SPeter Maydell if (changed & R_CONTROL_MODE_MASK) { 1534f4c6206SPeter Maydell uint32_t load; 1544f4c6206SPeter Maydell if (newctrl & R_CONTROL_MODE_MASK) { 1554f4c6206SPeter Maydell /* Periodic: the limit is the LOAD register value */ 1564f4c6206SPeter Maydell load = m->load; 1574f4c6206SPeter Maydell } else { 1584f4c6206SPeter Maydell /* Free-running: counter wraps around */ 1594f4c6206SPeter Maydell load = ptimer_get_limit(m->timer); 1604f4c6206SPeter Maydell if (!(m->control & R_CONTROL_SIZE_MASK)) { 1614f4c6206SPeter Maydell load = deposit32(m->load, 0, 16, load); 1624f4c6206SPeter Maydell } 1634f4c6206SPeter Maydell m->load = load; 1644f4c6206SPeter Maydell load = 0xffffffff; 1654f4c6206SPeter Maydell } 1664f4c6206SPeter Maydell if (!(m->control & R_CONTROL_SIZE_MASK)) { 1674f4c6206SPeter Maydell load &= 0xffff; 1684f4c6206SPeter Maydell } 1694f4c6206SPeter Maydell ptimer_set_limit(m->timer, load, 0); 1704f4c6206SPeter Maydell } 1714f4c6206SPeter Maydell 1724f4c6206SPeter Maydell if (changed & R_CONTROL_SIZE_MASK) { 1734f4c6206SPeter Maydell /* Timer switched between 16 and 32 bit count */ 1744f4c6206SPeter Maydell uint32_t value, load; 1754f4c6206SPeter Maydell 1764f4c6206SPeter Maydell value = ptimer_get_count(m->timer); 1774f4c6206SPeter Maydell load = ptimer_get_limit(m->timer); 1784f4c6206SPeter Maydell if (newctrl & R_CONTROL_SIZE_MASK) { 1794f4c6206SPeter Maydell /* 16 -> 32, top half of VALUE is in struct field */ 1804f4c6206SPeter Maydell value = deposit32(m->value, 0, 16, value); 1814f4c6206SPeter Maydell } else { 1824f4c6206SPeter Maydell /* 32 -> 16: save top half to struct field and truncate */ 1834f4c6206SPeter Maydell m->value = value; 1844f4c6206SPeter Maydell value &= 0xffff; 1854f4c6206SPeter Maydell } 1864f4c6206SPeter Maydell 1874f4c6206SPeter Maydell if (newctrl & R_CONTROL_MODE_MASK) { 1884f4c6206SPeter Maydell /* Periodic, timer limit has LOAD value */ 1894f4c6206SPeter Maydell if (newctrl & R_CONTROL_SIZE_MASK) { 1904f4c6206SPeter Maydell load = deposit32(m->load, 0, 16, load); 1914f4c6206SPeter Maydell } else { 1924f4c6206SPeter Maydell m->load = load; 1934f4c6206SPeter Maydell load &= 0xffff; 1944f4c6206SPeter Maydell } 1954f4c6206SPeter Maydell } else { 1964f4c6206SPeter Maydell /* Free-running, timer limit is set to give wraparound */ 1974f4c6206SPeter Maydell if (newctrl & R_CONTROL_SIZE_MASK) { 1984f4c6206SPeter Maydell load = 0xffffffff; 1994f4c6206SPeter Maydell } else { 2004f4c6206SPeter Maydell load = 0xffff; 2014f4c6206SPeter Maydell } 2024f4c6206SPeter Maydell } 2034f4c6206SPeter Maydell ptimer_set_count(m->timer, value); 2044f4c6206SPeter Maydell ptimer_set_limit(m->timer, load, 0); 2054f4c6206SPeter Maydell } 2064f4c6206SPeter Maydell 2074f4c6206SPeter Maydell if (newctrl & R_CONTROL_ENABLE_MASK) { 2084f4c6206SPeter Maydell /* 2094f4c6206SPeter Maydell * ENABLE is set; start the timer after all other changes. 2104f4c6206SPeter Maydell * We start it even if the ENABLE bit didn't actually change, 2114f4c6206SPeter Maydell * in case the timer was an expired one-shot timer that has 2124f4c6206SPeter Maydell * now been changed into a free-running or periodic timer. 2134f4c6206SPeter Maydell */ 2144f4c6206SPeter Maydell ptimer_run(m->timer, !!(newctrl & R_CONTROL_ONESHOT_MASK)); 2154f4c6206SPeter Maydell } 2164f4c6206SPeter Maydell 2174f4c6206SPeter Maydell m->control = newctrl; 218da38e068SPeter Maydell 219da38e068SPeter Maydell ptimer_transaction_commit(m->timer); 2204f4c6206SPeter Maydell } 2214f4c6206SPeter Maydell 2224f4c6206SPeter Maydell static uint64_t cmsdk_apb_dualtimer_read(void *opaque, hwaddr offset, 2234f4c6206SPeter Maydell unsigned size) 2244f4c6206SPeter Maydell { 2254f4c6206SPeter Maydell CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque); 2264f4c6206SPeter Maydell uint64_t r; 2274f4c6206SPeter Maydell 2284f4c6206SPeter Maydell if (offset >= A_TIMERITCR) { 2294f4c6206SPeter Maydell switch (offset) { 2304f4c6206SPeter Maydell case A_TIMERITCR: 2314f4c6206SPeter Maydell r = s->timeritcr; 2324f4c6206SPeter Maydell break; 2334f4c6206SPeter Maydell case A_PID4 ... A_CID3: 2344f4c6206SPeter Maydell r = timer_id[(offset - A_PID4) / 4]; 2354f4c6206SPeter Maydell break; 2364f4c6206SPeter Maydell default: 2374f4c6206SPeter Maydell bad_offset: 2384f4c6206SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 2394f4c6206SPeter Maydell "CMSDK APB dual-timer read: bad offset %x\n", 2404f4c6206SPeter Maydell (int) offset); 2414f4c6206SPeter Maydell r = 0; 2424f4c6206SPeter Maydell break; 2434f4c6206SPeter Maydell } 2444f4c6206SPeter Maydell } else { 2454f4c6206SPeter Maydell int timer = offset >> 5; 2464f4c6206SPeter Maydell CMSDKAPBDualTimerModule *m; 2474f4c6206SPeter Maydell 2484f4c6206SPeter Maydell if (timer >= ARRAY_SIZE(s->timermod)) { 2494f4c6206SPeter Maydell goto bad_offset; 2504f4c6206SPeter Maydell } 2514f4c6206SPeter Maydell 2524f4c6206SPeter Maydell m = &s->timermod[timer]; 2534f4c6206SPeter Maydell 2544f4c6206SPeter Maydell switch (offset & 0x1F) { 2554f4c6206SPeter Maydell case A_TIMER1LOAD: 2564f4c6206SPeter Maydell case A_TIMER1BGLOAD: 2574f4c6206SPeter Maydell if (m->control & R_CONTROL_MODE_MASK) { 2584f4c6206SPeter Maydell /* 2594f4c6206SPeter Maydell * Periodic: the ptimer limit is the LOAD register value, (or 2604f4c6206SPeter Maydell * just the low 16 bits of it if the timer is in 16-bit mode) 2614f4c6206SPeter Maydell */ 2624f4c6206SPeter Maydell r = ptimer_get_limit(m->timer); 2634f4c6206SPeter Maydell if (!(m->control & R_CONTROL_SIZE_MASK)) { 2644f4c6206SPeter Maydell r = deposit32(m->load, 0, 16, r); 2654f4c6206SPeter Maydell } 2664f4c6206SPeter Maydell } else { 2674f4c6206SPeter Maydell /* Free-running: LOAD register value is just in m->load */ 2684f4c6206SPeter Maydell r = m->load; 2694f4c6206SPeter Maydell } 2704f4c6206SPeter Maydell break; 2714f4c6206SPeter Maydell case A_TIMER1VALUE: 2724f4c6206SPeter Maydell r = ptimer_get_count(m->timer); 2734f4c6206SPeter Maydell if (!(m->control & R_CONTROL_SIZE_MASK)) { 2744f4c6206SPeter Maydell r = deposit32(m->value, 0, 16, r); 2754f4c6206SPeter Maydell } 2764f4c6206SPeter Maydell break; 2774f4c6206SPeter Maydell case A_TIMER1CONTROL: 2784f4c6206SPeter Maydell r = m->control; 2794f4c6206SPeter Maydell break; 2804f4c6206SPeter Maydell case A_TIMER1RIS: 2814f4c6206SPeter Maydell r = m->intstatus; 2824f4c6206SPeter Maydell break; 2834f4c6206SPeter Maydell case A_TIMER1MIS: 2844f4c6206SPeter Maydell r = cmsdk_dualtimermod_intstatus(m); 2854f4c6206SPeter Maydell break; 2864f4c6206SPeter Maydell default: 2874f4c6206SPeter Maydell goto bad_offset; 2884f4c6206SPeter Maydell } 2894f4c6206SPeter Maydell } 2904f4c6206SPeter Maydell 2914f4c6206SPeter Maydell trace_cmsdk_apb_dualtimer_read(offset, r, size); 2924f4c6206SPeter Maydell return r; 2934f4c6206SPeter Maydell } 2944f4c6206SPeter Maydell 2954f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset, 2964f4c6206SPeter Maydell uint64_t value, unsigned size) 2974f4c6206SPeter Maydell { 2984f4c6206SPeter Maydell CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque); 2994f4c6206SPeter Maydell 3004f4c6206SPeter Maydell trace_cmsdk_apb_dualtimer_write(offset, value, size); 3014f4c6206SPeter Maydell 3024f4c6206SPeter Maydell if (offset >= A_TIMERITCR) { 3034f4c6206SPeter Maydell switch (offset) { 3044f4c6206SPeter Maydell case A_TIMERITCR: 3054f4c6206SPeter Maydell s->timeritcr = value & R_TIMERITCR_VALID_MASK; 3064f4c6206SPeter Maydell cmsdk_apb_dualtimer_update(s); 3073e1dd459SPeter Maydell break; 3084f4c6206SPeter Maydell case A_TIMERITOP: 3094f4c6206SPeter Maydell s->timeritop = value & R_TIMERITOP_VALID_MASK; 3104f4c6206SPeter Maydell cmsdk_apb_dualtimer_update(s); 3113e1dd459SPeter Maydell break; 3124f4c6206SPeter Maydell default: 3134f4c6206SPeter Maydell bad_offset: 3144f4c6206SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 3154f4c6206SPeter Maydell "CMSDK APB dual-timer write: bad offset %x\n", 3164f4c6206SPeter Maydell (int) offset); 3174f4c6206SPeter Maydell break; 3184f4c6206SPeter Maydell } 3194f4c6206SPeter Maydell } else { 3204f4c6206SPeter Maydell int timer = offset >> 5; 3214f4c6206SPeter Maydell CMSDKAPBDualTimerModule *m; 3224f4c6206SPeter Maydell 3234f4c6206SPeter Maydell if (timer >= ARRAY_SIZE(s->timermod)) { 3244f4c6206SPeter Maydell goto bad_offset; 3254f4c6206SPeter Maydell } 3264f4c6206SPeter Maydell 3274f4c6206SPeter Maydell m = &s->timermod[timer]; 3284f4c6206SPeter Maydell 3294f4c6206SPeter Maydell switch (offset & 0x1F) { 3304f4c6206SPeter Maydell case A_TIMER1LOAD: 3314f4c6206SPeter Maydell /* Set the limit, and immediately reload the count from it */ 3324f4c6206SPeter Maydell m->load = value; 3334f4c6206SPeter Maydell m->value = value; 3344f4c6206SPeter Maydell if (!(m->control & R_CONTROL_SIZE_MASK)) { 3354f4c6206SPeter Maydell value &= 0xffff; 3364f4c6206SPeter Maydell } 337da38e068SPeter Maydell ptimer_transaction_begin(m->timer); 3384f4c6206SPeter Maydell if (!(m->control & R_CONTROL_MODE_MASK)) { 3394f4c6206SPeter Maydell /* 3404f4c6206SPeter Maydell * In free-running mode this won't set the limit but will 3414f4c6206SPeter Maydell * still change the current count value. 3424f4c6206SPeter Maydell */ 3434f4c6206SPeter Maydell ptimer_set_count(m->timer, value); 3444f4c6206SPeter Maydell } else { 3454f4c6206SPeter Maydell if (!value) { 3464f4c6206SPeter Maydell ptimer_stop(m->timer); 3474f4c6206SPeter Maydell } 3484f4c6206SPeter Maydell ptimer_set_limit(m->timer, value, 1); 3494f4c6206SPeter Maydell if (value && (m->control & R_CONTROL_ENABLE_MASK)) { 3504f4c6206SPeter Maydell /* Force possibly-expired oneshot timer to restart */ 3514f4c6206SPeter Maydell ptimer_run(m->timer, 1); 3524f4c6206SPeter Maydell } 3534f4c6206SPeter Maydell } 354da38e068SPeter Maydell ptimer_transaction_commit(m->timer); 3554f4c6206SPeter Maydell break; 3564f4c6206SPeter Maydell case A_TIMER1BGLOAD: 3574f4c6206SPeter Maydell /* Set the limit, but not the current count */ 3584f4c6206SPeter Maydell m->load = value; 3594f4c6206SPeter Maydell if (!(m->control & R_CONTROL_MODE_MASK)) { 3604f4c6206SPeter Maydell /* In free-running mode there is no limit */ 3614f4c6206SPeter Maydell break; 3624f4c6206SPeter Maydell } 3634f4c6206SPeter Maydell if (!(m->control & R_CONTROL_SIZE_MASK)) { 3644f4c6206SPeter Maydell value &= 0xffff; 3654f4c6206SPeter Maydell } 366da38e068SPeter Maydell ptimer_transaction_begin(m->timer); 3674f4c6206SPeter Maydell ptimer_set_limit(m->timer, value, 0); 368da38e068SPeter Maydell ptimer_transaction_commit(m->timer); 3694f4c6206SPeter Maydell break; 3704f4c6206SPeter Maydell case A_TIMER1CONTROL: 3714f4c6206SPeter Maydell cmsdk_dualtimermod_write_control(m, value); 3724f4c6206SPeter Maydell cmsdk_apb_dualtimer_update(s); 3734f4c6206SPeter Maydell break; 3744f4c6206SPeter Maydell case A_TIMER1INTCLR: 3754f4c6206SPeter Maydell m->intstatus = 0; 3764f4c6206SPeter Maydell cmsdk_apb_dualtimer_update(s); 3774f4c6206SPeter Maydell break; 3784f4c6206SPeter Maydell default: 3794f4c6206SPeter Maydell goto bad_offset; 3804f4c6206SPeter Maydell } 3814f4c6206SPeter Maydell } 3824f4c6206SPeter Maydell } 3834f4c6206SPeter Maydell 3844f4c6206SPeter Maydell static const MemoryRegionOps cmsdk_apb_dualtimer_ops = { 3854f4c6206SPeter Maydell .read = cmsdk_apb_dualtimer_read, 3864f4c6206SPeter Maydell .write = cmsdk_apb_dualtimer_write, 3874f4c6206SPeter Maydell .endianness = DEVICE_LITTLE_ENDIAN, 3884f4c6206SPeter Maydell /* byte/halfword accesses are just zero-padded on reads and writes */ 3894f4c6206SPeter Maydell .impl.min_access_size = 4, 3904f4c6206SPeter Maydell .impl.max_access_size = 4, 3914f4c6206SPeter Maydell .valid.min_access_size = 1, 3924f4c6206SPeter Maydell .valid.max_access_size = 4, 3934f4c6206SPeter Maydell }; 3944f4c6206SPeter Maydell 3954f4c6206SPeter Maydell static void cmsdk_dualtimermod_tick(void *opaque) 3964f4c6206SPeter Maydell { 3974f4c6206SPeter Maydell CMSDKAPBDualTimerModule *m = opaque; 3984f4c6206SPeter Maydell 3994f4c6206SPeter Maydell m->intstatus = 1; 4004f4c6206SPeter Maydell cmsdk_apb_dualtimer_update(m->parent); 4014f4c6206SPeter Maydell } 4024f4c6206SPeter Maydell 4034f4c6206SPeter Maydell static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m) 4044f4c6206SPeter Maydell { 4054f4c6206SPeter Maydell m->control = R_CONTROL_INTEN_MASK; 4064f4c6206SPeter Maydell m->intstatus = 0; 4074f4c6206SPeter Maydell m->load = 0; 4084f4c6206SPeter Maydell m->value = 0xffffffff; 409da38e068SPeter Maydell ptimer_transaction_begin(m->timer); 4104f4c6206SPeter Maydell ptimer_stop(m->timer); 4114f4c6206SPeter Maydell /* 4124f4c6206SPeter Maydell * We start in free-running mode, with VALUE at 0xffffffff, and 4134f4c6206SPeter Maydell * in 16-bit counter mode. This means that the ptimer count and 4144f4c6206SPeter Maydell * limit must both be set to 0xffff, so we wrap at 16 bits. 4154f4c6206SPeter Maydell */ 4164f4c6206SPeter Maydell ptimer_set_limit(m->timer, 0xffff, 1); 4174f4c6206SPeter Maydell ptimer_set_freq(m->timer, m->parent->pclk_frq); 418da38e068SPeter Maydell ptimer_transaction_commit(m->timer); 4194f4c6206SPeter Maydell } 4204f4c6206SPeter Maydell 4214f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_reset(DeviceState *dev) 4224f4c6206SPeter Maydell { 4234f4c6206SPeter Maydell CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev); 4244f4c6206SPeter Maydell int i; 4254f4c6206SPeter Maydell 4264f4c6206SPeter Maydell trace_cmsdk_apb_dualtimer_reset(); 4274f4c6206SPeter Maydell 4284f4c6206SPeter Maydell for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { 4294f4c6206SPeter Maydell cmsdk_dualtimermod_reset(&s->timermod[i]); 4304f4c6206SPeter Maydell } 4314f4c6206SPeter Maydell s->timeritcr = 0; 4324f4c6206SPeter Maydell s->timeritop = 0; 4334f4c6206SPeter Maydell } 4344f4c6206SPeter Maydell 4354f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_init(Object *obj) 4364f4c6206SPeter Maydell { 4374f4c6206SPeter Maydell SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 4384f4c6206SPeter Maydell CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(obj); 4394f4c6206SPeter Maydell int i; 4404f4c6206SPeter Maydell 4414f4c6206SPeter Maydell memory_region_init_io(&s->iomem, obj, &cmsdk_apb_dualtimer_ops, 4424f4c6206SPeter Maydell s, "cmsdk-apb-dualtimer", 0x1000); 4434f4c6206SPeter Maydell sysbus_init_mmio(sbd, &s->iomem); 4444f4c6206SPeter Maydell sysbus_init_irq(sbd, &s->timerintc); 4454f4c6206SPeter Maydell 4464f4c6206SPeter Maydell for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { 4474f4c6206SPeter Maydell sysbus_init_irq(sbd, &s->timermod[i].timerint); 4484f4c6206SPeter Maydell } 449*55fd0f84SPeter Maydell s->timclk = qdev_init_clock_in(DEVICE(s), "TIMCLK", NULL, NULL); 4504f4c6206SPeter Maydell } 4514f4c6206SPeter Maydell 4524f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp) 4534f4c6206SPeter Maydell { 4544f4c6206SPeter Maydell CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev); 4554f4c6206SPeter Maydell int i; 4564f4c6206SPeter Maydell 4574f4c6206SPeter Maydell if (s->pclk_frq == 0) { 4584f4c6206SPeter Maydell error_setg(errp, "CMSDK APB timer: pclk-frq property must be set"); 4594f4c6206SPeter Maydell return; 4604f4c6206SPeter Maydell } 4614f4c6206SPeter Maydell 4624f4c6206SPeter Maydell for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { 4634f4c6206SPeter Maydell CMSDKAPBDualTimerModule *m = &s->timermod[i]; 4644f4c6206SPeter Maydell 4654f4c6206SPeter Maydell m->parent = s; 466da38e068SPeter Maydell m->timer = ptimer_init(cmsdk_dualtimermod_tick, m, 4674f4c6206SPeter Maydell PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | 4684f4c6206SPeter Maydell PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | 4694f4c6206SPeter Maydell PTIMER_POLICY_NO_IMMEDIATE_RELOAD | 4704f4c6206SPeter Maydell PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); 4714f4c6206SPeter Maydell } 4724f4c6206SPeter Maydell } 4734f4c6206SPeter Maydell 4744f4c6206SPeter Maydell static const VMStateDescription cmsdk_dualtimermod_vmstate = { 4754f4c6206SPeter Maydell .name = "cmsdk-apb-dualtimer-module", 4764f4c6206SPeter Maydell .version_id = 1, 4774f4c6206SPeter Maydell .minimum_version_id = 1, 4784f4c6206SPeter Maydell .fields = (VMStateField[]) { 4794f4c6206SPeter Maydell VMSTATE_PTIMER(timer, CMSDKAPBDualTimerModule), 4804f4c6206SPeter Maydell VMSTATE_UINT32(load, CMSDKAPBDualTimerModule), 4814f4c6206SPeter Maydell VMSTATE_UINT32(value, CMSDKAPBDualTimerModule), 4824f4c6206SPeter Maydell VMSTATE_UINT32(control, CMSDKAPBDualTimerModule), 4834f4c6206SPeter Maydell VMSTATE_UINT32(intstatus, CMSDKAPBDualTimerModule), 4844f4c6206SPeter Maydell VMSTATE_END_OF_LIST() 4854f4c6206SPeter Maydell } 4864f4c6206SPeter Maydell }; 4874f4c6206SPeter Maydell 4884f4c6206SPeter Maydell static const VMStateDescription cmsdk_apb_dualtimer_vmstate = { 4894f4c6206SPeter Maydell .name = "cmsdk-apb-dualtimer", 490*55fd0f84SPeter Maydell .version_id = 2, 491*55fd0f84SPeter Maydell .minimum_version_id = 2, 4924f4c6206SPeter Maydell .fields = (VMStateField[]) { 493*55fd0f84SPeter Maydell VMSTATE_CLOCK(timclk, CMSDKAPBDualTimer), 4944f4c6206SPeter Maydell VMSTATE_STRUCT_ARRAY(timermod, CMSDKAPBDualTimer, 4954f4c6206SPeter Maydell CMSDK_APB_DUALTIMER_NUM_MODULES, 4964f4c6206SPeter Maydell 1, cmsdk_dualtimermod_vmstate, 4974f4c6206SPeter Maydell CMSDKAPBDualTimerModule), 4984f4c6206SPeter Maydell VMSTATE_UINT32(timeritcr, CMSDKAPBDualTimer), 4994f4c6206SPeter Maydell VMSTATE_UINT32(timeritop, CMSDKAPBDualTimer), 5004f4c6206SPeter Maydell VMSTATE_END_OF_LIST() 5014f4c6206SPeter Maydell } 5024f4c6206SPeter Maydell }; 5034f4c6206SPeter Maydell 5044f4c6206SPeter Maydell static Property cmsdk_apb_dualtimer_properties[] = { 5054f4c6206SPeter Maydell DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBDualTimer, pclk_frq, 0), 5064f4c6206SPeter Maydell DEFINE_PROP_END_OF_LIST(), 5074f4c6206SPeter Maydell }; 5084f4c6206SPeter Maydell 5094f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data) 5104f4c6206SPeter Maydell { 5114f4c6206SPeter Maydell DeviceClass *dc = DEVICE_CLASS(klass); 5124f4c6206SPeter Maydell 5134f4c6206SPeter Maydell dc->realize = cmsdk_apb_dualtimer_realize; 5144f4c6206SPeter Maydell dc->vmsd = &cmsdk_apb_dualtimer_vmstate; 5154f4c6206SPeter Maydell dc->reset = cmsdk_apb_dualtimer_reset; 5164f67d30bSMarc-André Lureau device_class_set_props(dc, cmsdk_apb_dualtimer_properties); 5174f4c6206SPeter Maydell } 5184f4c6206SPeter Maydell 5194f4c6206SPeter Maydell static const TypeInfo cmsdk_apb_dualtimer_info = { 5204f4c6206SPeter Maydell .name = TYPE_CMSDK_APB_DUALTIMER, 5214f4c6206SPeter Maydell .parent = TYPE_SYS_BUS_DEVICE, 5224f4c6206SPeter Maydell .instance_size = sizeof(CMSDKAPBDualTimer), 5234f4c6206SPeter Maydell .instance_init = cmsdk_apb_dualtimer_init, 5244f4c6206SPeter Maydell .class_init = cmsdk_apb_dualtimer_class_init, 5254f4c6206SPeter Maydell }; 5264f4c6206SPeter Maydell 5274f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_register_types(void) 5284f4c6206SPeter Maydell { 5294f4c6206SPeter Maydell type_register_static(&cmsdk_apb_dualtimer_info); 5304f4c6206SPeter Maydell } 5314f4c6206SPeter Maydell 5324f4c6206SPeter Maydell type_init(cmsdk_apb_dualtimer_register_types); 533