xref: /openbmc/qemu/hw/watchdog/sbsa_gwdt.c (revision b91a0fa7)
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 WatchdogTimerModel model = {
28     .wdt_name = TYPE_WDT_SBSA,
29     .wdt_description = "SBSA-compliant generic watchdog device",
30 };
31 
32 static const VMStateDescription vmstate_sbsa_gwdt = {
33     .name = "sbsa-gwdt",
34     .version_id = 1,
35     .minimum_version_id = 1,
36     .fields = (VMStateField[]) {
37         VMSTATE_TIMER_PTR(timer, SBSA_GWDTState),
38         VMSTATE_UINT32(wcs, SBSA_GWDTState),
39         VMSTATE_UINT32(worl, SBSA_GWDTState),
40         VMSTATE_UINT32(woru, SBSA_GWDTState),
41         VMSTATE_UINT32(wcvl, SBSA_GWDTState),
42         VMSTATE_UINT32(wcvu, SBSA_GWDTState),
43         VMSTATE_END_OF_LIST()
44     }
45 };
46 
47 typedef enum WdtRefreshType {
48     EXPLICIT_REFRESH = 0,
49     TIMEOUT_REFRESH = 1,
50 } WdtRefreshType;
51 
52 static uint64_t sbsa_gwdt_rread(void *opaque, hwaddr addr, unsigned int size)
53 {
54     SBSA_GWDTState *s = SBSA_GWDT(opaque);
55     uint32_t ret = 0;
56 
57     switch (addr) {
58     case SBSA_GWDT_WRR:
59         /* watch refresh read has no effect and returns 0 */
60         ret = 0;
61         break;
62     case SBSA_GWDT_W_IIDR:
63         ret = s->id;
64         break;
65     default:
66         qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame read :"
67                         " 0x%x\n", (int)addr);
68     }
69     return ret;
70 }
71 
72 static uint64_t sbsa_gwdt_read(void *opaque, hwaddr addr, unsigned int size)
73 {
74     SBSA_GWDTState *s = SBSA_GWDT(opaque);
75     uint32_t ret = 0;
76 
77     switch (addr) {
78     case SBSA_GWDT_WCS:
79         ret = s->wcs;
80         break;
81     case SBSA_GWDT_WOR:
82         ret = s->worl;
83         break;
84     case SBSA_GWDT_WORU:
85          ret = s->woru;
86          break;
87     case SBSA_GWDT_WCV:
88         ret = s->wcvl;
89         break;
90     case SBSA_GWDT_WCVU:
91         ret = s->wcvu;
92         break;
93     case SBSA_GWDT_W_IIDR:
94         ret = s->id;
95         break;
96     default:
97         qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame read :"
98                         " 0x%x\n", (int)addr);
99     }
100     return ret;
101 }
102 
103 static void sbsa_gwdt_update_timer(SBSA_GWDTState *s, WdtRefreshType rtype)
104 {
105     uint64_t timeout = 0;
106 
107     timer_del(s->timer);
108 
109     if (s->wcs & SBSA_GWDT_WCS_EN) {
110         /*
111          * Extract the upper 16 bits from woru & 32 bits from worl
112          * registers to construct the 48 bit offset value
113          */
114         timeout = s->woru;
115         timeout <<= 32;
116         timeout |= s->worl;
117         timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, SBSA_TIMER_FREQ);
118         timeout += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
119 
120         if ((rtype == EXPLICIT_REFRESH) || ((rtype == TIMEOUT_REFRESH) &&
121                 (!(s->wcs & SBSA_GWDT_WCS_WS0)))) {
122             /* store the current timeout value into compare registers */
123             s->wcvu = timeout >> 32;
124             s->wcvl = timeout;
125         }
126         timer_mod(s->timer, timeout);
127     }
128 }
129 
130 static void sbsa_gwdt_rwrite(void *opaque, hwaddr offset, uint64_t data,
131                              unsigned size) {
132     SBSA_GWDTState *s = SBSA_GWDT(opaque);
133 
134     if (offset == SBSA_GWDT_WRR) {
135         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
136 
137         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
138     } else {
139         qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame write :"
140                         " 0x%x\n", (int)offset);
141     }
142 }
143 
144 static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data,
145                              unsigned size) {
146     SBSA_GWDTState *s = SBSA_GWDT(opaque);
147 
148     switch (offset) {
149     case SBSA_GWDT_WCS:
150         s->wcs = data & SBSA_GWDT_WCS_EN;
151         qemu_set_irq(s->irq, 0);
152         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
153         break;
154 
155     case SBSA_GWDT_WOR:
156         s->worl = data;
157         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
158         qemu_set_irq(s->irq, 0);
159         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
160         break;
161 
162     case SBSA_GWDT_WORU:
163         s->woru = data & SBSA_GWDT_WOR_MASK;
164         s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
165         qemu_set_irq(s->irq, 0);
166         sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
167         break;
168 
169     case SBSA_GWDT_WCV:
170         s->wcvl = data;
171         break;
172 
173     case SBSA_GWDT_WCVU:
174         s->wcvu = data;
175         break;
176 
177     default:
178         qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame write :"
179                 " 0x%x\n", (int)offset);
180     }
181     return;
182 }
183 
184 static void wdt_sbsa_gwdt_reset(DeviceState *dev)
185 {
186     SBSA_GWDTState *s = SBSA_GWDT(dev);
187 
188     timer_del(s->timer);
189 
190     s->wcs  = 0;
191     s->wcvl = 0;
192     s->wcvu = 0;
193     s->worl = 0;
194     s->woru = 0;
195     s->id = SBSA_GWDT_ID;
196 }
197 
198 static void sbsa_gwdt_timer_sysinterrupt(void *opaque)
199 {
200     SBSA_GWDTState *s = SBSA_GWDT(opaque);
201 
202     if (!(s->wcs & SBSA_GWDT_WCS_WS0)) {
203         s->wcs |= SBSA_GWDT_WCS_WS0;
204         sbsa_gwdt_update_timer(s, TIMEOUT_REFRESH);
205         qemu_set_irq(s->irq, 1);
206     } else {
207         s->wcs |= SBSA_GWDT_WCS_WS1;
208         qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
209         /*
210          * Reset the watchdog only if the guest gets notified about
211          * expiry. watchdog_perform_action() may temporarily relinquish
212          * the BQL; reset before triggering the action to avoid races with
213          * sbsa_gwdt instructions.
214          */
215         switch (get_watchdog_action()) {
216         case WATCHDOG_ACTION_DEBUG:
217         case WATCHDOG_ACTION_NONE:
218         case WATCHDOG_ACTION_PAUSE:
219             break;
220         default:
221             wdt_sbsa_gwdt_reset(DEVICE(s));
222         }
223         watchdog_perform_action();
224     }
225 }
226 
227 static const MemoryRegionOps sbsa_gwdt_rops = {
228     .read = sbsa_gwdt_rread,
229     .write = sbsa_gwdt_rwrite,
230     .endianness = DEVICE_LITTLE_ENDIAN,
231     .valid.min_access_size = 4,
232     .valid.max_access_size = 4,
233     .valid.unaligned = false,
234 };
235 
236 static const MemoryRegionOps sbsa_gwdt_ops = {
237     .read = sbsa_gwdt_read,
238     .write = sbsa_gwdt_write,
239     .endianness = DEVICE_LITTLE_ENDIAN,
240     .valid.min_access_size = 4,
241     .valid.max_access_size = 4,
242     .valid.unaligned = false,
243 };
244 
245 static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp)
246 {
247     SBSA_GWDTState *s = SBSA_GWDT(dev);
248     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
249 
250     memory_region_init_io(&s->rmmio, OBJECT(dev),
251                           &sbsa_gwdt_rops, s,
252                           "sbsa_gwdt.refresh",
253                           SBSA_GWDT_RMMIO_SIZE);
254 
255     memory_region_init_io(&s->cmmio, OBJECT(dev),
256                           &sbsa_gwdt_ops, s,
257                           "sbsa_gwdt.control",
258                           SBSA_GWDT_CMMIO_SIZE);
259 
260     sysbus_init_mmio(sbd, &s->rmmio);
261     sysbus_init_mmio(sbd, &s->cmmio);
262 
263     sysbus_init_irq(sbd, &s->irq);
264 
265     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sbsa_gwdt_timer_sysinterrupt,
266                 dev);
267 }
268 
269 static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data)
270 {
271     DeviceClass *dc = DEVICE_CLASS(klass);
272 
273     dc->realize = wdt_sbsa_gwdt_realize;
274     dc->reset = wdt_sbsa_gwdt_reset;
275     dc->hotpluggable = false;
276     set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
277     dc->vmsd = &vmstate_sbsa_gwdt;
278     dc->desc = "SBSA-compliant generic watchdog device";
279 }
280 
281 static const TypeInfo wdt_sbsa_gwdt_info = {
282     .class_init = wdt_sbsa_gwdt_class_init,
283     .parent = TYPE_SYS_BUS_DEVICE,
284     .name  = TYPE_WDT_SBSA,
285     .instance_size  = sizeof(SBSA_GWDTState),
286 };
287 
288 static void wdt_sbsa_gwdt_register_types(void)
289 {
290     watchdog_add_model(&model);
291     type_register_static(&wdt_sbsa_gwdt_info);
292 }
293 
294 type_init(wdt_sbsa_gwdt_register_types)
295