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
15b6db70bcSRoque Arcudia Hernandez * Design Kit Technical Reference Manual (ARM DDI0479):
16b6db70bcSRoque Arcudia Hernandez * https://developer.arm.com/documentation/ddi0479/
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"
2832cad1ffSPhilippe Mathieu-Daudé #include "system/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
cmsdk_apb_watchdog_intstatus(CMSDKAPBWatchdog * s)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
cmsdk_apb_watchdog_resetstatus(CMSDKAPBWatchdog * s)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
cmsdk_apb_watchdog_update(CMSDKAPBWatchdog * s)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
cmsdk_apb_watchdog_read(void * opaque,hwaddr offset,unsigned size)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
cmsdk_apb_watchdog_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)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:
199eff9dc56SRoque Arcudia Hernandez /* Reset the load value and the current count. */
2008c9dbc62SPeter Maydell ptimer_transaction_begin(s->timer);
201050c2ea0SPeter Maydell ptimer_set_limit(s->timer, value, 1);
2028c9dbc62SPeter Maydell ptimer_transaction_commit(s->timer);
203050c2ea0SPeter Maydell break;
204eff9dc56SRoque Arcudia Hernandez case A_WDOGCONTROL: {
205eff9dc56SRoque Arcudia Hernandez uint32_t prev_control = s->control;
206566528f8SMichel Heily if (s->is_luminary && 0 != (R_WDOGCONTROL_INTEN_MASK & s->control)) {
207566528f8SMichel Heily /*
208566528f8SMichel Heily * The Luminary version of this device ignores writes to
209566528f8SMichel Heily * this register after the guest has enabled interrupts
210566528f8SMichel Heily * (so they can only be disabled again via reset).
211566528f8SMichel Heily */
212566528f8SMichel Heily break;
213566528f8SMichel Heily }
214050c2ea0SPeter Maydell s->control = value & R_WDOGCONTROL_VALID_MASK;
215eff9dc56SRoque Arcudia Hernandez if (R_WDOGCONTROL_INTEN_MASK & (s->control ^ prev_control)) {
216eff9dc56SRoque Arcudia Hernandez ptimer_transaction_begin(s->timer);
217eff9dc56SRoque Arcudia Hernandez if (R_WDOGCONTROL_INTEN_MASK & s->control) {
218eff9dc56SRoque Arcudia Hernandez /*
219eff9dc56SRoque Arcudia Hernandez * Set HIGH to enable the counter and the interrupt. Reloads
220eff9dc56SRoque Arcudia Hernandez * the counter from the value in WDOGLOAD when the interrupt
221eff9dc56SRoque Arcudia Hernandez * is enabled, after previously being disabled.
222eff9dc56SRoque Arcudia Hernandez */
223eff9dc56SRoque Arcudia Hernandez ptimer_set_count(s->timer, ptimer_get_limit(s->timer));
224eff9dc56SRoque Arcudia Hernandez ptimer_run(s->timer, 0);
225eff9dc56SRoque Arcudia Hernandez } else {
226eff9dc56SRoque Arcudia Hernandez /* Or LOW to disable the counter and interrupt. */
227eff9dc56SRoque Arcudia Hernandez ptimer_stop(s->timer);
228eff9dc56SRoque Arcudia Hernandez }
229eff9dc56SRoque Arcudia Hernandez ptimer_transaction_commit(s->timer);
230eff9dc56SRoque Arcudia Hernandez }
231050c2ea0SPeter Maydell cmsdk_apb_watchdog_update(s);
232050c2ea0SPeter Maydell break;
233eff9dc56SRoque Arcudia Hernandez }
234050c2ea0SPeter Maydell case A_WDOGINTCLR:
235050c2ea0SPeter Maydell s->intstatus = 0;
2368c9dbc62SPeter Maydell ptimer_transaction_begin(s->timer);
237050c2ea0SPeter Maydell ptimer_set_count(s->timer, ptimer_get_limit(s->timer));
2388c9dbc62SPeter Maydell ptimer_transaction_commit(s->timer);
239050c2ea0SPeter Maydell cmsdk_apb_watchdog_update(s);
240050c2ea0SPeter Maydell break;
241050c2ea0SPeter Maydell case A_WDOGLOCK:
242050c2ea0SPeter Maydell s->lock = (value != WDOG_UNLOCK_VALUE);
24369ed08e4SPhilippe Mathieu-Daudé trace_cmsdk_apb_watchdog_lock(s->lock);
244050c2ea0SPeter Maydell break;
245050c2ea0SPeter Maydell case A_WDOGITCR:
246566528f8SMichel Heily if (s->is_luminary) {
247566528f8SMichel Heily goto bad_offset;
248566528f8SMichel Heily }
249050c2ea0SPeter Maydell s->itcr = value & R_WDOGITCR_VALID_MASK;
250050c2ea0SPeter Maydell cmsdk_apb_watchdog_update(s);
251050c2ea0SPeter Maydell break;
252050c2ea0SPeter Maydell case A_WDOGITOP:
253566528f8SMichel Heily if (s->is_luminary) {
254566528f8SMichel Heily goto bad_offset;
255566528f8SMichel Heily }
256050c2ea0SPeter Maydell s->itop = value & R_WDOGITOP_VALID_MASK;
257050c2ea0SPeter Maydell cmsdk_apb_watchdog_update(s);
258050c2ea0SPeter Maydell break;
259050c2ea0SPeter Maydell case A_WDOGVALUE:
260050c2ea0SPeter Maydell case A_WDOGRIS:
261050c2ea0SPeter Maydell case A_WDOGMIS:
262050c2ea0SPeter Maydell case A_PID4 ... A_CID3:
263050c2ea0SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
264050c2ea0SPeter Maydell "CMSDK APB watchdog write: write to RO offset 0x%x\n",
265050c2ea0SPeter Maydell (int)offset);
266050c2ea0SPeter Maydell break;
267566528f8SMichel Heily case A_WDOGTEST:
268566528f8SMichel Heily if (!s->is_luminary) {
269566528f8SMichel Heily goto bad_offset;
270566528f8SMichel Heily }
271566528f8SMichel Heily qemu_log_mask(LOG_UNIMP,
272566528f8SMichel Heily "Luminary watchdog write: stall not implemented\n");
273566528f8SMichel Heily break;
274050c2ea0SPeter Maydell default:
275566528f8SMichel Heily bad_offset:
276050c2ea0SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
277050c2ea0SPeter Maydell "CMSDK APB watchdog write: bad offset 0x%x\n",
278050c2ea0SPeter Maydell (int)offset);
279050c2ea0SPeter Maydell break;
280050c2ea0SPeter Maydell }
281050c2ea0SPeter Maydell }
282050c2ea0SPeter Maydell
283050c2ea0SPeter Maydell static const MemoryRegionOps cmsdk_apb_watchdog_ops = {
284050c2ea0SPeter Maydell .read = cmsdk_apb_watchdog_read,
285050c2ea0SPeter Maydell .write = cmsdk_apb_watchdog_write,
286050c2ea0SPeter Maydell .endianness = DEVICE_LITTLE_ENDIAN,
287050c2ea0SPeter Maydell /* byte/halfword accesses are just zero-padded on reads and writes */
288050c2ea0SPeter Maydell .impl.min_access_size = 4,
289050c2ea0SPeter Maydell .impl.max_access_size = 4,
290050c2ea0SPeter Maydell .valid.min_access_size = 1,
291050c2ea0SPeter Maydell .valid.max_access_size = 4,
292050c2ea0SPeter Maydell };
293050c2ea0SPeter Maydell
cmsdk_apb_watchdog_tick(void * opaque)294050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_tick(void *opaque)
295050c2ea0SPeter Maydell {
296050c2ea0SPeter Maydell CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
297050c2ea0SPeter Maydell
298050c2ea0SPeter Maydell if (!s->intstatus) {
299050c2ea0SPeter Maydell /* Count expired for the first time: raise interrupt */
300050c2ea0SPeter Maydell s->intstatus = R_WDOGRIS_INT_MASK;
301050c2ea0SPeter Maydell } else {
302050c2ea0SPeter Maydell /* Count expired for the second time: raise reset and stop clock */
303050c2ea0SPeter Maydell s->resetstatus = 1;
304050c2ea0SPeter Maydell ptimer_stop(s->timer);
305050c2ea0SPeter Maydell }
306050c2ea0SPeter Maydell cmsdk_apb_watchdog_update(s);
307050c2ea0SPeter Maydell }
308050c2ea0SPeter Maydell
cmsdk_apb_watchdog_reset(DeviceState * dev)309050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_reset(DeviceState *dev)
310050c2ea0SPeter Maydell {
311050c2ea0SPeter Maydell CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev);
312050c2ea0SPeter Maydell
313050c2ea0SPeter Maydell trace_cmsdk_apb_watchdog_reset();
314050c2ea0SPeter Maydell s->control = 0;
315050c2ea0SPeter Maydell s->intstatus = 0;
316050c2ea0SPeter Maydell s->lock = 0;
317050c2ea0SPeter Maydell s->itcr = 0;
318050c2ea0SPeter Maydell s->itop = 0;
319050c2ea0SPeter Maydell s->resetstatus = 0;
320050c2ea0SPeter Maydell /* Set the limit and the count */
3218c9dbc62SPeter Maydell ptimer_transaction_begin(s->timer);
322eff9dc56SRoque Arcudia Hernandez /*
323eff9dc56SRoque Arcudia Hernandez * We need to stop the ptimer before setting its limit reset value. If the
324eff9dc56SRoque Arcudia Hernandez * order is the opposite when the code executes the stop after setting a new
325eff9dc56SRoque Arcudia Hernandez * limit it may want to recalculate the count based on the current time (if
326eff9dc56SRoque Arcudia Hernandez * the timer was currently running) and it won't get the proper reset value.
327eff9dc56SRoque Arcudia Hernandez */
328eff9dc56SRoque Arcudia Hernandez ptimer_stop(s->timer);
329050c2ea0SPeter Maydell ptimer_set_limit(s->timer, 0xffffffff, 1);
3308c9dbc62SPeter Maydell ptimer_transaction_commit(s->timer);
331050c2ea0SPeter Maydell }
332050c2ea0SPeter Maydell
cmsdk_apb_watchdog_clk_update(void * opaque,ClockEvent event)3335ee0abedSPeter Maydell static void cmsdk_apb_watchdog_clk_update(void *opaque, ClockEvent event)
3344c4599feSPeter Maydell {
3354c4599feSPeter Maydell CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
3364c4599feSPeter Maydell
3374c4599feSPeter Maydell ptimer_transaction_begin(s->timer);
3384c4599feSPeter Maydell ptimer_set_period_from_clock(s->timer, s->wdogclk, 1);
3394c4599feSPeter Maydell ptimer_transaction_commit(s->timer);
3404c4599feSPeter Maydell }
3414c4599feSPeter Maydell
cmsdk_apb_watchdog_init(Object * obj)342050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_init(Object *obj)
343050c2ea0SPeter Maydell {
344050c2ea0SPeter Maydell SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
345050c2ea0SPeter Maydell CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj);
346050c2ea0SPeter Maydell
347050c2ea0SPeter Maydell memory_region_init_io(&s->iomem, obj, &cmsdk_apb_watchdog_ops,
348050c2ea0SPeter Maydell s, "cmsdk-apb-watchdog", 0x1000);
349050c2ea0SPeter Maydell sysbus_init_mmio(sbd, &s->iomem);
350050c2ea0SPeter Maydell sysbus_init_irq(sbd, &s->wdogint);
3514c4599feSPeter Maydell s->wdogclk = qdev_init_clock_in(DEVICE(s), "WDOGCLK",
3525ee0abedSPeter Maydell cmsdk_apb_watchdog_clk_update, s,
3535ee0abedSPeter Maydell ClockUpdate);
354566528f8SMichel Heily
355566528f8SMichel Heily s->is_luminary = false;
356566528f8SMichel Heily s->id = cmsdk_apb_watchdog_id;
357050c2ea0SPeter Maydell }
358050c2ea0SPeter Maydell
cmsdk_apb_watchdog_realize(DeviceState * dev,Error ** errp)359050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp)
360050c2ea0SPeter Maydell {
361050c2ea0SPeter Maydell CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev);
362050c2ea0SPeter Maydell
3634c4599feSPeter Maydell if (!clock_has_source(s->wdogclk)) {
364050c2ea0SPeter Maydell error_setg(errp,
3654c4599feSPeter Maydell "CMSDK APB watchdog: WDOGCLK clock must be connected");
366050c2ea0SPeter Maydell return;
367050c2ea0SPeter Maydell }
368050c2ea0SPeter Maydell
3698c9dbc62SPeter Maydell s->timer = ptimer_init(cmsdk_apb_watchdog_tick, s,
370050c2ea0SPeter Maydell PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
371050c2ea0SPeter Maydell PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
372050c2ea0SPeter Maydell PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
373050c2ea0SPeter Maydell PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
374050c2ea0SPeter Maydell
3758c9dbc62SPeter Maydell ptimer_transaction_begin(s->timer);
3764c4599feSPeter Maydell ptimer_set_period_from_clock(s->timer, s->wdogclk, 1);
3778c9dbc62SPeter Maydell ptimer_transaction_commit(s->timer);
378050c2ea0SPeter Maydell }
379050c2ea0SPeter Maydell
380050c2ea0SPeter Maydell static const VMStateDescription cmsdk_apb_watchdog_vmstate = {
381050c2ea0SPeter Maydell .name = "cmsdk-apb-watchdog",
382eeae0b2bSPeter Maydell .version_id = 2,
383eeae0b2bSPeter Maydell .minimum_version_id = 2,
38445bc669eSRichard Henderson .fields = (const VMStateField[]) {
385eeae0b2bSPeter Maydell VMSTATE_CLOCK(wdogclk, CMSDKAPBWatchdog),
386050c2ea0SPeter Maydell VMSTATE_PTIMER(timer, CMSDKAPBWatchdog),
387050c2ea0SPeter Maydell VMSTATE_UINT32(control, CMSDKAPBWatchdog),
388050c2ea0SPeter Maydell VMSTATE_UINT32(intstatus, CMSDKAPBWatchdog),
389050c2ea0SPeter Maydell VMSTATE_UINT32(lock, CMSDKAPBWatchdog),
390050c2ea0SPeter Maydell VMSTATE_UINT32(itcr, CMSDKAPBWatchdog),
391050c2ea0SPeter Maydell VMSTATE_UINT32(itop, CMSDKAPBWatchdog),
392050c2ea0SPeter Maydell VMSTATE_UINT32(resetstatus, CMSDKAPBWatchdog),
393050c2ea0SPeter Maydell VMSTATE_END_OF_LIST()
394050c2ea0SPeter Maydell }
395050c2ea0SPeter Maydell };
396050c2ea0SPeter Maydell
cmsdk_apb_watchdog_class_init(ObjectClass * klass,const void * data)397*12d1a768SPhilippe Mathieu-Daudé static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, const void *data)
398050c2ea0SPeter Maydell {
399050c2ea0SPeter Maydell DeviceClass *dc = DEVICE_CLASS(klass);
400050c2ea0SPeter Maydell
401050c2ea0SPeter Maydell dc->realize = cmsdk_apb_watchdog_realize;
402050c2ea0SPeter Maydell dc->vmsd = &cmsdk_apb_watchdog_vmstate;
403e3d08143SPeter Maydell device_class_set_legacy_reset(dc, cmsdk_apb_watchdog_reset);
404050c2ea0SPeter Maydell }
405050c2ea0SPeter Maydell
406050c2ea0SPeter Maydell static const TypeInfo cmsdk_apb_watchdog_info = {
407050c2ea0SPeter Maydell .name = TYPE_CMSDK_APB_WATCHDOG,
408050c2ea0SPeter Maydell .parent = TYPE_SYS_BUS_DEVICE,
409050c2ea0SPeter Maydell .instance_size = sizeof(CMSDKAPBWatchdog),
410050c2ea0SPeter Maydell .instance_init = cmsdk_apb_watchdog_init,
411050c2ea0SPeter Maydell .class_init = cmsdk_apb_watchdog_class_init,
412050c2ea0SPeter Maydell };
413050c2ea0SPeter Maydell
luminary_watchdog_init(Object * obj)414566528f8SMichel Heily static void luminary_watchdog_init(Object *obj)
415566528f8SMichel Heily {
416566528f8SMichel Heily CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj);
417566528f8SMichel Heily
418566528f8SMichel Heily s->is_luminary = true;
419566528f8SMichel Heily s->id = luminary_watchdog_id;
420566528f8SMichel Heily }
421566528f8SMichel Heily
422566528f8SMichel Heily static const TypeInfo luminary_watchdog_info = {
423566528f8SMichel Heily .name = TYPE_LUMINARY_WATCHDOG,
424566528f8SMichel Heily .parent = TYPE_CMSDK_APB_WATCHDOG,
425566528f8SMichel Heily .instance_init = luminary_watchdog_init
426566528f8SMichel Heily };
427566528f8SMichel Heily
cmsdk_apb_watchdog_register_types(void)428050c2ea0SPeter Maydell static void cmsdk_apb_watchdog_register_types(void)
429050c2ea0SPeter Maydell {
430050c2ea0SPeter Maydell type_register_static(&cmsdk_apb_watchdog_info);
431566528f8SMichel Heily type_register_static(&luminary_watchdog_info);
432050c2ea0SPeter Maydell }
433050c2ea0SPeter Maydell
434050c2ea0SPeter Maydell type_init(cmsdk_apb_watchdog_register_types);
435