1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#include "qemu/osdep.h"
29#include "hw/sysbus.h"
30#include "hw/register.h"
31#include "qemu/bitops.h"
32#include "qemu/log.h"
33#include "qemu/timer.h"
34#include "qapi/error.h"
35#include "migration/vmstate.h"
36#include "hw/qdev-properties.h"
37#include "hw/fdt_generic_util.h"
38
39#ifndef XILINX_WWDT_ERR_DEBUG
40#define XILINX_WWDT_ERR_DEBUG 0
41#endif
42
43#define DB_PRINT_L(level, ...) do { \
44 if (XILINX_WWDT_ERR_DEBUG > (level)) { \
45 fprintf(stderr, "%s: ", __func__); \
46 fprintf(stderr, ## __VA_ARGS__); \
47 } \
48} while (0)
49
50#define TYPE_XILINX_WWDT "xlnx,versal-wwdt"
51
52
53
54#define LBE_BASIC_NO_BAD_EVENT_MASK 0x0
55#define LBE_KICK_IN_FIRST_WINDOW_MASK 0x1
56#define LBE_TSR_MISMATCH_MASK 0x2
57#define LBE_SECOND_WINDOW_TIMEOUT_MASK 0x3
58
59#define LBE_QA_NO_BAD_EVENT_MASK 0x4
60#define LBE_TOKEN_EARLY_MASK 0x5
61#define LBE_TOKEN_ERROR_MASK 0x6
62#define LBE_TIMEOUT_MASK 0x7
63
64#define INC_AND_ROLLOVER(num, max) do { \
65 if (num == max) { \
66 num = 0; \
67 } else { \
68 ++num; \
69 } \
70} while (0)
71
72#define XLNX_WWDT(obj) \
73 OBJECT_CHECK(WWDT, (obj), TYPE_XILINX_WWDT)
74
75REG32(MASTER_WRITE_CONTROL_REG, 0x0)
76 FIELD(MASTER_WRITE_CONTROL_REG, MWC, 0, 1)
77REG32(ENABLE_AND_STATUS_REG, 0x4)
78 FIELD(ENABLE_AND_STATUS_REG, LBE, 24, 3)
79 FIELD(ENABLE_AND_STATUS_REG, FCV, 20, 3)
80 FIELD(ENABLE_AND_STATUS_REG, WRP, 17, 1)
81 FIELD(ENABLE_AND_STATUS_REG, WINT, 16, 1)
82 FIELD(ENABLE_AND_STATUS_REG, ACNT, 14, 2)
83 FIELD(ENABLE_AND_STATUS_REG, TOUT, 12, 1)
84 FIELD(ENABLE_AND_STATUS_REG, SERR, 11, 1)
85 FIELD(ENABLE_AND_STATUS_REG, TERR, 10, 1)
86 FIELD(ENABLE_AND_STATUS_REG, TERL, 9, 1)
87 FIELD(ENABLE_AND_STATUS_REG, WSW, 8, 1)
88 FIELD(ENABLE_AND_STATUS_REG, TVAL, 2, 4)
89 FIELD(ENABLE_AND_STATUS_REG, WCFG, 1, 1)
90 FIELD(ENABLE_AND_STATUS_REG, WEN, 0, 1)
91REG32(FUNCTION_CONTROL_REG, 0x8)
92 FIELD(FUNCTION_CONTROL_REG, SBC, 8, 8)
93 FIELD(FUNCTION_CONTROL_REG, BSS, 6, 2)
94 FIELD(FUNCTION_CONTROL_REG, SSTE, 4, 1)
95 FIELD(FUNCTION_CONTROL_REG, PSME, 3, 1)
96 FIELD(FUNCTION_CONTROL_REG, FCE, 2, 1)
97 FIELD(FUNCTION_CONTROL_REG, WM, 1, 1)
98 FIELD(FUNCTION_CONTROL_REG, WDP, 0, 1)
99REG32(FIRST_WINDOW_CONFIG_REG, 0xc)
100REG32(SECOND_WINDOW_CONFIG_REG, 0x10)
101REG32(SST_WINDOW_CONFIG_REG, 0x14)
102REG32(TASK_SIGNATURE_REG0, 0x18)
103REG32(TASK_SIGNATURE_REG1, 0x1c)
104REG32(SECOND_SEQ_TIMER_REG, 0x20)
105REG32(TOKEN_FEEDBACK_REG, 0x24)
106 FIELD(TOKEN_FEEDBACK_REG, FDBK, 8, 4)
107 FIELD(TOKEN_FEEDBACK_REG, SEED, 0, 4)
108REG32(TOKEN_RESPONSE_REG, 0x28)
109 FIELD(TOKEN_RESPONSE_REG, ANS, 0, 8)
110REG32(INTERRUPT_ENABLE_REG, 0x30)
111 FIELD(INTERRUPT_ENABLE_REG, GWIEN, 2, 1)
112 FIELD(INTERRUPT_ENABLE_REG, WRPEN, 1, 1)
113 FIELD(INTERRUPT_ENABLE_REG, WINTEN, 0, 1)
114REG32(INTERRUPT_DISABLE_REG, 0x34)
115 FIELD(INTERRUPT_DISABLE_REG, GWID, 2, 1)
116 FIELD(INTERRUPT_DISABLE_REG, WRPD, 1, 1)
117 FIELD(INTERRUPT_DISABLE_REG, WINTD, 0, 1)
118REG32(INTERRUPT_MASK_REG, 0x38)
119 FIELD(INTERRUPT_MASK_REG, GWIM, 2, 1)
120 FIELD(INTERRUPT_MASK_REG, WRPM, 1, 1)
121 FIELD(INTERRUPT_MASK_REG, WINTM, 0, 1)
122REG32(ECO, 0x3c)
123REG32(GWDT_REFRESH_REG, 0x1000)
124 FIELD(GWDT_REFRESH_REG, GWRR, 0, 1)
125REG32(GWDT_CNTRL_STATUS_REG, 0x2000)
126 FIELD(GWDT_CNTRL_STATUS_REG, GWS, 1, 2)
127 FIELD(GWDT_CNTRL_STATUS_REG, GWEN, 0, 1)
128REG32(GWDT_OFFSET_REG, 0x2008)
129REG32(GWDT_COMPARE_VALUE_REG0, 0x2010)
130REG32(GWDT_COMPARE_VALUE_REG1, 0x2014)
131REG32(GWDT_WARM_RESET_REG, 0x2fd0)
132 FIELD(GWDT_WARM_RESET_REG, GWWRR, 0, 1)
133
134#define WWDT_R_MAX (R_GWDT_WARM_RESET_REG + 1)
135
136typedef struct WWDT {
137 SysBusDevice parent_obj;
138 MemoryRegion iomem;
139
140
141 QEMUTimer *gwdt_timer;
142 qemu_irq gwdt_ws0;
143 qemu_irq gwdt_ws1;
144
145
146 QEMUTimer *wwdt_timer;
147 QEMUTimer *wwdt_irq_timer;
148 QEMUTimer *sst_timer;
149 qemu_irq wwdt_reset_pending;
150 qemu_irq wwdt_irq;
151
152
153 qemu_irq wwdt_reset;
154
155
156 uint8_t lfsr;
157 bool wwdt_qa_running;
158 uint8_t token_cnt;
159
160 bool window_2;
161 bool wen;
162
163 uint64_t pclk;
164 uint32_t regs[WWDT_R_MAX];
165 RegisterInfo regs_info[WWDT_R_MAX];
166} WWDT;
167
168static void gwdt_update_irq(WWDT *s)
169{
170 bool irq;
171
172 irq = ARRAY_FIELD_EX32(s->regs, GWDT_CNTRL_STATUS_REG, GWS) & 0x1;
173 irq &= !ARRAY_FIELD_EX32(s->regs, INTERRUPT_MASK_REG, GWIM);
174 qemu_set_irq(s->gwdt_ws0, irq);
175 irq = ARRAY_FIELD_EX32(s->regs, GWDT_CNTRL_STATUS_REG, GWS) & 0x2;
176 qemu_set_irq(s->gwdt_ws1, irq);
177}
178
179static uint32_t gwdt_reload_val(WWDT *s)
180{
181 return s->regs[R_GWDT_OFFSET_REG];
182}
183
184static uint64_t gwdt_next_trigger(WWDT *s)
185{
186 uint64_t next = muldiv64(1000000000,
187 gwdt_reload_val(s),
188 s->pclk);
189
190 return next + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
191}
192
193static void gwdt_time_elapsed(void *opaque)
194{
195 WWDT *s = XLNX_WWDT(opaque);
196 uint8_t window = ARRAY_FIELD_EX32(s->regs, GWDT_CNTRL_STATUS_REG, GWS);
197
198 switch (window) {
199 case 0:
200 ++window;
201 ARRAY_FIELD_DP32(s->regs, GWDT_CNTRL_STATUS_REG, GWS, 1);
202 gwdt_update_irq(s);
203 break;
204 case 1:
205 ++window;
206 ARRAY_FIELD_DP32(s->regs, GWDT_CNTRL_STATUS_REG, GWS, 3);
207 gwdt_update_irq(s);
208
209 qemu_set_irq(s->wwdt_reset, 1);
210 break;
211 case 2:
212 case 3:
213
214 break;
215 default:
216 g_assert_not_reached();
217 }
218
219 timer_mod(s->gwdt_timer, gwdt_next_trigger(s));
220}
221
222static void gwdt_refresh_reg_postw(RegisterInfo *reg, uint64_t val64)
223{
224 WWDT *s = XLNX_WWDT(reg->opaque);
225
226 timer_mod(s->gwdt_timer, gwdt_next_trigger(s));
227
228 s->regs[R_GWDT_REFRESH_REG] = 0;
229
230 ARRAY_FIELD_DP32(s->regs, GWDT_CNTRL_STATUS_REG, GWS, 0);
231 gwdt_update_irq(s);
232}
233
234static void gwdt_cntrl_status_reg_postw(RegisterInfo *reg, uint64_t val64)
235{
236 WWDT *s = XLNX_WWDT(reg->opaque);
237
238 if (FIELD_EX64(val64, GWDT_CNTRL_STATUS_REG, GWEN)) {
239 timer_mod(s->gwdt_timer, gwdt_next_trigger(s));
240 } else {
241 timer_del(s->gwdt_timer);
242 ARRAY_FIELD_DP32(s->regs, GWDT_CNTRL_STATUS_REG, GWS, 0);
243 gwdt_update_irq(s);
244 }
245}
246
247static void gwdt_warm_reset_reg_postw(RegisterInfo *reg, uint64_t val64)
248{
249 WWDT *s = XLNX_WWDT(reg->opaque);
250
251 register_reset(&s->regs_info[R_GWDT_REFRESH_REG]);
252 register_reset(&s->regs_info[R_GWDT_CNTRL_STATUS_REG]);
253 register_reset(&s->regs_info[R_GWDT_OFFSET_REG]);
254 register_reset(&s->regs_info[R_GWDT_COMPARE_VALUE_REG0]);
255 register_reset(&s->regs_info[R_GWDT_COMPARE_VALUE_REG1]);
256
257 s->regs[R_GWDT_WARM_RESET_REG] = 0;
258 gwdt_update_irq(s);
259}
260
261static void wwdt_update_irq(WWDT *s)
262{
263 bool irq;
264
265 irq = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, WINT);
266 irq &= !ARRAY_FIELD_EX32(s->regs, INTERRUPT_MASK_REG, WINTM);
267 qemu_set_irq(s->wwdt_irq, irq);
268 irq = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, WRP);
269 irq &= !ARRAY_FIELD_EX32(s->regs, INTERRUPT_MASK_REG, WRPM);
270 qemu_set_irq(s->wwdt_reset_pending, irq);
271}
272
273static void wwdt_do_reset(WWDT *s)
274{
275 qemu_set_irq(s->wwdt_reset, 1);
276
277 timer_del(s->wwdt_timer);
278 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WEN, 0);
279}
280
281static void wwdt_sst_time_elapsed(void *opaque)
282{
283 WWDT *s = XLNX_WWDT(opaque);
284 wwdt_do_reset(s);
285}
286static uint32_t wwdt_sst_reload_val(WWDT *s)
287{
288 return s->regs[R_SECOND_SEQ_TIMER_REG];
289}
290
291static uint64_t wwdt_sst_next_trigger(WWDT *s)
292{
293 uint64_t next = muldiv64(1000000000,
294 wwdt_sst_reload_val(s),
295 s->pclk);
296
297 return next + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
298}
299
300static void wwdt_sst_start(WWDT *s)
301{
302 bool rp_en = !ARRAY_FIELD_EX32(s->regs, INTERRUPT_MASK_REG, WRPM);
303 if (rp_en) {
304 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WRP, 1);
305 wwdt_update_irq(s);
306 }
307 timer_mod(s->sst_timer, wwdt_sst_next_trigger(s));
308}
309
310static void wwdt_reset_event(WWDT *s)
311{
312 bool sst_en = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, SSTE);
313 if (sst_en) {
314 wwdt_sst_start(s);
315 } else {
316 wwdt_do_reset(s);
317 }
318}
319
320static void wwdt_good_event(WWDT *s)
321{
322 bool count_en = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, FCE);
323 bool is_qa = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, WM);
324
325 if (count_en || is_qa) {
326 uint8_t count = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, FCV);
327 if (count) {
328 --count;
329 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, FCV, count);
330 }
331 }
332}
333
334static void wwdt_bad_event(WWDT *s)
335{
336 bool count_en = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, FCE);
337 bool is_qa = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, WM);
338
339
340 if (count_en || is_qa) {
341 uint8_t count = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, FCV);
342
343 if (count == 7) {
344 wwdt_reset_event(s);
345 } else {
346 ++count;
347 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, FCV, count);
348 }
349 } else {
350 wwdt_reset_event(s);
351 }
352}
353
354static void wwdt_irq_time_elapsed(void *opaque)
355{
356 WWDT *s = XLNX_WWDT(opaque);
357
358 DB_PRINT_L(0, "WWDT SBC and BSS mask matched, asserting IRQ.\n");
359 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WINT, 1);
360 wwdt_update_irq(s);
361}
362
363static uint32_t wwdt_irq_reload_val(WWDT *s, bool *success)
364{
365 uint32_t shift = 1 << (ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG,
366 BSS) * 8);
367 uint32_t val = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, SBC) <<
368 shift;
369
370 if (val > s->regs[R_SECOND_WINDOW_CONFIG_REG]) {
371 success = false;
372 qemu_log_mask(LOG_GUEST_ERROR, "%s: WINT is set to trigger after "
373 "second window expires.\nSBC=0x%x BSS=0x%x window=0x%x\n",
374 object_get_canonical_path(OBJECT(s)),
375 ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, SBC),
376 ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, BSS),
377 s->regs[R_SECOND_WINDOW_CONFIG_REG]);
378 } else {
379 *success = true;
380 }
381
382 return val;
383}
384
385static uint64_t wwdt_irq_next_trigger(WWDT *s, bool *success)
386{
387 uint64_t next = muldiv64(1000000000,
388 wwdt_irq_reload_val(s, success),
389 s->pclk);
390
391 return next + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
392}
393
394
395
396
397
398
399
400
401
402static void wwdt_irq_timer_start(WWDT *s)
403{ uint64_t trigger;
404 bool success;
405
406 trigger = wwdt_irq_next_trigger(s, &success);
407 if (success) {
408 DB_PRINT_L(0, "Starting interrupt timer\n");
409 timer_mod(s->wwdt_irq_timer, trigger);
410 }
411}
412
413static uint32_t wwdt_reload_val(WWDT *s)
414{
415 if (s->window_2) {
416 return s->regs[R_SECOND_WINDOW_CONFIG_REG];
417 } else {
418 return s->regs[R_FIRST_WINDOW_CONFIG_REG];
419 }
420}
421
422static uint64_t wwdt_next_trigger(WWDT *s)
423{
424 uint64_t next = muldiv64(1000000000,
425 wwdt_reload_val(s),
426 s->pclk);
427
428 return next + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
429}
430
431static void wwdt_qa_gen_token(WWDT *s)
432{
433 uint8_t fdbk = ARRAY_FIELD_EX32(s->regs, TOKEN_FEEDBACK_REG, FDBK);
434 uint8_t token;
435
436 switch (fdbk & 0x3) {
437 case 0x0:
438 case 0x3:
439 token = !!((s->lfsr & 0x2) ^ (s->token_cnt & 0x2));
440 token |= ((s->lfsr & 0x8) ^ (s->token_cnt & 0x8)) >> 2;
441 token |= ((s->lfsr & 0x1) ^ (s->token_cnt & 0x1)) << 2;
442 token |= ((s->lfsr & 0x4) ^ (s->token_cnt & 0x4)) << 1;
443 break;
444 case 0x1:
445 token = !!((s->lfsr & 0x8) ^ (s->token_cnt & 0x8));
446 token |= ((s->lfsr & 0x4) ^ (s->token_cnt & 0x4)) >> 1;
447 token |= ((s->lfsr & 0x2) ^ (s->token_cnt & 0x2)) << 1;
448 token |= ((s->lfsr & 0x1) ^ (s->token_cnt & 0x1)) << 3;
449 break;
450 case 0x2:
451 token = !!((s->lfsr & 0x4) ^ (s->token_cnt & 0x4));
452 token |= ((s->lfsr & 0x8) ^ (s->token_cnt & 0x8)) >> 2;
453 token |= ((s->lfsr & 0x1) ^ (s->token_cnt & 0x1)) << 2;
454 token |= ((s->lfsr & 0x2) ^ (s->token_cnt & 0x2)) << 2;
455 break;
456 }
457 DB_PRINT_L(0, "Generated question token 0x%x\n", token);
458
459 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TVAL, token);
460}
461
462static void wwdt_qa_timer_start(WWDT *s)
463{
464
465 if (!s->wwdt_qa_running) {
466 s->lfsr = ARRAY_FIELD_EX32(s->regs, TOKEN_FEEDBACK_REG, SEED);
467 s->token_cnt = 1;
468 s->wwdt_qa_running = true;
469 wwdt_qa_gen_token(s);
470 }
471
472 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, ACNT, 3);
473
474 DB_PRINT_L(0, "Starting first window timer\n");
475 timer_mod(s->wwdt_timer, wwdt_next_trigger(s));
476}
477
478static void wwdt_qa_time_elapsed(WWDT *s)
479{
480
481 if (s->window_2) {
482 DB_PRINT_L(0, "Second window timeout\n");
483 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
484 LBE_TIMEOUT_MASK);
485 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TOUT, 1);
486 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 0);
487 s->window_2 = false;
488
489 wwdt_bad_event(s);
490 wwdt_qa_timer_start(s);
491 } else {
492
493 if (ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, ACNT) != 2) {
494 DB_PRINT_L(0, "First window timeout\n");
495 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
496 LBE_TIMEOUT_MASK);
497 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TOUT, 1);
498 wwdt_bad_event(s);
499 wwdt_qa_timer_start(s);
500 } else {
501 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 1);
502 s->window_2 = true;
503
504 wwdt_irq_timer_start(s);
505 timer_mod(s->wwdt_timer, wwdt_next_trigger(s));
506 }
507 }
508}
509
510static void wwdt_basic_time_elapsed(WWDT *s)
511{
512 if (s->window_2) {
513 DB_PRINT_L(0, "Second window timeout\n");
514 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
515 LBE_SECOND_WINDOW_TIMEOUT_MASK);
516 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TOUT, 1);
517 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 0);
518 wwdt_bad_event(s);
519 s->window_2 = false;
520 } else {
521 DB_PRINT_L(0, "Starting second window timer\n");
522 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 1);
523 s->window_2 = true;
524
525 wwdt_irq_timer_start(s);
526 }
527
528 timer_mod(s->wwdt_timer, wwdt_next_trigger(s));
529}
530
531static void wwdt_time_elapsed(void *opaque)
532{
533 WWDT *s = XLNX_WWDT(opaque);
534 bool is_basic = !ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, WM);
535
536 DB_PRINT_L(0, "Time elapsed\n");
537 if (is_basic) {
538 wwdt_basic_time_elapsed(s);
539 } else {
540 wwdt_qa_time_elapsed(s);
541 }
542}
543
544static bool wwdt_basic_en(WWDT *s)
545{
546 bool success;
547
548 if (!s->regs[R_SECOND_WINDOW_CONFIG_REG]) {
549 qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot run WWDT in basic mode "
550 "with second window set to 0\n",
551 object_get_canonical_path(OBJECT(s)));
552 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WCFG, 1);
553 success = false;
554 } else {
555
556 ARRAY_FIELD_DP32(s->regs, MASTER_WRITE_CONTROL_REG, MWC, 0);
557 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
558 LBE_BASIC_NO_BAD_EVENT_MASK);
559 DB_PRINT_L(0, "Starting first window timer\n");
560 timer_mod(s->wwdt_timer, wwdt_next_trigger(s));
561 s->wen = true;
562 success = true;
563 }
564
565 return success;
566}
567
568static bool wwdt_qa_en(WWDT *s)
569{
570 bool success;
571
572
573
574
575
576 if (s->regs[R_SECOND_WINDOW_CONFIG_REG] &&
577 s->regs[R_FIRST_WINDOW_CONFIG_REG]) {
578 ARRAY_FIELD_DP32(s->regs, MASTER_WRITE_CONTROL_REG, MWC, 0);
579 s->wen = true;
580 success = true;
581 } else {
582 qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot run WWDT in Q&A mode "
583 "with either window set to 0\n",
584 object_get_canonical_path(OBJECT(s)));
585 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WCFG, 1);
586 success = false;
587 }
588
589 return success;
590}
591
592static bool wwdt_en(WWDT *s)
593{
594 bool is_basic = !ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, WM);
595 bool success;
596
597 if (is_basic) {
598 success = wwdt_basic_en(s);
599 } else {
600 success = wwdt_qa_en(s);
601 }
602 return success;
603}
604
605static bool wwdt_tsr_check(WWDT *s)
606{
607 bool match = s->regs[R_TASK_SIGNATURE_REG0] ==
608 s->regs[R_TASK_SIGNATURE_REG1];
609
610 if (match) {
611 wwdt_good_event(s);
612 } else {
613 wwdt_bad_event(s);
614 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
615 LBE_TSR_MISMATCH_MASK);
616 qemu_log_mask(LOG_GUEST_ERROR, "%s: TSR mistmatch: TSR0=0x%x, "
617 "TSR1=0x%x\n", object_get_canonical_path(OBJECT(s)),
618 s->regs[R_TASK_SIGNATURE_REG0],
619 s->regs[R_TASK_SIGNATURE_REG1]);
620 }
621 return match;
622}
623
624static void wwdt_basic_do_disable(WWDT *s)
625{
626 timer_del(s->wwdt_timer);
627 s->wen = false;
628 s->window_2 = false;
629 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 0);
630}
631
632static bool wwdt_basic_disable(WWDT *s)
633{
634 bool success;
635
636
637 if (s->window_2) {
638 bool psm_en = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, PSME);
639 if (psm_en) {
640 if (wwdt_tsr_check(s)) {
641 wwdt_basic_do_disable(s);
642 success = true;
643 } else {
644 success = false;
645 }
646 } else {
647 wwdt_basic_do_disable(s);
648 success = true;
649 }
650 } else {
651 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
652 LBE_KICK_IN_FIRST_WINDOW_MASK);
653 wwdt_bad_event(s);
654 qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot clear WEN in first "
655 "window\n", object_get_canonical_path(OBJECT(s)));
656 success = false;
657 }
658
659 return success;
660}
661
662static bool wwdt_qa_disable(WWDT *s)
663{
664 uint8_t fail_count = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, FCV);
665 bool success;
666
667
668 if (!fail_count) {
669 s->wwdt_qa_running = false;
670 s->window_2 = false;
671 timer_del(s->wwdt_timer);
672 success = true;
673 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 0);
674 } else {
675 wwdt_bad_event(s);
676 qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot clear WEN if FCV is "
677 "non-zero\n", object_get_canonical_path(OBJECT(s)));
678 success = false;
679 }
680 return success;
681}
682
683static bool wwdt_disable(WWDT *s)
684{
685 bool is_basic = !ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, WM);
686 bool success;
687
688 if (is_basic) {
689 success = wwdt_basic_disable(s);
690 } else {
691 success = wwdt_qa_disable(s);
692 }
693 return success;
694}
695
696static void wwdt_basic_do_kick(WWDT *s)
697{
698 DB_PRINT_L(0, "WDT kicked in second window\nRestarting in first window\n");
699 wwdt_good_event(s);
700 timer_mod(s->wwdt_timer, wwdt_next_trigger(s));
701 s->window_2 = false;
702 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 0);
703}
704
705static void wwdt_basic_kick(WWDT *s)
706{
707 bool wsw_set = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, WSW);
708
709
710 if (!s->window_2 && wsw_set) {
711 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
712 LBE_KICK_IN_FIRST_WINDOW_MASK);
713 wwdt_bad_event(s);
714
715 qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot reset WDT in first "
716 "window\n", object_get_canonical_path(OBJECT(s)));
717
718 } else if (s->window_2 && !wsw_set) {
719 bool psm_en = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, PSME);
720 if (psm_en) {
721 if (wwdt_tsr_check(s)) {
722 wwdt_basic_do_kick(s);
723 }
724 } else {
725 wwdt_basic_do_kick(s);
726 }
727 }
728}
729
730static uint8_t wwdt_qa_gen_ans(WWDT *s)
731{
732 uint8_t acnt = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, ACNT);
733 uint8_t token = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, TVAL);
734 uint8_t fdbk = ARRAY_FIELD_EX32(s->regs, TOKEN_FEEDBACK_REG, FDBK);
735 uint8_t ans;
736
737 INC_AND_ROLLOVER(acnt, 3);
738
739 fdbk >>= 2;
740 switch (fdbk) {
741 case 0x0:
742 ans = (token & 0x1) ^ !!(acnt & 0x2) ^ !!(token & 0x8);
743 ans |= ((token & 0x1) ^ !!(acnt & 0x2) ^ !!(token & 0x4) ^
744 !!(token & 0x2)) << 1;
745 ans |= ((token & 0x1) ^ !!(acnt & 0x2) ^ !!(token & 0x8) ^
746 !!(token & 0x2)) << 2;
747 ans |= (!!(token & 0x4) ^ !!(acnt & 0x2) ^ (token & 0x1) ^
748 !!(token & 0x8)) << 3;
749 ans |= ((token & 0x2) ^ (acnt & 0x1)) << 4;
750 ans |= (!!(token & 0x8) ^ (acnt & 0x1)) << 5;
751 ans |= ((token & 0x1) ^ (acnt & 0x1)) << 6;
752 ans |= (!!(token & 0x4) ^ (acnt & 0x1)) << 7;
753 break;
754 case 0x1:
755 ans = !!(token & 0x2) ^ !!(acnt & 0x2) ^ (token & 0x1);
756 ans |= (!!(token & 0x2) ^ !!(acnt & 0x2) ^ (token & 0x1) ^
757 !!(token & 0x2)) << 1;
758 ans |= (!!(token & 0x2) ^ !!(acnt & 0x2) ^ (token & 0x1) ^
759 !!(token & 0x2)) << 2;
760 ans |= ((token & 0x1) ^ !!(acnt & 0x2) ^ !!(token & 0x2) ^
761 !!(token & 0x8)) << 3;
762 ans |= ((token & 0x1) ^ (acnt & 0x1)) << 4;
763 ans |= ((token & 0x1) ^ (acnt & 0x1)) << 5;
764 ans |= (!!(token & 0x2) ^ (acnt & 0x1)) << 6;
765 ans |= ((token & 0x1) ^ (acnt & 0x1)) << 7;
766 break;
767 case 0x2:
768 ans = !!(token & 0x4) ^ !!(acnt & 0x2) ^ !!(token & 0x2);
769 ans |= (!!(token & 0x4) ^ !!(acnt & 0x2) ^ !!(token & 0x2) ^
770 !!(token & 0x2)) << 1;
771 ans |= (!!(token & 0x4) ^ !!(acnt & 0x2) ^ !!(token & 0x2) ^
772 !!(token & 0x2)) << 2;
773 ans |= (!!(token & 0x2) ^ !!(acnt & 0x2) ^ !!(token & 0x4) ^
774 !!(token & 0x8)) << 3;
775 ans |= (!!(token & 0x4) ^ (acnt & 0x1)) << 4;
776 ans |= (!!(token & 0x2) ^ (acnt & 0x1)) << 5;
777 ans |= (!!(token & 0x4) ^ (acnt & 0x1)) << 6;
778 ans |= (!!(token & 0x2) ^ (acnt & 0x1)) << 7;
779 break;
780 case 0x3:
781 ans = !!(token & 0x8) ^ !!(acnt & 0x2) ^ !!(token & 0x4);
782 ans |= (!!(token & 0x8) ^ !!(acnt & 0x2) ^ !!(token & 0x8) ^
783 !!(token & 0x2)) << 1;
784 ans |= (!!(token & 0x8) ^ !!(acnt & 0x2) ^ !!(token & 0x4) ^
785 !!(token & 0x2)) << 2;
786 ans |= (!!(token & 0x8) ^ !!(acnt & 0x2) ^ !!(token & 0x8) ^
787 !!(token & 0x8)) << 3;
788 ans |= (!!(token & 0x8) ^ (acnt & 0x1)) << 4;
789 ans |= (!!(token & 0x4) ^ (acnt & 0x1)) << 5;
790 ans |= (!!(token & 0x8) ^ (acnt & 0x1)) << 6;
791 ans |= (!!(token & 0x4) ^ (acnt & 0x1)) << 7;
792 break;
793 }
794
795 DB_PRINT_L(0, "Generated answer 0x%x\n", ans);
796
797 return ans;
798}
799
800static void wwdt_qa_next_token(WWDT *s)
801{
802 s->lfsr = ((!!((s->lfsr & 0x4) | (s->lfsr & 0x2))) |
803 ((s->lfsr & 0x1) << 2) |
804 (((s->lfsr & 0x8) ^ (s->lfsr & 0x4)) << 3));
805 INC_AND_ROLLOVER(s->token_cnt, 0x0f);
806 wwdt_qa_gen_token(s);
807}
808
809static void wwdt_qa_answer_match(WWDT *s)
810{
811 uint8_t acnt = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, ACNT);
812
813 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TERR, 0);
814
815 if (acnt == 2) {
816 if (s->window_2) {
817
818 s->window_2 = false;
819 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 0);
820 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, SERR, 0);
821 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TERL, 0);
822 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
823 LBE_QA_NO_BAD_EVENT_MASK);
824 wwdt_good_event(s);
825 wwdt_qa_next_token(s);
826 wwdt_qa_timer_start(s);
827 } else {
828 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
829 LBE_TOKEN_EARLY_MASK);
830 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, SERR, 1);
831 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TERL, 1);
832 wwdt_bad_event(s);
833 }
834 } else {
835 INC_AND_ROLLOVER(acnt, 3);
836
837 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, ACNT, acnt);
838 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, SERR, 0);
839 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TERL, 0);
840 }
841}
842
843static void wwdt_qa_answer_mismatch(WWDT *s)
844{
845 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, SERR, 1);
846 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, TERR, 1);
847 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
848 LBE_TOKEN_ERROR_MASK);
849 wwdt_bad_event(s);
850}
851
852static void wwdt_enable_and_status_reg_postw(RegisterInfo *reg, uint64_t val64)
853{
854 bool success;
855 WWDT *s = XLNX_WWDT(reg->opaque);
856 uint32_t val = (uint32_t)val64;
857 bool wen_set = ARRAY_FIELD_EX32(s->regs, ENABLE_AND_STATUS_REG, WEN);
858 bool is_basic = !ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, WM);
859 bool wsw_clear = FIELD_EX32(val, ENABLE_AND_STATUS_REG, WSW);
860
861
862
863
864
865
866 if (wsw_clear) {
867 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, 0);
868 } else {
869 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WSW, s->window_2);
870 }
871
872 if (!s->wen && wen_set) {
873 success = wwdt_en(s);
874
875 if (!success) {
876 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WEN, 0);
877 }
878 } else if (s->wen && !wen_set) {
879 success = wwdt_disable(s);
880
881 if (!success) {
882 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, WEN, 1);
883 }
884 }
885
886 if (is_basic && wsw_clear) {
887 wwdt_basic_kick(s);
888 } else if (wsw_clear) {
889 qemu_log_mask(LOG_GUEST_ERROR, "%s: WSW is RO in Q&A mode.\n",
890 object_get_canonical_path(OBJECT(s)));
891 }
892
893 wwdt_update_irq(s);
894}
895
896static uint64_t wwdt_enable_and_status_reg_prew(RegisterInfo *reg,
897 uint64_t val64)
898{
899 WWDT *s = XLNX_WWDT(reg->opaque);
900 uint32_t val = (uint64_t)val64;
901 bool wen_set = FIELD_EX32(val, ENABLE_AND_STATUS_REG, WEN);
902 bool dis_prot = ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, WDP);
903
904 if (dis_prot) {
905 if (s->wen && !wen_set) {
906 qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot clear WEN when WDS is "
907 "set\n", object_get_canonical_path(OBJECT(s)));
908 FIELD_DP32(val, ENABLE_AND_STATUS_REG, WEN, 1);
909 }
910 }
911 return val;
912}
913
914static void wwdt_token_response_reg_postw(RegisterInfo *reg, uint64_t val)
915{
916 WWDT *s = XLNX_WWDT(reg->opaque);
917 bool is_basic = !ARRAY_FIELD_EX32(s->regs, FUNCTION_CONTROL_REG, WM);
918 uint8_t expected_ans;
919
920 if (is_basic) {
921 return;
922 }
923
924
925 if (!s->wwdt_qa_running) {
926 ARRAY_FIELD_DP32(s->regs, ENABLE_AND_STATUS_REG, LBE,
927 LBE_QA_NO_BAD_EVENT_MASK);
928 wwdt_qa_timer_start(s);
929
930 } else {
931 expected_ans = wwdt_qa_gen_ans(s);
932 if (expected_ans == val) {
933 wwdt_qa_answer_match(s);
934 } else {
935 wwdt_qa_answer_mismatch(s);
936 }
937 }
938}
939
940static void wwdt_interrupt_enable_reg_postw(RegisterInfo *reg, uint64_t val64)
941{
942 WWDT *s = XLNX_WWDT(reg->opaque);
943 uint32_t val = (uint32_t)val64;
944
945 val = ~val;
946 s->regs[R_INTERRUPT_MASK_REG] &= ~val;
947}
948
949static void wwdt_interrupt_disable_reg_postw(RegisterInfo *reg, uint64_t val64)
950{
951 WWDT *s = XLNX_WWDT(reg->opaque);
952 uint32_t val = (uint32_t)val64;
953
954 s->regs[R_INTERRUPT_MASK_REG] |= val;
955}
956
957static const RegisterAccessInfo wwdt_regs_info[] = {
958 { .name = "MASTER_WRITE_CONTROL_REG", .addr = A_MASTER_WRITE_CONTROL_REG,
959 .reset = 0x1,
960 .rsvd = 0xfffffffe,
961 },{ .name = "ENABLE_AND_STATUS_REG", .addr = A_ENABLE_AND_STATUS_REG,
962 .reset = 0x500000,
963 .rsvd = 0xf88c20c0,
964 .ro = 0x70de3c,
965 .w1c = 0x7030002,
966 .post_write = wwdt_enable_and_status_reg_postw,
967 .pre_write = wwdt_enable_and_status_reg_prew,
968 },{ .name = "FUNCTION_CONTROL_REG", .addr = A_FUNCTION_CONTROL_REG,
969 .rsvd = 0xffff0020,
970 },{ .name = "FIRST_WINDOW_CONFIG_REG", .addr = A_FIRST_WINDOW_CONFIG_REG,
971 },{ .name = "SECOND_WINDOW_CONFIG_REG", .addr = A_SECOND_WINDOW_CONFIG_REG,
972 },{ .name = "SST_WINDOW_CONFIG_REG", .addr = A_SST_WINDOW_CONFIG_REG,
973 },{ .name = "TASK_SIGNATURE_REG0", .addr = A_TASK_SIGNATURE_REG0,
974 },{ .name = "TASK_SIGNATURE_REG1", .addr = A_TASK_SIGNATURE_REG1,
975 },{ .name = "SECOND_SEQ_TIMER_REG", .addr = A_SECOND_SEQ_TIMER_REG,
976 .ro = 0xffffffff,
977 },{ .name = "TOKEN_FEEDBACK_REG", .addr = A_TOKEN_FEEDBACK_REG,
978 .rsvd = 0xfffff0f0,
979 },{ .name = "TOKEN_RESPONSE_REG", .addr = A_TOKEN_RESPONSE_REG,
980 .rsvd = 0xffffff00,
981 .post_write = wwdt_token_response_reg_postw,
982 },{ .name = "INTERRUPT_ENABLE_REG", .addr = A_INTERRUPT_ENABLE_REG,
983 .rsvd = 0xfffffff8,
984 .post_write = wwdt_interrupt_enable_reg_postw,
985 },{ .name = "INTERRUPT_DISABLE_REG", .addr = A_INTERRUPT_DISABLE_REG,
986 .rsvd = 0xfffffff8,
987 .post_write = wwdt_interrupt_disable_reg_postw,
988 },{ .name = "INTERRUPT_MASK_REG", .addr = A_INTERRUPT_MASK_REG,
989 .reset = 0x2,
990 .rsvd = 0xfffffff8,
991 .ro = 0x7,
992 },{ .name = "ECO", .addr = A_ECO,
993 },{ .name = "GWDT_REFRESH_REG", .addr = A_GWDT_REFRESH_REG,
994 .rsvd = 0xfffffffe,
995 .post_write = gwdt_refresh_reg_postw,
996 },{ .name = "GWDT_CNTRL_STATUS_REG", .addr = A_GWDT_CNTRL_STATUS_REG,
997 .rsvd = 0xfffffff8,
998 .ro = 0x6,
999 .post_write = gwdt_cntrl_status_reg_postw,
1000 },{ .name = "GWDT_OFFSET_REG", .addr = A_GWDT_OFFSET_REG,
1001 },{ .name = "GWDT_COMPARE_VALUE_REG0", .addr = A_GWDT_COMPARE_VALUE_REG0,
1002 },{ .name = "GWDT_COMPARE_VALUE_REG1", .addr = A_GWDT_COMPARE_VALUE_REG1,
1003 },{ .name = "GWDT_WARM_RESET_REG", .addr = A_GWDT_WARM_RESET_REG,
1004 .rsvd = 0xfffffffe,
1005 .post_write = gwdt_warm_reset_reg_postw,
1006 }
1007};
1008
1009static void wwdt_reset(DeviceState *dev)
1010{
1011 WWDT *s = XLNX_WWDT(dev);
1012 uint32_t i;
1013
1014 for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
1015 register_reset(&s->regs_info[i]);
1016 }
1017 s->lfsr = 0;
1018 s->token_cnt = 0;
1019 s->wwdt_qa_running = false;
1020}
1021
1022static WWDT *wwdt_from_mr(void *mr_accessor)
1023{
1024 RegisterInfoArray *reg_array = mr_accessor;
1025 Object *obj;
1026
1027 assert(reg_array != NULL);
1028
1029 obj = reg_array->mem.owner;
1030 assert(obj);
1031
1032 return XLNX_WWDT(obj);
1033}
1034
1035static MemTxResult wwdt_write(void *opaque, hwaddr addr, uint64_t val,
1036 unsigned size, MemTxAttrs attrs)
1037{
1038 WWDT *s = wwdt_from_mr(opaque);
1039 bool locked = !ARRAY_FIELD_EX32(s->regs, MASTER_WRITE_CONTROL_REG, MWC);
1040
1041
1042 if (locked && (addr > A_MASTER_WRITE_CONTROL_REG &&
1043 addr < A_GWDT_REFRESH_REG)) {
1044 qemu_log_mask(LOG_GUEST_ERROR, "%s: Accessing locked register "
1045 "0x%"HWADDR_PRIx"\n",
1046 object_get_canonical_path(OBJECT(s)), addr);
1047 return MEMTX_ERROR;
1048 }
1049
1050 register_write_memory(opaque, addr, val, size);
1051 return MEMTX_OK;
1052}
1053
1054static const MemoryRegionOps wwdt_ops = {
1055 .read = register_read_memory,
1056 .write_with_attrs = wwdt_write,
1057 .endianness = DEVICE_LITTLE_ENDIAN,
1058 .valid = {
1059 .min_access_size = 4,
1060 .max_access_size = 4,
1061 },
1062};
1063
1064static void wwdt_realize(DeviceState *dev, Error **errp)
1065{
1066 WWDT *s = XLNX_WWDT(dev);
1067
1068 if (!s->pclk) {
1069 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "pclk");
1070 }
1071}
1072
1073static void wwdt_init(Object *obj)
1074{
1075 WWDT *s = XLNX_WWDT(obj);
1076 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1077 RegisterInfoArray *reg_array;
1078
1079 memory_region_init(&s->iomem, obj, TYPE_XILINX_WWDT, WWDT_R_MAX * 4);
1080 reg_array =
1081 register_init_block32(DEVICE(obj), wwdt_regs_info,
1082 ARRAY_SIZE(wwdt_regs_info),
1083 s->regs_info, s->regs,
1084 &wwdt_ops,
1085 XILINX_WWDT_ERR_DEBUG,
1086 WWDT_R_MAX * 4);
1087 memory_region_add_subregion(&s->iomem,
1088 0x0,
1089 ®_array->mem);
1090 sysbus_init_mmio(sbd, &s->iomem);
1091
1092
1093 sysbus_init_irq(sbd, &s->wwdt_irq);
1094 sysbus_init_irq(sbd, &s->wwdt_reset_pending);
1095 sysbus_init_irq(sbd, &s->gwdt_ws0);
1096 sysbus_init_irq(sbd, &s->gwdt_ws1);
1097
1098 qdev_init_gpio_out_named(DEVICE(obj), &s->wwdt_reset, "reset-out", 1);
1099
1100 s->gwdt_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, gwdt_time_elapsed, s);
1101 s->wwdt_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, wwdt_time_elapsed, s);
1102 s->wwdt_irq_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, wwdt_irq_time_elapsed,
1103 s);
1104 s->sst_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, wwdt_sst_time_elapsed, s);
1105}
1106
1107static Property wwdt_properties[] = {
1108 DEFINE_PROP_UINT64("pclk", WWDT, pclk, 0),
1109 DEFINE_PROP_END_OF_LIST(),
1110};
1111
1112static const FDTGenericGPIOSet wwdt_client_gpios[] = {
1113 {
1114 .names = &fdt_generic_gpio_name_set_gpio,
1115 .gpios = (FDTGenericGPIOConnection[]) {
1116 { .name = "reset-out", .fdt_index = 0, .range = 1 },
1117 { },
1118 }
1119 },
1120 { },
1121};
1122
1123static const VMStateDescription vmstate_wwdt = {
1124 .name = TYPE_XILINX_WWDT,
1125 .version_id = 1,
1126 .minimum_version_id = 1,
1127 .fields = (VMStateField[]) {
1128 VMSTATE_UINT32_ARRAY(regs, WWDT, WWDT_R_MAX),
1129 VMSTATE_END_OF_LIST(),
1130 }
1131};
1132
1133static void wwdt_class_init(ObjectClass *klass, void *data)
1134{
1135 DeviceClass *dc = DEVICE_CLASS(klass);
1136 FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(klass);
1137
1138 dc->reset = wwdt_reset;
1139 dc->realize = wwdt_realize;
1140 dc->vmsd = &vmstate_wwdt;
1141 device_class_set_props(dc, wwdt_properties);
1142 fggc->client_gpios = wwdt_client_gpios;
1143}
1144
1145static const TypeInfo wwdt_info = {
1146 .name = TYPE_XILINX_WWDT,
1147 .parent = TYPE_SYS_BUS_DEVICE,
1148 .instance_size = sizeof(WWDT),
1149 .class_init = wwdt_class_init,
1150 .instance_init = wwdt_init,
1151 .interfaces = (InterfaceInfo[]) {
1152 { TYPE_FDT_GENERIC_GPIO },
1153 { }
1154 }
1155};
1156
1157static void wwdt_register_types(void)
1158{
1159 type_register_static(&wwdt_info);
1160}
1161
1162type_init(wwdt_register_types)
1163