xref: /openbmc/qemu/hw/watchdog/sbsa_gwdt.c (revision effd60c8)
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 "sysemu/reset.h"
20 #include "sysemu/watchdog.h"
21 #include "hw/watchdog/sbsa_gwdt.h"
22 #include "qemu/timer.h"
23 #include "migration/vmstate.h"
24 #include "qemu/log.h"
25 #include "qemu/module.h"
26 
27 static const VMStateDescription vmstate_sbsa_gwdt = {
28     .name = "sbsa-gwdt",
29     .version_id = 1,
30     .minimum_version_id = 1,
31     .fields = (const VMStateField[]) {
32         VMSTATE_TIMER_PTR(timer, SBSA_GWDTState),
33         VMSTATE_UINT32(wcs, SBSA_GWDTState),
34         VMSTATE_UINT32(worl, SBSA_GWDTState),
35         VMSTATE_UINT32(woru, SBSA_GWDTState),
36         VMSTATE_UINT32(wcvl, SBSA_GWDTState),
37         VMSTATE_UINT32(wcvu, SBSA_GWDTState),
38         VMSTATE_END_OF_LIST()
39     }
40 };
41 
42 typedef enum WdtRefreshType {
43     EXPLICIT_REFRESH = 0,
44     TIMEOUT_REFRESH = 1,
45 } WdtRefreshType;
46 
47 static uint64_t sbsa_gwdt_rread(void *opaque, hwaddr addr, unsigned int size)
48 {
49     SBSA_GWDTState *s = SBSA_GWDT(opaque);
50     uint32_t ret = 0;
51 
52     switch (addr) {
53     case SBSA_GWDT_WRR:
54         /* watch refresh read has no effect and returns 0 */
55         ret = 0;
56         break;
57     case SBSA_GWDT_W_IIDR:
58         ret = s->id;
59         break;
60     default:
61         qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame read :"
62                         " 0x%x\n", (int)addr);
63     }
64     return ret;
65 }
66 
67 static uint64_t sbsa_gwdt_read(void *opaque, hwaddr addr, unsigned int size)
68 {
69     SBSA_GWDTState *s = SBSA_GWDT(opaque);
70     uint32_t ret = 0;
71 
72     switch (addr) {
73     case SBSA_GWDT_WCS:
74         ret = s->wcs;
75         break;
76     case SBSA_GWDT_WOR:
77         ret = s->worl;
78         break;
79     case SBSA_GWDT_WORU:
80          ret = s->woru;
81          break;
82     case SBSA_GWDT_WCV:
83         ret = s->wcvl;
84         break;
85     case SBSA_GWDT_WCVU:
86         ret = s->wcvu;
87         break;
88     case SBSA_GWDT_W_IIDR:
89         ret = s->id;
90         break;
91     default:
92         qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame read :"
93                         " 0x%x\n", (int)addr);
94     }
95     return ret;
96 }
97 
98 static void sbsa_gwdt_update_timer(SBSA_GWDTState *s, WdtRefreshType rtype)
99 {
100     uint64_t timeout = 0;
101 
102     timer_del(s->timer);
103 
104     if (s->wcs & SBSA_GWDT_WCS_EN) {
105         /*
106          * Extract the upper 16 bits from woru & 32 bits from worl
107          * registers to construct the 48 bit offset value
108          */
109         timeout = s->woru;
110         timeout <<= 32;
111         timeout |= s->worl;
112         timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, SBSA_TIMER_FREQ);
113         timeout += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
114 
115         if ((rtype == EXPLICIT_REFRESH) || ((rtype == TIMEOUT_REFRESH) &&
116                 (!(s->wcs & SBSA_GWDT_WCS_WS0)))) {
117             /* store the current timeout value into compare registers */
118             s->wcvu = timeout >> 32;
119             s->wcvl = timeout;
120         }
121         timer_mod(s->timer, timeout);
122     }
123 }
124 
125 static void sbsa_gwdt_rwrite(void *opaque, hwaddr offset, uint64_t data,
126                              unsigned size) {
127     SBSA_GWDTState *s = SBSA_GWDT(opaque);
128 
129     if (offset == SBSA_GWDT_WRR) {
130         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
131 
132         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
133     } else {
134         qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame write :"
135                         " 0x%x\n", (int)offset);
136     }
137 }
138 
139 static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data,
140                              unsigned size) {
141     SBSA_GWDTState *s = SBSA_GWDT(opaque);
142 
143     switch (offset) {
144     case SBSA_GWDT_WCS:
145         s->wcs = data & SBSA_GWDT_WCS_EN;
146         qemu_set_irq(s->irq, 0);
147         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
148         break;
149 
150     case SBSA_GWDT_WOR:
151         s->worl = data;
152         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
153         qemu_set_irq(s->irq, 0);
154         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
155         break;
156 
157     case SBSA_GWDT_WORU:
158         s->woru = data & SBSA_GWDT_WOR_MASK;
159         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
160         qemu_set_irq(s->irq, 0);
161         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
162         break;
163 
164     case SBSA_GWDT_WCV:
165         s->wcvl = data;
166         break;
167 
168     case SBSA_GWDT_WCVU:
169         s->wcvu = data;
170         break;
171 
172     default:
173         qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame write :"
174                 " 0x%x\n", (int)offset);
175     }
176     return;
177 }
178 
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 
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 
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 void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data)
265 {
266     DeviceClass *dc = DEVICE_CLASS(klass);
267 
268     dc->realize = wdt_sbsa_gwdt_realize;
269     dc->reset = wdt_sbsa_gwdt_reset;
270     dc->hotpluggable = false;
271     set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
272     dc->vmsd = &vmstate_sbsa_gwdt;
273     dc->desc = "SBSA-compliant generic watchdog device";
274 }
275 
276 static const TypeInfo wdt_sbsa_gwdt_info = {
277     .class_init = wdt_sbsa_gwdt_class_init,
278     .parent = TYPE_SYS_BUS_DEVICE,
279     .name  = TYPE_WDT_SBSA,
280     .instance_size  = sizeof(SBSA_GWDTState),
281 };
282 
283 static void wdt_sbsa_gwdt_register_types(void)
284 {
285     type_register_static(&wdt_sbsa_gwdt_info);
286 }
287 
288 type_init(wdt_sbsa_gwdt_register_types)
289