1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
27static WatchdogTimerModel model = {
28 .wdt_name = TYPE_WDT_SBSA,
29 .wdt_description = "SBSA-compliant generic watchdog device",
30};
31
32static 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
47typedef enum WdtRefreshType {
48 EXPLICIT_REFRESH = 0,
49 TIMEOUT_REFRESH = 1,
50} WdtRefreshType;
51
52static 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
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
72static 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
103static 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
112
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
123 s->wcvu = timeout >> 32;
124 s->wcvl = timeout;
125 }
126 timer_mod(s->timer, timeout);
127 }
128}
129
130static 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
144static 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
184static 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
198static 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
211
212
213
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
227static 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
236static 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
245static 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
269static 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
281static 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
288static void wdt_sbsa_gwdt_register_types(void)
289{
290 watchdog_add_model(&model);
291 type_register_static(&wdt_sbsa_gwdt_info);
292}
293
294type_init(wdt_sbsa_gwdt_register_types)
295