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/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 return;
178 }
179
wdt_sbsa_gwdt_reset(DeviceState * dev)180 static void wdt_sbsa_gwdt_reset(DeviceState *dev)
181 {
182 SBSA_GWDTState *s = SBSA_GWDT(dev);
183
184 timer_del(s->timer);
185
186 s->wcs = 0;
187 s->wcvl = 0;
188 s->wcvu = 0;
189 s->worl = 0;
190 s->woru = 0;
191 s->id = SBSA_GWDT_ID;
192 }
193
sbsa_gwdt_timer_sysinterrupt(void * opaque)194 static void sbsa_gwdt_timer_sysinterrupt(void *opaque)
195 {
196 SBSA_GWDTState *s = SBSA_GWDT(opaque);
197
198 if (!(s->wcs & SBSA_GWDT_WCS_WS0)) {
199 s->wcs |= SBSA_GWDT_WCS_WS0;
200 sbsa_gwdt_update_timer(s, TIMEOUT_REFRESH);
201 qemu_set_irq(s->irq, 1);
202 } else {
203 s->wcs |= SBSA_GWDT_WCS_WS1;
204 qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
205 /*
206 * Reset the watchdog only if the guest gets notified about
207 * expiry. watchdog_perform_action() may temporarily relinquish
208 * the BQL; reset before triggering the action to avoid races with
209 * sbsa_gwdt instructions.
210 */
211 switch (get_watchdog_action()) {
212 case WATCHDOG_ACTION_DEBUG:
213 case WATCHDOG_ACTION_NONE:
214 case WATCHDOG_ACTION_PAUSE:
215 break;
216 default:
217 wdt_sbsa_gwdt_reset(DEVICE(s));
218 }
219 watchdog_perform_action();
220 }
221 }
222
223 static const MemoryRegionOps sbsa_gwdt_rops = {
224 .read = sbsa_gwdt_rread,
225 .write = sbsa_gwdt_rwrite,
226 .endianness = DEVICE_LITTLE_ENDIAN,
227 .valid.min_access_size = 4,
228 .valid.max_access_size = 4,
229 .valid.unaligned = false,
230 };
231
232 static const MemoryRegionOps sbsa_gwdt_ops = {
233 .read = sbsa_gwdt_read,
234 .write = sbsa_gwdt_write,
235 .endianness = DEVICE_LITTLE_ENDIAN,
236 .valid.min_access_size = 4,
237 .valid.max_access_size = 4,
238 .valid.unaligned = false,
239 };
240
wdt_sbsa_gwdt_realize(DeviceState * dev,Error ** errp)241 static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp)
242 {
243 SBSA_GWDTState *s = SBSA_GWDT(dev);
244 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
245
246 memory_region_init_io(&s->rmmio, OBJECT(dev),
247 &sbsa_gwdt_rops, s,
248 "sbsa_gwdt.refresh",
249 SBSA_GWDT_RMMIO_SIZE);
250
251 memory_region_init_io(&s->cmmio, OBJECT(dev),
252 &sbsa_gwdt_ops, s,
253 "sbsa_gwdt.control",
254 SBSA_GWDT_CMMIO_SIZE);
255
256 sysbus_init_mmio(sbd, &s->rmmio);
257 sysbus_init_mmio(sbd, &s->cmmio);
258
259 sysbus_init_irq(sbd, &s->irq);
260
261 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sbsa_gwdt_timer_sysinterrupt,
262 dev);
263 }
264
265 static Property wdt_sbsa_gwdt_props[] = {
266 /*
267 * Timer frequency in Hz. This must match the frequency used by
268 * the CPU's generic timer. Default 62.5Hz matches QEMU's legacy
269 * CPU timer frequency default.
270 */
271 DEFINE_PROP_UINT64("clock-frequency", struct SBSA_GWDTState, freq,
272 62500000),
273 DEFINE_PROP_END_OF_LIST(),
274 };
275
wdt_sbsa_gwdt_class_init(ObjectClass * klass,void * data)276 static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data)
277 {
278 DeviceClass *dc = DEVICE_CLASS(klass);
279
280 dc->realize = wdt_sbsa_gwdt_realize;
281 device_class_set_legacy_reset(dc, wdt_sbsa_gwdt_reset);
282 dc->hotpluggable = false;
283 set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
284 dc->vmsd = &vmstate_sbsa_gwdt;
285 dc->desc = "SBSA-compliant generic watchdog device";
286 device_class_set_props(dc, wdt_sbsa_gwdt_props);
287 }
288
289 static const TypeInfo wdt_sbsa_gwdt_info = {
290 .class_init = wdt_sbsa_gwdt_class_init,
291 .parent = TYPE_SYS_BUS_DEVICE,
292 .name = TYPE_WDT_SBSA,
293 .instance_size = sizeof(SBSA_GWDTState),
294 };
295
wdt_sbsa_gwdt_register_types(void)296 static void wdt_sbsa_gwdt_register_types(void)
297 {
298 type_register_static(&wdt_sbsa_gwdt_info);
299 }
300
301 type_init(wdt_sbsa_gwdt_register_types)
302