xref: /qemu/hw/timer/cmsdk-apb-dualtimer.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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"
2855fd0f84SPeter 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 
cmsdk_dualtimermod_intstatus(CMSDKAPBDualTimerModule * m)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 
cmsdk_apb_dualtimer_update(CMSDKAPBDualTimer * s)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 
cmsdk_dualtimermod_divisor(CMSDKAPBDualTimerModule * m)1097208aafbSPeter Maydell static int cmsdk_dualtimermod_divisor(CMSDKAPBDualTimerModule *m)
1107208aafbSPeter Maydell {
1117208aafbSPeter Maydell     /* Return the divisor set by the current CONTROL.PRESCALE value */
1127208aafbSPeter Maydell     switch (FIELD_EX32(m->control, CONTROL, PRESCALE)) {
1137208aafbSPeter Maydell     case 0:
1147208aafbSPeter Maydell         return 1;
1157208aafbSPeter Maydell     case 1:
1167208aafbSPeter Maydell         return 16;
1177208aafbSPeter Maydell     case 2:
1187208aafbSPeter Maydell     case 3: /* UNDEFINED, we treat like 2 (and complained when it was set) */
1197208aafbSPeter Maydell         return 256;
1207208aafbSPeter Maydell     default:
1217208aafbSPeter Maydell         g_assert_not_reached();
1227208aafbSPeter Maydell     }
1237208aafbSPeter Maydell }
1247208aafbSPeter Maydell 
cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule * m,uint32_t newctrl)1254f4c6206SPeter Maydell static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m,
1264f4c6206SPeter Maydell                                              uint32_t newctrl)
1274f4c6206SPeter Maydell {
1284f4c6206SPeter Maydell     /* Handle a write to the CONTROL register */
1294f4c6206SPeter Maydell     uint32_t changed;
1304f4c6206SPeter Maydell 
131da38e068SPeter Maydell     ptimer_transaction_begin(m->timer);
132da38e068SPeter Maydell 
1334f4c6206SPeter Maydell     newctrl &= R_CONTROL_VALID_MASK;
1344f4c6206SPeter Maydell 
1354f4c6206SPeter Maydell     changed = m->control ^ newctrl;
1364f4c6206SPeter Maydell 
1374f4c6206SPeter Maydell     if (changed & ~newctrl & R_CONTROL_ENABLE_MASK) {
1384f4c6206SPeter Maydell         /* ENABLE cleared, stop timer before any further changes */
1394f4c6206SPeter Maydell         ptimer_stop(m->timer);
1404f4c6206SPeter Maydell     }
1414f4c6206SPeter Maydell 
1424f4c6206SPeter Maydell     if (changed & R_CONTROL_PRESCALE_MASK) {
1434f4c6206SPeter Maydell         int divisor;
1444f4c6206SPeter Maydell 
1454f4c6206SPeter Maydell         switch (FIELD_EX32(newctrl, CONTROL, PRESCALE)) {
1464f4c6206SPeter Maydell         case 0:
1474f4c6206SPeter Maydell             divisor = 1;
1484f4c6206SPeter Maydell             break;
1494f4c6206SPeter Maydell         case 1:
1504f4c6206SPeter Maydell             divisor = 16;
1514f4c6206SPeter Maydell             break;
1524f4c6206SPeter Maydell         case 2:
1534f4c6206SPeter Maydell             divisor = 256;
1544f4c6206SPeter Maydell             break;
1554f4c6206SPeter Maydell         case 3:
1564f4c6206SPeter Maydell             /* UNDEFINED; complain, and arbitrarily treat like 2 */
1574f4c6206SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR,
1584f4c6206SPeter Maydell                           "CMSDK APB dual-timer: CONTROL.PRESCALE==0b11"
1594f4c6206SPeter Maydell                           " is undefined behaviour\n");
1604f4c6206SPeter Maydell             divisor = 256;
1614f4c6206SPeter Maydell             break;
1624f4c6206SPeter Maydell         default:
1634f4c6206SPeter Maydell             g_assert_not_reached();
1644f4c6206SPeter Maydell         }
1657208aafbSPeter Maydell         ptimer_set_period_from_clock(m->timer, m->parent->timclk, divisor);
1664f4c6206SPeter Maydell     }
1674f4c6206SPeter Maydell 
1684f4c6206SPeter Maydell     if (changed & R_CONTROL_MODE_MASK) {
1694f4c6206SPeter Maydell         uint32_t load;
1704f4c6206SPeter Maydell         if (newctrl & R_CONTROL_MODE_MASK) {
1714f4c6206SPeter Maydell             /* Periodic: the limit is the LOAD register value */
1724f4c6206SPeter Maydell             load = m->load;
1734f4c6206SPeter Maydell         } else {
1744f4c6206SPeter Maydell             /* Free-running: counter wraps around */
1754f4c6206SPeter Maydell             load = ptimer_get_limit(m->timer);
1764f4c6206SPeter Maydell             if (!(m->control & R_CONTROL_SIZE_MASK)) {
1774f4c6206SPeter Maydell                 load = deposit32(m->load, 0, 16, load);
1784f4c6206SPeter Maydell             }
1794f4c6206SPeter Maydell             m->load = load;
1804f4c6206SPeter Maydell             load = 0xffffffff;
1814f4c6206SPeter Maydell         }
1824f4c6206SPeter Maydell         if (!(m->control & R_CONTROL_SIZE_MASK)) {
1834f4c6206SPeter Maydell             load &= 0xffff;
1844f4c6206SPeter Maydell         }
1854f4c6206SPeter Maydell         ptimer_set_limit(m->timer, load, 0);
1864f4c6206SPeter Maydell     }
1874f4c6206SPeter Maydell 
1884f4c6206SPeter Maydell     if (changed & R_CONTROL_SIZE_MASK) {
1894f4c6206SPeter Maydell         /* Timer switched between 16 and 32 bit count */
1904f4c6206SPeter Maydell         uint32_t value, load;
1914f4c6206SPeter Maydell 
1924f4c6206SPeter Maydell         value = ptimer_get_count(m->timer);
1934f4c6206SPeter Maydell         load = ptimer_get_limit(m->timer);
1944f4c6206SPeter Maydell         if (newctrl & R_CONTROL_SIZE_MASK) {
1954f4c6206SPeter Maydell             /* 16 -> 32, top half of VALUE is in struct field */
1964f4c6206SPeter Maydell             value = deposit32(m->value, 0, 16, value);
1974f4c6206SPeter Maydell         } else {
1984f4c6206SPeter Maydell             /* 32 -> 16: save top half to struct field and truncate */
1994f4c6206SPeter Maydell             m->value = value;
2004f4c6206SPeter Maydell             value &= 0xffff;
2014f4c6206SPeter Maydell         }
2024f4c6206SPeter Maydell 
2034f4c6206SPeter Maydell         if (newctrl & R_CONTROL_MODE_MASK) {
2044f4c6206SPeter Maydell             /* Periodic, timer limit has LOAD value */
2054f4c6206SPeter Maydell             if (newctrl & R_CONTROL_SIZE_MASK) {
2064f4c6206SPeter Maydell                 load = deposit32(m->load, 0, 16, load);
2074f4c6206SPeter Maydell             } else {
2084f4c6206SPeter Maydell                 m->load = load;
2094f4c6206SPeter Maydell                 load &= 0xffff;
2104f4c6206SPeter Maydell             }
2114f4c6206SPeter Maydell         } else {
2124f4c6206SPeter Maydell             /* Free-running, timer limit is set to give wraparound */
2134f4c6206SPeter Maydell             if (newctrl & R_CONTROL_SIZE_MASK) {
2144f4c6206SPeter Maydell                 load = 0xffffffff;
2154f4c6206SPeter Maydell             } else {
2164f4c6206SPeter Maydell                 load = 0xffff;
2174f4c6206SPeter Maydell             }
2184f4c6206SPeter Maydell         }
2194f4c6206SPeter Maydell         ptimer_set_count(m->timer, value);
2204f4c6206SPeter Maydell         ptimer_set_limit(m->timer, load, 0);
2214f4c6206SPeter Maydell     }
2224f4c6206SPeter Maydell 
2234f4c6206SPeter Maydell     if (newctrl & R_CONTROL_ENABLE_MASK) {
2244f4c6206SPeter Maydell         /*
2254f4c6206SPeter Maydell          * ENABLE is set; start the timer after all other changes.
2264f4c6206SPeter Maydell          * We start it even if the ENABLE bit didn't actually change,
2274f4c6206SPeter Maydell          * in case the timer was an expired one-shot timer that has
2284f4c6206SPeter Maydell          * now been changed into a free-running or periodic timer.
2294f4c6206SPeter Maydell          */
2304f4c6206SPeter Maydell         ptimer_run(m->timer, !!(newctrl & R_CONTROL_ONESHOT_MASK));
2314f4c6206SPeter Maydell     }
2324f4c6206SPeter Maydell 
2334f4c6206SPeter Maydell     m->control = newctrl;
234da38e068SPeter Maydell 
235da38e068SPeter Maydell     ptimer_transaction_commit(m->timer);
2364f4c6206SPeter Maydell }
2374f4c6206SPeter Maydell 
cmsdk_apb_dualtimer_read(void * opaque,hwaddr offset,unsigned size)2384f4c6206SPeter Maydell static uint64_t cmsdk_apb_dualtimer_read(void *opaque, hwaddr offset,
2394f4c6206SPeter Maydell                                           unsigned size)
2404f4c6206SPeter Maydell {
2414f4c6206SPeter Maydell     CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
2424f4c6206SPeter Maydell     uint64_t r;
2434f4c6206SPeter Maydell 
2444f4c6206SPeter Maydell     if (offset >= A_TIMERITCR) {
2454f4c6206SPeter Maydell         switch (offset) {
2464f4c6206SPeter Maydell         case A_TIMERITCR:
2474f4c6206SPeter Maydell             r = s->timeritcr;
2484f4c6206SPeter Maydell             break;
2494f4c6206SPeter Maydell         case A_PID4 ... A_CID3:
2504f4c6206SPeter Maydell             r = timer_id[(offset - A_PID4) / 4];
2514f4c6206SPeter Maydell             break;
2524f4c6206SPeter Maydell         default:
2534f4c6206SPeter Maydell         bad_offset:
2544f4c6206SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR,
2554f4c6206SPeter Maydell                           "CMSDK APB dual-timer read: bad offset %x\n",
2564f4c6206SPeter Maydell                           (int) offset);
2574f4c6206SPeter Maydell             r = 0;
2584f4c6206SPeter Maydell             break;
2594f4c6206SPeter Maydell         }
2604f4c6206SPeter Maydell     } else {
2614f4c6206SPeter Maydell         int timer = offset >> 5;
2624f4c6206SPeter Maydell         CMSDKAPBDualTimerModule *m;
2634f4c6206SPeter Maydell 
2644f4c6206SPeter Maydell         if (timer >= ARRAY_SIZE(s->timermod)) {
2654f4c6206SPeter Maydell             goto bad_offset;
2664f4c6206SPeter Maydell         }
2674f4c6206SPeter Maydell 
2684f4c6206SPeter Maydell         m = &s->timermod[timer];
2694f4c6206SPeter Maydell 
2704f4c6206SPeter Maydell         switch (offset & 0x1F) {
2714f4c6206SPeter Maydell         case A_TIMER1LOAD:
2724f4c6206SPeter Maydell         case A_TIMER1BGLOAD:
2734f4c6206SPeter Maydell             if (m->control & R_CONTROL_MODE_MASK) {
2744f4c6206SPeter Maydell                 /*
2754f4c6206SPeter Maydell                  * Periodic: the ptimer limit is the LOAD register value, (or
2764f4c6206SPeter Maydell                  * just the low 16 bits of it if the timer is in 16-bit mode)
2774f4c6206SPeter Maydell                  */
2784f4c6206SPeter Maydell                 r = ptimer_get_limit(m->timer);
2794f4c6206SPeter Maydell                 if (!(m->control & R_CONTROL_SIZE_MASK)) {
2804f4c6206SPeter Maydell                     r = deposit32(m->load, 0, 16, r);
2814f4c6206SPeter Maydell                 }
2824f4c6206SPeter Maydell             } else {
2834f4c6206SPeter Maydell                 /* Free-running: LOAD register value is just in m->load */
2844f4c6206SPeter Maydell                 r = m->load;
2854f4c6206SPeter Maydell             }
2864f4c6206SPeter Maydell             break;
2874f4c6206SPeter Maydell         case A_TIMER1VALUE:
2884f4c6206SPeter Maydell             r = ptimer_get_count(m->timer);
2894f4c6206SPeter Maydell             if (!(m->control & R_CONTROL_SIZE_MASK)) {
2904f4c6206SPeter Maydell                 r = deposit32(m->value, 0, 16, r);
2914f4c6206SPeter Maydell             }
2924f4c6206SPeter Maydell             break;
2934f4c6206SPeter Maydell         case A_TIMER1CONTROL:
2944f4c6206SPeter Maydell             r = m->control;
2954f4c6206SPeter Maydell             break;
2964f4c6206SPeter Maydell         case A_TIMER1RIS:
2974f4c6206SPeter Maydell             r = m->intstatus;
2984f4c6206SPeter Maydell             break;
2994f4c6206SPeter Maydell         case A_TIMER1MIS:
3004f4c6206SPeter Maydell             r = cmsdk_dualtimermod_intstatus(m);
3014f4c6206SPeter Maydell             break;
3024f4c6206SPeter Maydell         default:
3034f4c6206SPeter Maydell             goto bad_offset;
3044f4c6206SPeter Maydell         }
3054f4c6206SPeter Maydell     }
3064f4c6206SPeter Maydell 
3074f4c6206SPeter Maydell     trace_cmsdk_apb_dualtimer_read(offset, r, size);
3084f4c6206SPeter Maydell     return r;
3094f4c6206SPeter Maydell }
3104f4c6206SPeter Maydell 
cmsdk_apb_dualtimer_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)3114f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset,
3124f4c6206SPeter Maydell                                        uint64_t value, unsigned size)
3134f4c6206SPeter Maydell {
3144f4c6206SPeter Maydell     CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
3154f4c6206SPeter Maydell 
3164f4c6206SPeter Maydell     trace_cmsdk_apb_dualtimer_write(offset, value, size);
3174f4c6206SPeter Maydell 
3184f4c6206SPeter Maydell     if (offset >= A_TIMERITCR) {
3194f4c6206SPeter Maydell         switch (offset) {
3204f4c6206SPeter Maydell         case A_TIMERITCR:
3214f4c6206SPeter Maydell             s->timeritcr = value & R_TIMERITCR_VALID_MASK;
3224f4c6206SPeter Maydell             cmsdk_apb_dualtimer_update(s);
3233e1dd459SPeter Maydell             break;
3244f4c6206SPeter Maydell         case A_TIMERITOP:
3254f4c6206SPeter Maydell             s->timeritop = value & R_TIMERITOP_VALID_MASK;
3264f4c6206SPeter Maydell             cmsdk_apb_dualtimer_update(s);
3273e1dd459SPeter Maydell             break;
3284f4c6206SPeter Maydell         default:
3294f4c6206SPeter Maydell         bad_offset:
3304f4c6206SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR,
3314f4c6206SPeter Maydell                           "CMSDK APB dual-timer write: bad offset %x\n",
3324f4c6206SPeter Maydell                           (int) offset);
3334f4c6206SPeter Maydell             break;
3344f4c6206SPeter Maydell         }
3354f4c6206SPeter Maydell     } else {
3364f4c6206SPeter Maydell         int timer = offset >> 5;
3374f4c6206SPeter Maydell         CMSDKAPBDualTimerModule *m;
3384f4c6206SPeter Maydell 
3394f4c6206SPeter Maydell         if (timer >= ARRAY_SIZE(s->timermod)) {
3404f4c6206SPeter Maydell             goto bad_offset;
3414f4c6206SPeter Maydell         }
3424f4c6206SPeter Maydell 
3434f4c6206SPeter Maydell         m = &s->timermod[timer];
3444f4c6206SPeter Maydell 
3454f4c6206SPeter Maydell         switch (offset & 0x1F) {
3464f4c6206SPeter Maydell         case A_TIMER1LOAD:
3474f4c6206SPeter Maydell             /* Set the limit, and immediately reload the count from it */
3484f4c6206SPeter Maydell             m->load = value;
3494f4c6206SPeter Maydell             m->value = value;
3504f4c6206SPeter Maydell             if (!(m->control & R_CONTROL_SIZE_MASK)) {
3514f4c6206SPeter Maydell                 value &= 0xffff;
3524f4c6206SPeter Maydell             }
353da38e068SPeter Maydell             ptimer_transaction_begin(m->timer);
3544f4c6206SPeter Maydell             if (!(m->control & R_CONTROL_MODE_MASK)) {
3554f4c6206SPeter Maydell                 /*
3564f4c6206SPeter Maydell                  * In free-running mode this won't set the limit but will
3574f4c6206SPeter Maydell                  * still change the current count value.
3584f4c6206SPeter Maydell                  */
3594f4c6206SPeter Maydell                 ptimer_set_count(m->timer, value);
3604f4c6206SPeter Maydell             } else {
3614f4c6206SPeter Maydell                 if (!value) {
3624f4c6206SPeter Maydell                     ptimer_stop(m->timer);
3634f4c6206SPeter Maydell                 }
3644f4c6206SPeter Maydell                 ptimer_set_limit(m->timer, value, 1);
3654f4c6206SPeter Maydell                 if (value && (m->control & R_CONTROL_ENABLE_MASK)) {
3664f4c6206SPeter Maydell                     /* Force possibly-expired oneshot timer to restart */
3674f4c6206SPeter Maydell                     ptimer_run(m->timer, 1);
3684f4c6206SPeter Maydell                 }
3694f4c6206SPeter Maydell             }
370da38e068SPeter Maydell             ptimer_transaction_commit(m->timer);
3714f4c6206SPeter Maydell             break;
3724f4c6206SPeter Maydell         case A_TIMER1BGLOAD:
3734f4c6206SPeter Maydell             /* Set the limit, but not the current count */
3744f4c6206SPeter Maydell             m->load = value;
3754f4c6206SPeter Maydell             if (!(m->control & R_CONTROL_MODE_MASK)) {
3764f4c6206SPeter Maydell                 /* In free-running mode there is no limit */
3774f4c6206SPeter Maydell                 break;
3784f4c6206SPeter Maydell             }
3794f4c6206SPeter Maydell             if (!(m->control & R_CONTROL_SIZE_MASK)) {
3804f4c6206SPeter Maydell                 value &= 0xffff;
3814f4c6206SPeter Maydell             }
382da38e068SPeter Maydell             ptimer_transaction_begin(m->timer);
3834f4c6206SPeter Maydell             ptimer_set_limit(m->timer, value, 0);
384da38e068SPeter Maydell             ptimer_transaction_commit(m->timer);
3854f4c6206SPeter Maydell             break;
3864f4c6206SPeter Maydell         case A_TIMER1CONTROL:
3874f4c6206SPeter Maydell             cmsdk_dualtimermod_write_control(m, value);
3884f4c6206SPeter Maydell             cmsdk_apb_dualtimer_update(s);
3894f4c6206SPeter Maydell             break;
3904f4c6206SPeter Maydell         case A_TIMER1INTCLR:
3914f4c6206SPeter Maydell             m->intstatus = 0;
3924f4c6206SPeter Maydell             cmsdk_apb_dualtimer_update(s);
3934f4c6206SPeter Maydell             break;
3944f4c6206SPeter Maydell         default:
3954f4c6206SPeter Maydell             goto bad_offset;
3964f4c6206SPeter Maydell         }
3974f4c6206SPeter Maydell     }
3984f4c6206SPeter Maydell }
3994f4c6206SPeter Maydell 
4004f4c6206SPeter Maydell static const MemoryRegionOps cmsdk_apb_dualtimer_ops = {
4014f4c6206SPeter Maydell     .read = cmsdk_apb_dualtimer_read,
4024f4c6206SPeter Maydell     .write = cmsdk_apb_dualtimer_write,
4034f4c6206SPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
4044f4c6206SPeter Maydell     /* byte/halfword accesses are just zero-padded on reads and writes */
4054f4c6206SPeter Maydell     .impl.min_access_size = 4,
4064f4c6206SPeter Maydell     .impl.max_access_size = 4,
4074f4c6206SPeter Maydell     .valid.min_access_size = 1,
4084f4c6206SPeter Maydell     .valid.max_access_size = 4,
4094f4c6206SPeter Maydell };
4104f4c6206SPeter Maydell 
cmsdk_dualtimermod_tick(void * opaque)4114f4c6206SPeter Maydell static void cmsdk_dualtimermod_tick(void *opaque)
4124f4c6206SPeter Maydell {
4134f4c6206SPeter Maydell     CMSDKAPBDualTimerModule *m = opaque;
4144f4c6206SPeter Maydell 
4154f4c6206SPeter Maydell     m->intstatus = 1;
4164f4c6206SPeter Maydell     cmsdk_apb_dualtimer_update(m->parent);
4174f4c6206SPeter Maydell }
4184f4c6206SPeter Maydell 
cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule * m)4194f4c6206SPeter Maydell static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m)
4204f4c6206SPeter Maydell {
4214f4c6206SPeter Maydell     m->control = R_CONTROL_INTEN_MASK;
4224f4c6206SPeter Maydell     m->intstatus = 0;
4234f4c6206SPeter Maydell     m->load = 0;
4244f4c6206SPeter Maydell     m->value = 0xffffffff;
425da38e068SPeter Maydell     ptimer_transaction_begin(m->timer);
4264f4c6206SPeter Maydell     ptimer_stop(m->timer);
4274f4c6206SPeter Maydell     /*
4284f4c6206SPeter Maydell      * We start in free-running mode, with VALUE at 0xffffffff, and
4294f4c6206SPeter Maydell      * in 16-bit counter mode. This means that the ptimer count and
4304f4c6206SPeter Maydell      * limit must both be set to 0xffff, so we wrap at 16 bits.
4314f4c6206SPeter Maydell      */
4324f4c6206SPeter Maydell     ptimer_set_limit(m->timer, 0xffff, 1);
4337208aafbSPeter Maydell     ptimer_set_period_from_clock(m->timer, m->parent->timclk,
4347208aafbSPeter Maydell                                  cmsdk_dualtimermod_divisor(m));
435da38e068SPeter Maydell     ptimer_transaction_commit(m->timer);
4364f4c6206SPeter Maydell }
4374f4c6206SPeter Maydell 
cmsdk_apb_dualtimer_reset(DeviceState * dev)4384f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_reset(DeviceState *dev)
4394f4c6206SPeter Maydell {
4404f4c6206SPeter Maydell     CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev);
4414f4c6206SPeter Maydell     int i;
4424f4c6206SPeter Maydell 
4434f4c6206SPeter Maydell     trace_cmsdk_apb_dualtimer_reset();
4444f4c6206SPeter Maydell 
4454f4c6206SPeter Maydell     for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
4464f4c6206SPeter Maydell         cmsdk_dualtimermod_reset(&s->timermod[i]);
4474f4c6206SPeter Maydell     }
4484f4c6206SPeter Maydell     s->timeritcr = 0;
4494f4c6206SPeter Maydell     s->timeritop = 0;
4504f4c6206SPeter Maydell }
4514f4c6206SPeter Maydell 
cmsdk_apb_dualtimer_clk_update(void * opaque,ClockEvent event)4525ee0abedSPeter Maydell static void cmsdk_apb_dualtimer_clk_update(void *opaque, ClockEvent event)
4537208aafbSPeter Maydell {
4547208aafbSPeter Maydell     CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
4557208aafbSPeter Maydell     int i;
4567208aafbSPeter Maydell 
4577208aafbSPeter Maydell     for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
4587208aafbSPeter Maydell         CMSDKAPBDualTimerModule *m = &s->timermod[i];
4597208aafbSPeter Maydell         ptimer_transaction_begin(m->timer);
4607208aafbSPeter Maydell         ptimer_set_period_from_clock(m->timer, m->parent->timclk,
4617208aafbSPeter Maydell                                      cmsdk_dualtimermod_divisor(m));
4627208aafbSPeter Maydell         ptimer_transaction_commit(m->timer);
4637208aafbSPeter Maydell     }
4647208aafbSPeter Maydell }
4657208aafbSPeter Maydell 
cmsdk_apb_dualtimer_init(Object * obj)4664f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_init(Object *obj)
4674f4c6206SPeter Maydell {
4684f4c6206SPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
4694f4c6206SPeter Maydell     CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(obj);
4704f4c6206SPeter Maydell     int i;
4714f4c6206SPeter Maydell 
4724f4c6206SPeter Maydell     memory_region_init_io(&s->iomem, obj, &cmsdk_apb_dualtimer_ops,
4734f4c6206SPeter Maydell                           s, "cmsdk-apb-dualtimer", 0x1000);
4744f4c6206SPeter Maydell     sysbus_init_mmio(sbd, &s->iomem);
4754f4c6206SPeter Maydell     sysbus_init_irq(sbd, &s->timerintc);
4764f4c6206SPeter Maydell 
4774f4c6206SPeter Maydell     for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
4784f4c6206SPeter Maydell         sysbus_init_irq(sbd, &s->timermod[i].timerint);
4794f4c6206SPeter Maydell     }
4807208aafbSPeter Maydell     s->timclk = qdev_init_clock_in(DEVICE(s), "TIMCLK",
4815ee0abedSPeter Maydell                                    cmsdk_apb_dualtimer_clk_update, s,
4825ee0abedSPeter Maydell                                    ClockUpdate);
4834f4c6206SPeter Maydell }
4844f4c6206SPeter Maydell 
cmsdk_apb_dualtimer_realize(DeviceState * dev,Error ** errp)4854f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp)
4864f4c6206SPeter Maydell {
4874f4c6206SPeter Maydell     CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev);
4884f4c6206SPeter Maydell     int i;
4894f4c6206SPeter Maydell 
4907208aafbSPeter Maydell     if (!clock_has_source(s->timclk)) {
4917208aafbSPeter Maydell         error_setg(errp, "CMSDK APB dualtimer: TIMCLK clock must be connected");
4924f4c6206SPeter Maydell         return;
4934f4c6206SPeter Maydell     }
4944f4c6206SPeter Maydell 
4954f4c6206SPeter Maydell     for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
4964f4c6206SPeter Maydell         CMSDKAPBDualTimerModule *m = &s->timermod[i];
4974f4c6206SPeter Maydell 
4984f4c6206SPeter Maydell         m->parent = s;
499da38e068SPeter Maydell         m->timer = ptimer_init(cmsdk_dualtimermod_tick, m,
5004f4c6206SPeter Maydell                                PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
5014f4c6206SPeter Maydell                                PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
5024f4c6206SPeter Maydell                                PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
5034f4c6206SPeter Maydell                                PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
5044f4c6206SPeter Maydell     }
5054f4c6206SPeter Maydell }
5064f4c6206SPeter Maydell 
5074f4c6206SPeter Maydell static const VMStateDescription cmsdk_dualtimermod_vmstate = {
5084f4c6206SPeter Maydell     .name = "cmsdk-apb-dualtimer-module",
5094f4c6206SPeter Maydell     .version_id = 1,
5104f4c6206SPeter Maydell     .minimum_version_id = 1,
511ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
5124f4c6206SPeter Maydell         VMSTATE_PTIMER(timer, CMSDKAPBDualTimerModule),
5134f4c6206SPeter Maydell         VMSTATE_UINT32(load, CMSDKAPBDualTimerModule),
5144f4c6206SPeter Maydell         VMSTATE_UINT32(value, CMSDKAPBDualTimerModule),
5154f4c6206SPeter Maydell         VMSTATE_UINT32(control, CMSDKAPBDualTimerModule),
5164f4c6206SPeter Maydell         VMSTATE_UINT32(intstatus, CMSDKAPBDualTimerModule),
5174f4c6206SPeter Maydell         VMSTATE_END_OF_LIST()
5184f4c6206SPeter Maydell     }
5194f4c6206SPeter Maydell };
5204f4c6206SPeter Maydell 
5214f4c6206SPeter Maydell static const VMStateDescription cmsdk_apb_dualtimer_vmstate = {
5224f4c6206SPeter Maydell     .name = "cmsdk-apb-dualtimer",
52355fd0f84SPeter Maydell     .version_id = 2,
52455fd0f84SPeter Maydell     .minimum_version_id = 2,
525ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
52655fd0f84SPeter Maydell         VMSTATE_CLOCK(timclk, CMSDKAPBDualTimer),
5274f4c6206SPeter Maydell         VMSTATE_STRUCT_ARRAY(timermod, CMSDKAPBDualTimer,
5284f4c6206SPeter Maydell                              CMSDK_APB_DUALTIMER_NUM_MODULES,
5294f4c6206SPeter Maydell                              1, cmsdk_dualtimermod_vmstate,
5304f4c6206SPeter Maydell                              CMSDKAPBDualTimerModule),
5314f4c6206SPeter Maydell         VMSTATE_UINT32(timeritcr, CMSDKAPBDualTimer),
5324f4c6206SPeter Maydell         VMSTATE_UINT32(timeritop, CMSDKAPBDualTimer),
5334f4c6206SPeter Maydell         VMSTATE_END_OF_LIST()
5344f4c6206SPeter Maydell     }
5354f4c6206SPeter Maydell };
5364f4c6206SPeter Maydell 
cmsdk_apb_dualtimer_class_init(ObjectClass * klass,const void * data)537*12d1a768SPhilippe Mathieu-Daudé static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, const void *data)
5384f4c6206SPeter Maydell {
5394f4c6206SPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
5404f4c6206SPeter Maydell 
5414f4c6206SPeter Maydell     dc->realize = cmsdk_apb_dualtimer_realize;
5424f4c6206SPeter Maydell     dc->vmsd = &cmsdk_apb_dualtimer_vmstate;
543e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, cmsdk_apb_dualtimer_reset);
5444f4c6206SPeter Maydell }
5454f4c6206SPeter Maydell 
5464f4c6206SPeter Maydell static const TypeInfo cmsdk_apb_dualtimer_info = {
5474f4c6206SPeter Maydell     .name = TYPE_CMSDK_APB_DUALTIMER,
5484f4c6206SPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
5494f4c6206SPeter Maydell     .instance_size = sizeof(CMSDKAPBDualTimer),
5504f4c6206SPeter Maydell     .instance_init = cmsdk_apb_dualtimer_init,
5514f4c6206SPeter Maydell     .class_init = cmsdk_apb_dualtimer_class_init,
5524f4c6206SPeter Maydell };
5534f4c6206SPeter Maydell 
cmsdk_apb_dualtimer_register_types(void)5544f4c6206SPeter Maydell static void cmsdk_apb_dualtimer_register_types(void)
5554f4c6206SPeter Maydell {
5564f4c6206SPeter Maydell     type_register_static(&cmsdk_apb_dualtimer_info);
5574f4c6206SPeter Maydell }
5584f4c6206SPeter Maydell 
5594f4c6206SPeter Maydell type_init(cmsdk_apb_dualtimer_register_types);
560