xref: /qemu/hw/watchdog/cmsdk-apb-watchdog.c (revision e3d0814368d00e7985c31edf5d0cfce45972d4be)
1050c2ea0SPeter Maydell /*
2050c2ea0SPeter Maydell  * ARM CMSDK APB watchdog emulation
3050c2ea0SPeter Maydell  *
4050c2ea0SPeter Maydell  * Copyright (c) 2018 Linaro Limited
5050c2ea0SPeter Maydell  * Written by Peter Maydell
6050c2ea0SPeter Maydell  *
7050c2ea0SPeter Maydell  *  This program is free software; you can redistribute it and/or modify
8050c2ea0SPeter Maydell  *  it under the terms of the GNU General Public License version 2 or
9050c2ea0SPeter Maydell  *  (at your option) any later version.
10050c2ea0SPeter Maydell  */
11050c2ea0SPeter Maydell 
12050c2ea0SPeter Maydell /*
13050c2ea0SPeter Maydell  * This is a model of the "APB watchdog" which is part of the Cortex-M
14050c2ea0SPeter Maydell  * System Design Kit (CMSDK) and documented in the Cortex-M System
15050c2ea0SPeter Maydell  * Design Kit Technical Reference Manual (ARM DDI0479C):
16050c2ea0SPeter Maydell  * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
17566528f8SMichel Heily  *
18566528f8SMichel Heily  * We also support the variant of this device found in the TI
19566528f8SMichel Heily  * Stellaris/Luminary boards and documented in:
20566528f8SMichel Heily  * http://www.ti.com/lit/ds/symlink/lm3s6965.pdf
21050c2ea0SPeter Maydell  */
22050c2ea0SPeter Maydell 
23050c2ea0SPeter Maydell #include "qemu/osdep.h"
24050c2ea0SPeter Maydell #include "qemu/log.h"
25050c2ea0SPeter Maydell #include "trace.h"
26050c2ea0SPeter Maydell #include "qapi/error.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
28050c2ea0SPeter Maydell #include "sysemu/watchdog.h"
29050c2ea0SPeter Maydell #include "hw/sysbus.h"
3064552b6bSMarkus Armbruster #include "hw/irq.h"
31a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
32050c2ea0SPeter Maydell #include "hw/registerfields.h"
33eeae0b2bSPeter Maydell #include "hw/qdev-clock.h"
34050c2ea0SPeter Maydell #include "hw/watchdog/cmsdk-apb-watchdog.h"
35d6454270SMarkus Armbruster #include "migration/vmstate.h"
36050c2ea0SPeter Maydell 
37050c2ea0SPeter Maydell REG32(WDOGLOAD, 0x0)
38050c2ea0SPeter Maydell REG32(WDOGVALUE, 0x4)
39050c2ea0SPeter Maydell REG32(WDOGCONTROL, 0x8)
40050c2ea0SPeter Maydell     FIELD(WDOGCONTROL, INTEN, 0, 1)
41050c2ea0SPeter Maydell     FIELD(WDOGCONTROL, RESEN, 1, 1)
42050c2ea0SPeter Maydell #define R_WDOGCONTROL_VALID_MASK (R_WDOGCONTROL_INTEN_MASK | \
43050c2ea0SPeter Maydell                                   R_WDOGCONTROL_RESEN_MASK)
44050c2ea0SPeter Maydell REG32(WDOGINTCLR, 0xc)
45050c2ea0SPeter Maydell REG32(WDOGRIS, 0x10)
46050c2ea0SPeter Maydell     FIELD(WDOGRIS, INT, 0, 1)
47050c2ea0SPeter Maydell REG32(WDOGMIS, 0x14)
48566528f8SMichel Heily REG32(WDOGTEST, 0x418) /* only in Stellaris/Luminary version of the device */
49050c2ea0SPeter Maydell REG32(WDOGLOCK, 0xc00)
50050c2ea0SPeter Maydell #define WDOG_UNLOCK_VALUE 0x1ACCE551
51050c2ea0SPeter Maydell REG32(WDOGITCR, 0xf00)
52050c2ea0SPeter Maydell     FIELD(WDOGITCR, ENABLE, 0, 1)
53050c2ea0SPeter Maydell #define R_WDOGITCR_VALID_MASK R_WDOGITCR_ENABLE_MASK
54050c2ea0SPeter Maydell REG32(WDOGITOP, 0xf04)
55050c2ea0SPeter Maydell     FIELD(WDOGITOP, WDOGRES, 0, 1)
56050c2ea0SPeter Maydell     FIELD(WDOGITOP, WDOGINT, 1, 1)
57050c2ea0SPeter Maydell #define R_WDOGITOP_VALID_MASK (R_WDOGITOP_WDOGRES_MASK | \
58050c2ea0SPeter Maydell                                R_WDOGITOP_WDOGINT_MASK)
59050c2ea0SPeter Maydell REG32(PID4, 0xfd0)
60050c2ea0SPeter Maydell REG32(PID5, 0xfd4)
61050c2ea0SPeter Maydell REG32(PID6, 0xfd8)
62050c2ea0SPeter Maydell REG32(PID7, 0xfdc)
63050c2ea0SPeter Maydell REG32(PID0, 0xfe0)
64050c2ea0SPeter Maydell REG32(PID1, 0xfe4)
65050c2ea0SPeter Maydell REG32(PID2, 0xfe8)
66050c2ea0SPeter Maydell REG32(PID3, 0xfec)
67050c2ea0SPeter Maydell REG32(CID0, 0xff0)
68050c2ea0SPeter Maydell REG32(CID1, 0xff4)
69050c2ea0SPeter Maydell REG32(CID2, 0xff8)
70050c2ea0SPeter Maydell REG32(CID3, 0xffc)
71050c2ea0SPeter Maydell 
72050c2ea0SPeter Maydell /* PID/CID values */
73566528f8SMichel Heily static const uint32_t cmsdk_apb_watchdog_id[] = {
74050c2ea0SPeter Maydell     0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
75050c2ea0SPeter Maydell     0x24, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
76050c2ea0SPeter Maydell     0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
77050c2ea0SPeter Maydell };
78050c2ea0SPeter Maydell 
79566528f8SMichel Heily static const uint32_t luminary_watchdog_id[] = {
80566528f8SMichel Heily     0x00, 0x00, 0x00, 0x00, /* PID4..PID7 */
81566528f8SMichel Heily     0x05, 0x18, 0x18, 0x01, /* PID0..PID3 */
82566528f8SMichel Heily     0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
83566528f8SMichel Heily };
84566528f8SMichel Heily 
85050c2ea0SPeter Maydell static bool cmsdk_apb_watchdog_intstatus(CMSDKAPBWatchdog *s)
86050c2ea0SPeter Maydell {
87050c2ea0SPeter Maydell     /* Return masked interrupt status */
88050c2ea0SPeter Maydell     return s->intstatus && (s->control & R_WDOGCONTROL_INTEN_MASK);
89050c2ea0SPeter Maydell }
90050c2ea0SPeter Maydell 
91050c2ea0SPeter Maydell static bool cmsdk_apb_watchdog_resetstatus(CMSDKAPBWatchdog *s)
92050c2ea0SPeter Maydell {
93050c2ea0SPeter Maydell     /* Return masked reset status */
94050c2ea0SPeter Maydell     return s->resetstatus && (s->control & R_WDOGCONTROL_RESEN_MASK);
95050c2ea0SPeter Maydell }
96050c2ea0SPeter Maydell 
97050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_update(CMSDKAPBWatchdog *s)
98050c2ea0SPeter Maydell {
99050c2ea0SPeter Maydell     bool wdogint;
100050c2ea0SPeter Maydell     bool wdogres;
101050c2ea0SPeter Maydell 
102050c2ea0SPeter Maydell     if (s->itcr) {
103566528f8SMichel Heily         /*
104566528f8SMichel Heily          * Not checking that !s->is_luminary since s->itcr can't be written
105566528f8SMichel Heily          * when s->is_luminary in the first place.
106566528f8SMichel Heily          */
107050c2ea0SPeter Maydell         wdogint = s->itop & R_WDOGITOP_WDOGINT_MASK;
108050c2ea0SPeter Maydell         wdogres = s->itop & R_WDOGITOP_WDOGRES_MASK;
109050c2ea0SPeter Maydell     } else {
110050c2ea0SPeter Maydell         wdogint = cmsdk_apb_watchdog_intstatus(s);
111050c2ea0SPeter Maydell         wdogres = cmsdk_apb_watchdog_resetstatus(s);
112050c2ea0SPeter Maydell     }
113050c2ea0SPeter Maydell 
114050c2ea0SPeter Maydell     qemu_set_irq(s->wdogint, wdogint);
115050c2ea0SPeter Maydell     if (wdogres) {
116050c2ea0SPeter Maydell         watchdog_perform_action();
117050c2ea0SPeter Maydell     }
118050c2ea0SPeter Maydell }
119050c2ea0SPeter Maydell 
120050c2ea0SPeter Maydell static uint64_t cmsdk_apb_watchdog_read(void *opaque, hwaddr offset,
121050c2ea0SPeter Maydell                                         unsigned size)
122050c2ea0SPeter Maydell {
123050c2ea0SPeter Maydell     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
124050c2ea0SPeter Maydell     uint64_t r;
125050c2ea0SPeter Maydell 
126050c2ea0SPeter Maydell     switch (offset) {
127050c2ea0SPeter Maydell     case A_WDOGLOAD:
128050c2ea0SPeter Maydell         r = ptimer_get_limit(s->timer);
129050c2ea0SPeter Maydell         break;
130050c2ea0SPeter Maydell     case A_WDOGVALUE:
131050c2ea0SPeter Maydell         r = ptimer_get_count(s->timer);
132050c2ea0SPeter Maydell         break;
133050c2ea0SPeter Maydell     case A_WDOGCONTROL:
134050c2ea0SPeter Maydell         r = s->control;
135050c2ea0SPeter Maydell         break;
136050c2ea0SPeter Maydell     case A_WDOGRIS:
137050c2ea0SPeter Maydell         r = s->intstatus;
138050c2ea0SPeter Maydell         break;
139050c2ea0SPeter Maydell     case A_WDOGMIS:
140050c2ea0SPeter Maydell         r = cmsdk_apb_watchdog_intstatus(s);
141050c2ea0SPeter Maydell         break;
142050c2ea0SPeter Maydell     case A_WDOGLOCK:
143050c2ea0SPeter Maydell         r = s->lock;
144050c2ea0SPeter Maydell         break;
145050c2ea0SPeter Maydell     case A_WDOGITCR:
146566528f8SMichel Heily         if (s->is_luminary) {
147566528f8SMichel Heily             goto bad_offset;
148566528f8SMichel Heily         }
149050c2ea0SPeter Maydell         r = s->itcr;
150050c2ea0SPeter Maydell         break;
151050c2ea0SPeter Maydell     case A_PID4 ... A_CID3:
152566528f8SMichel Heily         r = s->id[(offset - A_PID4) / 4];
153050c2ea0SPeter Maydell         break;
154050c2ea0SPeter Maydell     case A_WDOGINTCLR:
155050c2ea0SPeter Maydell     case A_WDOGITOP:
156566528f8SMichel Heily         if (s->is_luminary) {
157566528f8SMichel Heily             goto bad_offset;
158566528f8SMichel Heily         }
159050c2ea0SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
160050c2ea0SPeter Maydell                       "CMSDK APB watchdog read: read of WO offset %x\n",
161050c2ea0SPeter Maydell                       (int)offset);
162050c2ea0SPeter Maydell         r = 0;
163050c2ea0SPeter Maydell         break;
164566528f8SMichel Heily     case A_WDOGTEST:
165566528f8SMichel Heily         if (!s->is_luminary) {
166566528f8SMichel Heily             goto bad_offset;
167566528f8SMichel Heily         }
168566528f8SMichel Heily         qemu_log_mask(LOG_UNIMP,
169566528f8SMichel Heily                       "Luminary watchdog read: stall not implemented\n");
170566528f8SMichel Heily         r = 0;
171566528f8SMichel Heily         break;
172050c2ea0SPeter Maydell     default:
173566528f8SMichel Heily bad_offset:
174050c2ea0SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
175050c2ea0SPeter Maydell                       "CMSDK APB watchdog read: bad offset %x\n", (int)offset);
176050c2ea0SPeter Maydell         r = 0;
177050c2ea0SPeter Maydell         break;
178050c2ea0SPeter Maydell     }
179050c2ea0SPeter Maydell     trace_cmsdk_apb_watchdog_read(offset, r, size);
180050c2ea0SPeter Maydell     return r;
181050c2ea0SPeter Maydell }
182050c2ea0SPeter Maydell 
183050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset,
184050c2ea0SPeter Maydell                                      uint64_t value, unsigned size)
185050c2ea0SPeter Maydell {
186050c2ea0SPeter Maydell     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
187050c2ea0SPeter Maydell 
188050c2ea0SPeter Maydell     trace_cmsdk_apb_watchdog_write(offset, value, size);
189050c2ea0SPeter Maydell 
190050c2ea0SPeter Maydell     if (s->lock && offset != A_WDOGLOCK) {
191050c2ea0SPeter Maydell         /* Write access is disabled via WDOGLOCK */
192050c2ea0SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
193050c2ea0SPeter Maydell                       "CMSDK APB watchdog write: write to locked watchdog\n");
194050c2ea0SPeter Maydell         return;
195050c2ea0SPeter Maydell     }
196050c2ea0SPeter Maydell 
197050c2ea0SPeter Maydell     switch (offset) {
198050c2ea0SPeter Maydell     case A_WDOGLOAD:
199050c2ea0SPeter Maydell         /*
200050c2ea0SPeter Maydell          * Reset the load value and the current count, and make sure
201050c2ea0SPeter Maydell          * we're counting.
202050c2ea0SPeter Maydell          */
2038c9dbc62SPeter Maydell         ptimer_transaction_begin(s->timer);
204050c2ea0SPeter Maydell         ptimer_set_limit(s->timer, value, 1);
205050c2ea0SPeter Maydell         ptimer_run(s->timer, 0);
2068c9dbc62SPeter Maydell         ptimer_transaction_commit(s->timer);
207050c2ea0SPeter Maydell         break;
208050c2ea0SPeter Maydell     case A_WDOGCONTROL:
209566528f8SMichel Heily         if (s->is_luminary && 0 != (R_WDOGCONTROL_INTEN_MASK & s->control)) {
210566528f8SMichel Heily             /*
211566528f8SMichel Heily              * The Luminary version of this device ignores writes to
212566528f8SMichel Heily              * this register after the guest has enabled interrupts
213566528f8SMichel Heily              * (so they can only be disabled again via reset).
214566528f8SMichel Heily              */
215566528f8SMichel Heily             break;
216566528f8SMichel Heily         }
217050c2ea0SPeter Maydell         s->control = value & R_WDOGCONTROL_VALID_MASK;
218050c2ea0SPeter Maydell         cmsdk_apb_watchdog_update(s);
219050c2ea0SPeter Maydell         break;
220050c2ea0SPeter Maydell     case A_WDOGINTCLR:
221050c2ea0SPeter Maydell         s->intstatus = 0;
2228c9dbc62SPeter Maydell         ptimer_transaction_begin(s->timer);
223050c2ea0SPeter Maydell         ptimer_set_count(s->timer, ptimer_get_limit(s->timer));
2248c9dbc62SPeter Maydell         ptimer_transaction_commit(s->timer);
225050c2ea0SPeter Maydell         cmsdk_apb_watchdog_update(s);
226050c2ea0SPeter Maydell         break;
227050c2ea0SPeter Maydell     case A_WDOGLOCK:
228050c2ea0SPeter Maydell         s->lock = (value != WDOG_UNLOCK_VALUE);
22969ed08e4SPhilippe Mathieu-Daudé         trace_cmsdk_apb_watchdog_lock(s->lock);
230050c2ea0SPeter Maydell         break;
231050c2ea0SPeter Maydell     case A_WDOGITCR:
232566528f8SMichel Heily         if (s->is_luminary) {
233566528f8SMichel Heily             goto bad_offset;
234566528f8SMichel Heily         }
235050c2ea0SPeter Maydell         s->itcr = value & R_WDOGITCR_VALID_MASK;
236050c2ea0SPeter Maydell         cmsdk_apb_watchdog_update(s);
237050c2ea0SPeter Maydell         break;
238050c2ea0SPeter Maydell     case A_WDOGITOP:
239566528f8SMichel Heily         if (s->is_luminary) {
240566528f8SMichel Heily             goto bad_offset;
241566528f8SMichel Heily         }
242050c2ea0SPeter Maydell         s->itop = value & R_WDOGITOP_VALID_MASK;
243050c2ea0SPeter Maydell         cmsdk_apb_watchdog_update(s);
244050c2ea0SPeter Maydell         break;
245050c2ea0SPeter Maydell     case A_WDOGVALUE:
246050c2ea0SPeter Maydell     case A_WDOGRIS:
247050c2ea0SPeter Maydell     case A_WDOGMIS:
248050c2ea0SPeter Maydell     case A_PID4 ... A_CID3:
249050c2ea0SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
250050c2ea0SPeter Maydell                       "CMSDK APB watchdog write: write to RO offset 0x%x\n",
251050c2ea0SPeter Maydell                       (int)offset);
252050c2ea0SPeter Maydell         break;
253566528f8SMichel Heily     case A_WDOGTEST:
254566528f8SMichel Heily         if (!s->is_luminary) {
255566528f8SMichel Heily             goto bad_offset;
256566528f8SMichel Heily         }
257566528f8SMichel Heily         qemu_log_mask(LOG_UNIMP,
258566528f8SMichel Heily                       "Luminary watchdog write: stall not implemented\n");
259566528f8SMichel Heily         break;
260050c2ea0SPeter Maydell     default:
261566528f8SMichel Heily bad_offset:
262050c2ea0SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
263050c2ea0SPeter Maydell                       "CMSDK APB watchdog write: bad offset 0x%x\n",
264050c2ea0SPeter Maydell                       (int)offset);
265050c2ea0SPeter Maydell         break;
266050c2ea0SPeter Maydell     }
267050c2ea0SPeter Maydell }
268050c2ea0SPeter Maydell 
269050c2ea0SPeter Maydell static const MemoryRegionOps cmsdk_apb_watchdog_ops = {
270050c2ea0SPeter Maydell     .read = cmsdk_apb_watchdog_read,
271050c2ea0SPeter Maydell     .write = cmsdk_apb_watchdog_write,
272050c2ea0SPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
273050c2ea0SPeter Maydell     /* byte/halfword accesses are just zero-padded on reads and writes */
274050c2ea0SPeter Maydell     .impl.min_access_size = 4,
275050c2ea0SPeter Maydell     .impl.max_access_size = 4,
276050c2ea0SPeter Maydell     .valid.min_access_size = 1,
277050c2ea0SPeter Maydell     .valid.max_access_size = 4,
278050c2ea0SPeter Maydell };
279050c2ea0SPeter Maydell 
280050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_tick(void *opaque)
281050c2ea0SPeter Maydell {
282050c2ea0SPeter Maydell     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
283050c2ea0SPeter Maydell 
284050c2ea0SPeter Maydell     if (!s->intstatus) {
285050c2ea0SPeter Maydell         /* Count expired for the first time: raise interrupt */
286050c2ea0SPeter Maydell         s->intstatus = R_WDOGRIS_INT_MASK;
287050c2ea0SPeter Maydell     } else {
288050c2ea0SPeter Maydell         /* Count expired for the second time: raise reset and stop clock */
289050c2ea0SPeter Maydell         s->resetstatus = 1;
290050c2ea0SPeter Maydell         ptimer_stop(s->timer);
291050c2ea0SPeter Maydell     }
292050c2ea0SPeter Maydell     cmsdk_apb_watchdog_update(s);
293050c2ea0SPeter Maydell }
294050c2ea0SPeter Maydell 
295050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_reset(DeviceState *dev)
296050c2ea0SPeter Maydell {
297050c2ea0SPeter Maydell     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev);
298050c2ea0SPeter Maydell 
299050c2ea0SPeter Maydell     trace_cmsdk_apb_watchdog_reset();
300050c2ea0SPeter Maydell     s->control = 0;
301050c2ea0SPeter Maydell     s->intstatus = 0;
302050c2ea0SPeter Maydell     s->lock = 0;
303050c2ea0SPeter Maydell     s->itcr = 0;
304050c2ea0SPeter Maydell     s->itop = 0;
305050c2ea0SPeter Maydell     s->resetstatus = 0;
306050c2ea0SPeter Maydell     /* Set the limit and the count */
3078c9dbc62SPeter Maydell     ptimer_transaction_begin(s->timer);
308050c2ea0SPeter Maydell     ptimer_set_limit(s->timer, 0xffffffff, 1);
309050c2ea0SPeter Maydell     ptimer_run(s->timer, 0);
3108c9dbc62SPeter Maydell     ptimer_transaction_commit(s->timer);
311050c2ea0SPeter Maydell }
312050c2ea0SPeter Maydell 
3135ee0abedSPeter Maydell static void cmsdk_apb_watchdog_clk_update(void *opaque, ClockEvent event)
3144c4599feSPeter Maydell {
3154c4599feSPeter Maydell     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
3164c4599feSPeter Maydell 
3174c4599feSPeter Maydell     ptimer_transaction_begin(s->timer);
3184c4599feSPeter Maydell     ptimer_set_period_from_clock(s->timer, s->wdogclk, 1);
3194c4599feSPeter Maydell     ptimer_transaction_commit(s->timer);
3204c4599feSPeter Maydell }
3214c4599feSPeter Maydell 
322050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_init(Object *obj)
323050c2ea0SPeter Maydell {
324050c2ea0SPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
325050c2ea0SPeter Maydell     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj);
326050c2ea0SPeter Maydell 
327050c2ea0SPeter Maydell     memory_region_init_io(&s->iomem, obj, &cmsdk_apb_watchdog_ops,
328050c2ea0SPeter Maydell                           s, "cmsdk-apb-watchdog", 0x1000);
329050c2ea0SPeter Maydell     sysbus_init_mmio(sbd, &s->iomem);
330050c2ea0SPeter Maydell     sysbus_init_irq(sbd, &s->wdogint);
3314c4599feSPeter Maydell     s->wdogclk = qdev_init_clock_in(DEVICE(s), "WDOGCLK",
3325ee0abedSPeter Maydell                                     cmsdk_apb_watchdog_clk_update, s,
3335ee0abedSPeter Maydell                                     ClockUpdate);
334566528f8SMichel Heily 
335566528f8SMichel Heily     s->is_luminary = false;
336566528f8SMichel Heily     s->id = cmsdk_apb_watchdog_id;
337050c2ea0SPeter Maydell }
338050c2ea0SPeter Maydell 
339050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp)
340050c2ea0SPeter Maydell {
341050c2ea0SPeter Maydell     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev);
342050c2ea0SPeter Maydell 
3434c4599feSPeter Maydell     if (!clock_has_source(s->wdogclk)) {
344050c2ea0SPeter Maydell         error_setg(errp,
3454c4599feSPeter Maydell                    "CMSDK APB watchdog: WDOGCLK clock must be connected");
346050c2ea0SPeter Maydell         return;
347050c2ea0SPeter Maydell     }
348050c2ea0SPeter Maydell 
3498c9dbc62SPeter Maydell     s->timer = ptimer_init(cmsdk_apb_watchdog_tick, s,
350050c2ea0SPeter Maydell                            PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
351050c2ea0SPeter Maydell                            PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
352050c2ea0SPeter Maydell                            PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
353050c2ea0SPeter Maydell                            PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
354050c2ea0SPeter Maydell 
3558c9dbc62SPeter Maydell     ptimer_transaction_begin(s->timer);
3564c4599feSPeter Maydell     ptimer_set_period_from_clock(s->timer, s->wdogclk, 1);
3578c9dbc62SPeter Maydell     ptimer_transaction_commit(s->timer);
358050c2ea0SPeter Maydell }
359050c2ea0SPeter Maydell 
360050c2ea0SPeter Maydell static const VMStateDescription cmsdk_apb_watchdog_vmstate = {
361050c2ea0SPeter Maydell     .name = "cmsdk-apb-watchdog",
362eeae0b2bSPeter Maydell     .version_id = 2,
363eeae0b2bSPeter Maydell     .minimum_version_id = 2,
36445bc669eSRichard Henderson     .fields = (const VMStateField[]) {
365eeae0b2bSPeter Maydell         VMSTATE_CLOCK(wdogclk, CMSDKAPBWatchdog),
366050c2ea0SPeter Maydell         VMSTATE_PTIMER(timer, CMSDKAPBWatchdog),
367050c2ea0SPeter Maydell         VMSTATE_UINT32(control, CMSDKAPBWatchdog),
368050c2ea0SPeter Maydell         VMSTATE_UINT32(intstatus, CMSDKAPBWatchdog),
369050c2ea0SPeter Maydell         VMSTATE_UINT32(lock, CMSDKAPBWatchdog),
370050c2ea0SPeter Maydell         VMSTATE_UINT32(itcr, CMSDKAPBWatchdog),
371050c2ea0SPeter Maydell         VMSTATE_UINT32(itop, CMSDKAPBWatchdog),
372050c2ea0SPeter Maydell         VMSTATE_UINT32(resetstatus, CMSDKAPBWatchdog),
373050c2ea0SPeter Maydell         VMSTATE_END_OF_LIST()
374050c2ea0SPeter Maydell     }
375050c2ea0SPeter Maydell };
376050c2ea0SPeter Maydell 
377050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data)
378050c2ea0SPeter Maydell {
379050c2ea0SPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
380050c2ea0SPeter Maydell 
381050c2ea0SPeter Maydell     dc->realize = cmsdk_apb_watchdog_realize;
382050c2ea0SPeter Maydell     dc->vmsd = &cmsdk_apb_watchdog_vmstate;
383*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, cmsdk_apb_watchdog_reset);
384050c2ea0SPeter Maydell }
385050c2ea0SPeter Maydell 
386050c2ea0SPeter Maydell static const TypeInfo cmsdk_apb_watchdog_info = {
387050c2ea0SPeter Maydell     .name = TYPE_CMSDK_APB_WATCHDOG,
388050c2ea0SPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
389050c2ea0SPeter Maydell     .instance_size = sizeof(CMSDKAPBWatchdog),
390050c2ea0SPeter Maydell     .instance_init = cmsdk_apb_watchdog_init,
391050c2ea0SPeter Maydell     .class_init = cmsdk_apb_watchdog_class_init,
392050c2ea0SPeter Maydell };
393050c2ea0SPeter Maydell 
394566528f8SMichel Heily static void luminary_watchdog_init(Object *obj)
395566528f8SMichel Heily {
396566528f8SMichel Heily     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj);
397566528f8SMichel Heily 
398566528f8SMichel Heily     s->is_luminary = true;
399566528f8SMichel Heily     s->id = luminary_watchdog_id;
400566528f8SMichel Heily }
401566528f8SMichel Heily 
402566528f8SMichel Heily static const TypeInfo luminary_watchdog_info = {
403566528f8SMichel Heily     .name = TYPE_LUMINARY_WATCHDOG,
404566528f8SMichel Heily     .parent = TYPE_CMSDK_APB_WATCHDOG,
405566528f8SMichel Heily     .instance_init = luminary_watchdog_init
406566528f8SMichel Heily };
407566528f8SMichel Heily 
408050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_register_types(void)
409050c2ea0SPeter Maydell {
410050c2ea0SPeter Maydell     type_register_static(&cmsdk_apb_watchdog_info);
411566528f8SMichel Heily     type_register_static(&luminary_watchdog_info);
412050c2ea0SPeter Maydell }
413050c2ea0SPeter Maydell 
414050c2ea0SPeter Maydell type_init(cmsdk_apb_watchdog_register_types);
415