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