xref: /qemu/hw/watchdog/sbsa_gwdt.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
1 /*
2  * Generic watchdog device model for SBSA
3  *
4  * The watchdog device has been implemented as revision 1 variant of
5  * the ARM SBSA specification v6.0
6  * (https://developer.arm.com/documentation/den0029/d?lang=en)
7  *
8  * Copyright Linaro.org 2020
9  *
10  * Authors:
11  *  Shashi Mallela <shashi.mallela@linaro.org>
12  *
13  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
14  * option) any later version.  See the COPYING file in the top-level directory.
15  *
16  */
17 
18 #include "qemu/osdep.h"
19 #include "system/reset.h"
20 #include "system/watchdog.h"
21 #include "hw/qdev-properties.h"
22 #include "hw/watchdog/sbsa_gwdt.h"
23 #include "qemu/timer.h"
24 #include "migration/vmstate.h"
25 #include "qemu/log.h"
26 #include "qemu/module.h"
27 
28 static const VMStateDescription vmstate_sbsa_gwdt = {
29     .name = "sbsa-gwdt",
30     .version_id = 1,
31     .minimum_version_id = 1,
32     .fields = (const VMStateField[]) {
33         VMSTATE_TIMER_PTR(timer, SBSA_GWDTState),
34         VMSTATE_UINT32(wcs, SBSA_GWDTState),
35         VMSTATE_UINT32(worl, SBSA_GWDTState),
36         VMSTATE_UINT32(woru, SBSA_GWDTState),
37         VMSTATE_UINT32(wcvl, SBSA_GWDTState),
38         VMSTATE_UINT32(wcvu, SBSA_GWDTState),
39         VMSTATE_END_OF_LIST()
40     }
41 };
42 
43 typedef enum WdtRefreshType {
44     EXPLICIT_REFRESH = 0,
45     TIMEOUT_REFRESH = 1,
46 } WdtRefreshType;
47 
sbsa_gwdt_rread(void * opaque,hwaddr addr,unsigned int size)48 static uint64_t sbsa_gwdt_rread(void *opaque, hwaddr addr, unsigned int size)
49 {
50     SBSA_GWDTState *s = SBSA_GWDT(opaque);
51     uint32_t ret = 0;
52 
53     switch (addr) {
54     case SBSA_GWDT_WRR:
55         /* watch refresh read has no effect and returns 0 */
56         ret = 0;
57         break;
58     case SBSA_GWDT_W_IIDR:
59         ret = s->id;
60         break;
61     default:
62         qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame read :"
63                         " 0x%x\n", (int)addr);
64     }
65     return ret;
66 }
67 
sbsa_gwdt_read(void * opaque,hwaddr addr,unsigned int size)68 static uint64_t sbsa_gwdt_read(void *opaque, hwaddr addr, unsigned int size)
69 {
70     SBSA_GWDTState *s = SBSA_GWDT(opaque);
71     uint32_t ret = 0;
72 
73     switch (addr) {
74     case SBSA_GWDT_WCS:
75         ret = s->wcs;
76         break;
77     case SBSA_GWDT_WOR:
78         ret = s->worl;
79         break;
80     case SBSA_GWDT_WORU:
81          ret = s->woru;
82          break;
83     case SBSA_GWDT_WCV:
84         ret = s->wcvl;
85         break;
86     case SBSA_GWDT_WCVU:
87         ret = s->wcvu;
88         break;
89     case SBSA_GWDT_W_IIDR:
90         ret = s->id;
91         break;
92     default:
93         qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame read :"
94                         " 0x%x\n", (int)addr);
95     }
96     return ret;
97 }
98 
sbsa_gwdt_update_timer(SBSA_GWDTState * s,WdtRefreshType rtype)99 static void sbsa_gwdt_update_timer(SBSA_GWDTState *s, WdtRefreshType rtype)
100 {
101     uint64_t timeout = 0;
102 
103     timer_del(s->timer);
104 
105     if (s->wcs & SBSA_GWDT_WCS_EN) {
106         /*
107          * Extract the upper 16 bits from woru & 32 bits from worl
108          * registers to construct the 48 bit offset value
109          */
110         timeout = s->woru;
111         timeout <<= 32;
112         timeout |= s->worl;
113         timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, s->freq);
114         timeout += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
115 
116         if ((rtype == EXPLICIT_REFRESH) || ((rtype == TIMEOUT_REFRESH) &&
117                 (!(s->wcs & SBSA_GWDT_WCS_WS0)))) {
118             /* store the current timeout value into compare registers */
119             s->wcvu = timeout >> 32;
120             s->wcvl = timeout;
121         }
122         timer_mod(s->timer, timeout);
123     }
124 }
125 
sbsa_gwdt_rwrite(void * opaque,hwaddr offset,uint64_t data,unsigned size)126 static void sbsa_gwdt_rwrite(void *opaque, hwaddr offset, uint64_t data,
127                              unsigned size) {
128     SBSA_GWDTState *s = SBSA_GWDT(opaque);
129 
130     if (offset == SBSA_GWDT_WRR) {
131         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
132 
133         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
134     } else {
135         qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame write :"
136                         " 0x%x\n", (int)offset);
137     }
138 }
139 
sbsa_gwdt_write(void * opaque,hwaddr offset,uint64_t data,unsigned size)140 static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data,
141                              unsigned size) {
142     SBSA_GWDTState *s = SBSA_GWDT(opaque);
143 
144     switch (offset) {
145     case SBSA_GWDT_WCS:
146         s->wcs = data & SBSA_GWDT_WCS_EN;
147         qemu_set_irq(s->irq, 0);
148         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
149         break;
150 
151     case SBSA_GWDT_WOR:
152         s->worl = data;
153         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
154         qemu_set_irq(s->irq, 0);
155         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
156         break;
157 
158     case SBSA_GWDT_WORU:
159         s->woru = data & SBSA_GWDT_WOR_MASK;
160         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
161         qemu_set_irq(s->irq, 0);
162         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
163         break;
164 
165     case SBSA_GWDT_WCV:
166         s->wcvl = data;
167         break;
168 
169     case SBSA_GWDT_WCVU:
170         s->wcvu = data;
171         break;
172 
173     default:
174         qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame write :"
175                 " 0x%x\n", (int)offset);
176     }
177 }
178 
wdt_sbsa_gwdt_reset(DeviceState * dev)179 static void wdt_sbsa_gwdt_reset(DeviceState *dev)
180 {
181     SBSA_GWDTState *s = SBSA_GWDT(dev);
182 
183     timer_del(s->timer);
184 
185     s->wcs  = 0;
186     s->wcvl = 0;
187     s->wcvu = 0;
188     s->worl = 0;
189     s->woru = 0;
190     s->id = SBSA_GWDT_ID;
191 }
192 
sbsa_gwdt_timer_sysinterrupt(void * opaque)193 static void sbsa_gwdt_timer_sysinterrupt(void *opaque)
194 {
195     SBSA_GWDTState *s = SBSA_GWDT(opaque);
196 
197     if (!(s->wcs & SBSA_GWDT_WCS_WS0)) {
198         s->wcs |= SBSA_GWDT_WCS_WS0;
199         sbsa_gwdt_update_timer(s, TIMEOUT_REFRESH);
200         qemu_set_irq(s->irq, 1);
201     } else {
202         s->wcs |= SBSA_GWDT_WCS_WS1;
203         qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
204         /*
205          * Reset the watchdog only if the guest gets notified about
206          * expiry. watchdog_perform_action() may temporarily relinquish
207          * the BQL; reset before triggering the action to avoid races with
208          * sbsa_gwdt instructions.
209          */
210         switch (get_watchdog_action()) {
211         case WATCHDOG_ACTION_DEBUG:
212         case WATCHDOG_ACTION_NONE:
213         case WATCHDOG_ACTION_PAUSE:
214             break;
215         default:
216             wdt_sbsa_gwdt_reset(DEVICE(s));
217         }
218         watchdog_perform_action();
219     }
220 }
221 
222 static const MemoryRegionOps sbsa_gwdt_rops = {
223     .read = sbsa_gwdt_rread,
224     .write = sbsa_gwdt_rwrite,
225     .endianness = DEVICE_LITTLE_ENDIAN,
226     .valid.min_access_size = 4,
227     .valid.max_access_size = 4,
228     .valid.unaligned = false,
229 };
230 
231 static const MemoryRegionOps sbsa_gwdt_ops = {
232     .read = sbsa_gwdt_read,
233     .write = sbsa_gwdt_write,
234     .endianness = DEVICE_LITTLE_ENDIAN,
235     .valid.min_access_size = 4,
236     .valid.max_access_size = 4,
237     .valid.unaligned = false,
238 };
239 
wdt_sbsa_gwdt_realize(DeviceState * dev,Error ** errp)240 static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp)
241 {
242     SBSA_GWDTState *s = SBSA_GWDT(dev);
243     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
244 
245     memory_region_init_io(&s->rmmio, OBJECT(dev),
246                           &sbsa_gwdt_rops, s,
247                           "sbsa_gwdt.refresh",
248                           SBSA_GWDT_RMMIO_SIZE);
249 
250     memory_region_init_io(&s->cmmio, OBJECT(dev),
251                           &sbsa_gwdt_ops, s,
252                           "sbsa_gwdt.control",
253                           SBSA_GWDT_CMMIO_SIZE);
254 
255     sysbus_init_mmio(sbd, &s->rmmio);
256     sysbus_init_mmio(sbd, &s->cmmio);
257 
258     sysbus_init_irq(sbd, &s->irq);
259 
260     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sbsa_gwdt_timer_sysinterrupt,
261                 dev);
262 }
263 
264 static const Property wdt_sbsa_gwdt_props[] = {
265     /*
266      * Timer frequency in Hz. This must match the frequency used by
267      * the CPU's generic timer. Default 62.5Hz matches QEMU's legacy
268      * CPU timer frequency default.
269      */
270     DEFINE_PROP_UINT64("clock-frequency", struct SBSA_GWDTState, freq,
271                        62500000),
272 };
273 
wdt_sbsa_gwdt_class_init(ObjectClass * klass,const void * data)274 static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, const void *data)
275 {
276     DeviceClass *dc = DEVICE_CLASS(klass);
277 
278     dc->realize = wdt_sbsa_gwdt_realize;
279     device_class_set_legacy_reset(dc, wdt_sbsa_gwdt_reset);
280     dc->hotpluggable = false;
281     set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
282     dc->vmsd = &vmstate_sbsa_gwdt;
283     dc->desc = "SBSA-compliant generic watchdog device";
284     device_class_set_props(dc, wdt_sbsa_gwdt_props);
285 }
286 
287 static const TypeInfo wdt_sbsa_gwdt_info = {
288     .class_init = wdt_sbsa_gwdt_class_init,
289     .parent = TYPE_SYS_BUS_DEVICE,
290     .name  = TYPE_WDT_SBSA,
291     .instance_size  = sizeof(SBSA_GWDTState),
292 };
293 
wdt_sbsa_gwdt_register_types(void)294 static void wdt_sbsa_gwdt_register_types(void)
295 {
296     type_register_static(&wdt_sbsa_gwdt_info);
297 }
298 
299 type_init(wdt_sbsa_gwdt_register_types)
300