xref: /openbmc/qemu/hw/watchdog/allwinner-wdt.c (revision 17b9730f)
1*17b9730fSStrahinja Jankovic /*
2*17b9730fSStrahinja Jankovic  * Allwinner Watchdog emulation
3*17b9730fSStrahinja Jankovic  *
4*17b9730fSStrahinja Jankovic  * Copyright (C) 2023 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
5*17b9730fSStrahinja Jankovic  *
6*17b9730fSStrahinja Jankovic  *  This file is derived from Allwinner RTC,
7*17b9730fSStrahinja Jankovic  *  by Niek Linnenbank.
8*17b9730fSStrahinja Jankovic  *
9*17b9730fSStrahinja Jankovic  * This program is free software: you can redistribute it and/or modify
10*17b9730fSStrahinja Jankovic  * it under the terms of the GNU General Public License as published by
11*17b9730fSStrahinja Jankovic  * the Free Software Foundation, either version 2 of the License, or
12*17b9730fSStrahinja Jankovic  * (at your option) any later version.
13*17b9730fSStrahinja Jankovic  *
14*17b9730fSStrahinja Jankovic  * This program is distributed in the hope that it will be useful,
15*17b9730fSStrahinja Jankovic  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16*17b9730fSStrahinja Jankovic  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17*17b9730fSStrahinja Jankovic  * GNU General Public License for more details.
18*17b9730fSStrahinja Jankovic  *
19*17b9730fSStrahinja Jankovic  * You should have received a copy of the GNU General Public License
20*17b9730fSStrahinja Jankovic  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21*17b9730fSStrahinja Jankovic  */
22*17b9730fSStrahinja Jankovic 
23*17b9730fSStrahinja Jankovic #include "qemu/osdep.h"
24*17b9730fSStrahinja Jankovic #include "qemu/log.h"
25*17b9730fSStrahinja Jankovic #include "qemu/units.h"
26*17b9730fSStrahinja Jankovic #include "qemu/module.h"
27*17b9730fSStrahinja Jankovic #include "trace.h"
28*17b9730fSStrahinja Jankovic #include "hw/sysbus.h"
29*17b9730fSStrahinja Jankovic #include "hw/registerfields.h"
30*17b9730fSStrahinja Jankovic #include "hw/watchdog/allwinner-wdt.h"
31*17b9730fSStrahinja Jankovic #include "sysemu/watchdog.h"
32*17b9730fSStrahinja Jankovic #include "migration/vmstate.h"
33*17b9730fSStrahinja Jankovic 
34*17b9730fSStrahinja Jankovic /* WDT registers */
35*17b9730fSStrahinja Jankovic enum {
36*17b9730fSStrahinja Jankovic     REG_IRQ_EN = 0,     /* Watchdog interrupt enable */
37*17b9730fSStrahinja Jankovic     REG_IRQ_STA,        /* Watchdog interrupt status */
38*17b9730fSStrahinja Jankovic     REG_CTRL,           /* Watchdog control register */
39*17b9730fSStrahinja Jankovic     REG_CFG,            /* Watchdog configuration register */
40*17b9730fSStrahinja Jankovic     REG_MODE,           /* Watchdog mode register */
41*17b9730fSStrahinja Jankovic };
42*17b9730fSStrahinja Jankovic 
43*17b9730fSStrahinja Jankovic /* Universal WDT register flags */
44*17b9730fSStrahinja Jankovic #define WDT_RESTART_MASK    (1 << 0)
45*17b9730fSStrahinja Jankovic #define WDT_EN_MASK         (1 << 0)
46*17b9730fSStrahinja Jankovic 
47*17b9730fSStrahinja Jankovic /* sun4i specific WDT register flags */
48*17b9730fSStrahinja Jankovic #define RST_EN_SUN4I_MASK       (1 << 1)
49*17b9730fSStrahinja Jankovic #define INTV_VALUE_SUN4I_SHIFT  (3)
50*17b9730fSStrahinja Jankovic #define INTV_VALUE_SUN4I_MASK   (0xfu << INTV_VALUE_SUN4I_SHIFT)
51*17b9730fSStrahinja Jankovic 
52*17b9730fSStrahinja Jankovic /* sun6i specific WDT register flags */
53*17b9730fSStrahinja Jankovic #define RST_EN_SUN6I_MASK       (1 << 0)
54*17b9730fSStrahinja Jankovic #define KEY_FIELD_SUN6I_SHIFT   (1)
55*17b9730fSStrahinja Jankovic #define KEY_FIELD_SUN6I_MASK    (0xfffu << KEY_FIELD_SUN6I_SHIFT)
56*17b9730fSStrahinja Jankovic #define KEY_FIELD_SUN6I         (0xA57u)
57*17b9730fSStrahinja Jankovic #define INTV_VALUE_SUN6I_SHIFT  (4)
58*17b9730fSStrahinja Jankovic #define INTV_VALUE_SUN6I_MASK   (0xfu << INTV_VALUE_SUN6I_SHIFT)
59*17b9730fSStrahinja Jankovic 
60*17b9730fSStrahinja Jankovic /* Map of INTV_VALUE to 0.5s units. */
61*17b9730fSStrahinja Jankovic static const uint8_t allwinner_wdt_count_map[] = {
62*17b9730fSStrahinja Jankovic     1,
63*17b9730fSStrahinja Jankovic     2,
64*17b9730fSStrahinja Jankovic     4,
65*17b9730fSStrahinja Jankovic     6,
66*17b9730fSStrahinja Jankovic     8,
67*17b9730fSStrahinja Jankovic     10,
68*17b9730fSStrahinja Jankovic     12,
69*17b9730fSStrahinja Jankovic     16,
70*17b9730fSStrahinja Jankovic     20,
71*17b9730fSStrahinja Jankovic     24,
72*17b9730fSStrahinja Jankovic     28,
73*17b9730fSStrahinja Jankovic     32
74*17b9730fSStrahinja Jankovic };
75*17b9730fSStrahinja Jankovic 
76*17b9730fSStrahinja Jankovic /* WDT sun4i register map (offset to name) */
77*17b9730fSStrahinja Jankovic const uint8_t allwinner_wdt_sun4i_regmap[] = {
78*17b9730fSStrahinja Jankovic     [0x0000] = REG_CTRL,
79*17b9730fSStrahinja Jankovic     [0x0004] = REG_MODE,
80*17b9730fSStrahinja Jankovic };
81*17b9730fSStrahinja Jankovic 
82*17b9730fSStrahinja Jankovic /* WDT sun6i register map (offset to name) */
83*17b9730fSStrahinja Jankovic const uint8_t allwinner_wdt_sun6i_regmap[] = {
84*17b9730fSStrahinja Jankovic     [0x0000] = REG_IRQ_EN,
85*17b9730fSStrahinja Jankovic     [0x0004] = REG_IRQ_STA,
86*17b9730fSStrahinja Jankovic     [0x0010] = REG_CTRL,
87*17b9730fSStrahinja Jankovic     [0x0014] = REG_CFG,
88*17b9730fSStrahinja Jankovic     [0x0018] = REG_MODE,
89*17b9730fSStrahinja Jankovic };
90*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_read(AwWdtState * s,uint32_t offset)91*17b9730fSStrahinja Jankovic static bool allwinner_wdt_sun4i_read(AwWdtState *s, uint32_t offset)
92*17b9730fSStrahinja Jankovic {
93*17b9730fSStrahinja Jankovic     /* no sun4i specific registers currently implemented */
94*17b9730fSStrahinja Jankovic     return false;
95*17b9730fSStrahinja Jankovic }
96*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_write(AwWdtState * s,uint32_t offset,uint32_t data)97*17b9730fSStrahinja Jankovic static bool allwinner_wdt_sun4i_write(AwWdtState *s, uint32_t offset,
98*17b9730fSStrahinja Jankovic                                       uint32_t data)
99*17b9730fSStrahinja Jankovic {
100*17b9730fSStrahinja Jankovic     /* no sun4i specific registers currently implemented */
101*17b9730fSStrahinja Jankovic     return false;
102*17b9730fSStrahinja Jankovic }
103*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_can_reset_system(AwWdtState * s)104*17b9730fSStrahinja Jankovic static bool allwinner_wdt_sun4i_can_reset_system(AwWdtState *s)
105*17b9730fSStrahinja Jankovic {
106*17b9730fSStrahinja Jankovic     if (s->regs[REG_MODE] & RST_EN_SUN4I_MASK) {
107*17b9730fSStrahinja Jankovic         return true;
108*17b9730fSStrahinja Jankovic     } else {
109*17b9730fSStrahinja Jankovic         return false;
110*17b9730fSStrahinja Jankovic     }
111*17b9730fSStrahinja Jankovic }
112*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_is_key_valid(AwWdtState * s,uint32_t val)113*17b9730fSStrahinja Jankovic static bool allwinner_wdt_sun4i_is_key_valid(AwWdtState *s, uint32_t val)
114*17b9730fSStrahinja Jankovic {
115*17b9730fSStrahinja Jankovic     /* sun4i has no key */
116*17b9730fSStrahinja Jankovic     return true;
117*17b9730fSStrahinja Jankovic }
118*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_get_intv_value(AwWdtState * s)119*17b9730fSStrahinja Jankovic static uint8_t allwinner_wdt_sun4i_get_intv_value(AwWdtState *s)
120*17b9730fSStrahinja Jankovic {
121*17b9730fSStrahinja Jankovic     return ((s->regs[REG_MODE] & INTV_VALUE_SUN4I_MASK) >>
122*17b9730fSStrahinja Jankovic             INTV_VALUE_SUN4I_SHIFT);
123*17b9730fSStrahinja Jankovic }
124*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_read(AwWdtState * s,uint32_t offset)125*17b9730fSStrahinja Jankovic static bool allwinner_wdt_sun6i_read(AwWdtState *s, uint32_t offset)
126*17b9730fSStrahinja Jankovic {
127*17b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
128*17b9730fSStrahinja Jankovic 
129*17b9730fSStrahinja Jankovic     switch (c->regmap[offset]) {
130*17b9730fSStrahinja Jankovic     case REG_IRQ_EN:
131*17b9730fSStrahinja Jankovic     case REG_IRQ_STA:
132*17b9730fSStrahinja Jankovic     case REG_CFG:
133*17b9730fSStrahinja Jankovic         return true;
134*17b9730fSStrahinja Jankovic     default:
135*17b9730fSStrahinja Jankovic         break;
136*17b9730fSStrahinja Jankovic     }
137*17b9730fSStrahinja Jankovic     return false;
138*17b9730fSStrahinja Jankovic }
139*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_write(AwWdtState * s,uint32_t offset,uint32_t data)140*17b9730fSStrahinja Jankovic static bool allwinner_wdt_sun6i_write(AwWdtState *s, uint32_t offset,
141*17b9730fSStrahinja Jankovic                                       uint32_t data)
142*17b9730fSStrahinja Jankovic {
143*17b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
144*17b9730fSStrahinja Jankovic 
145*17b9730fSStrahinja Jankovic     switch (c->regmap[offset]) {
146*17b9730fSStrahinja Jankovic     case REG_IRQ_EN:
147*17b9730fSStrahinja Jankovic     case REG_IRQ_STA:
148*17b9730fSStrahinja Jankovic     case REG_CFG:
149*17b9730fSStrahinja Jankovic         return true;
150*17b9730fSStrahinja Jankovic     default:
151*17b9730fSStrahinja Jankovic         break;
152*17b9730fSStrahinja Jankovic     }
153*17b9730fSStrahinja Jankovic     return false;
154*17b9730fSStrahinja Jankovic }
155*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_can_reset_system(AwWdtState * s)156*17b9730fSStrahinja Jankovic static bool allwinner_wdt_sun6i_can_reset_system(AwWdtState *s)
157*17b9730fSStrahinja Jankovic {
158*17b9730fSStrahinja Jankovic     if (s->regs[REG_CFG] & RST_EN_SUN6I_MASK) {
159*17b9730fSStrahinja Jankovic         return true;
160*17b9730fSStrahinja Jankovic     } else {
161*17b9730fSStrahinja Jankovic         return false;
162*17b9730fSStrahinja Jankovic     }
163*17b9730fSStrahinja Jankovic }
164*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_is_key_valid(AwWdtState * s,uint32_t val)165*17b9730fSStrahinja Jankovic static bool allwinner_wdt_sun6i_is_key_valid(AwWdtState *s, uint32_t val)
166*17b9730fSStrahinja Jankovic {
167*17b9730fSStrahinja Jankovic     uint16_t key = (val & KEY_FIELD_SUN6I_MASK) >> KEY_FIELD_SUN6I_SHIFT;
168*17b9730fSStrahinja Jankovic     return (key == KEY_FIELD_SUN6I);
169*17b9730fSStrahinja Jankovic }
170*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_get_intv_value(AwWdtState * s)171*17b9730fSStrahinja Jankovic static uint8_t allwinner_wdt_sun6i_get_intv_value(AwWdtState *s)
172*17b9730fSStrahinja Jankovic {
173*17b9730fSStrahinja Jankovic     return ((s->regs[REG_MODE] & INTV_VALUE_SUN6I_MASK) >>
174*17b9730fSStrahinja Jankovic             INTV_VALUE_SUN6I_SHIFT);
175*17b9730fSStrahinja Jankovic }
176*17b9730fSStrahinja Jankovic 
allwinner_wdt_update_timer(AwWdtState * s)177*17b9730fSStrahinja Jankovic static void allwinner_wdt_update_timer(AwWdtState *s)
178*17b9730fSStrahinja Jankovic {
179*17b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
180*17b9730fSStrahinja Jankovic     uint8_t count = c->get_intv_value(s);
181*17b9730fSStrahinja Jankovic 
182*17b9730fSStrahinja Jankovic     ptimer_transaction_begin(s->timer);
183*17b9730fSStrahinja Jankovic     ptimer_stop(s->timer);
184*17b9730fSStrahinja Jankovic 
185*17b9730fSStrahinja Jankovic     /* Use map to convert. */
186*17b9730fSStrahinja Jankovic     if (count < sizeof(allwinner_wdt_count_map)) {
187*17b9730fSStrahinja Jankovic         ptimer_set_count(s->timer, allwinner_wdt_count_map[count]);
188*17b9730fSStrahinja Jankovic     } else {
189*17b9730fSStrahinja Jankovic         qemu_log_mask(LOG_GUEST_ERROR, "%s: incorrect INTV_VALUE 0x%02x\n",
190*17b9730fSStrahinja Jankovic                 __func__, count);
191*17b9730fSStrahinja Jankovic     }
192*17b9730fSStrahinja Jankovic 
193*17b9730fSStrahinja Jankovic     ptimer_run(s->timer, 1);
194*17b9730fSStrahinja Jankovic     ptimer_transaction_commit(s->timer);
195*17b9730fSStrahinja Jankovic 
196*17b9730fSStrahinja Jankovic     trace_allwinner_wdt_update_timer(count);
197*17b9730fSStrahinja Jankovic }
198*17b9730fSStrahinja Jankovic 
allwinner_wdt_read(void * opaque,hwaddr offset,unsigned size)199*17b9730fSStrahinja Jankovic static uint64_t allwinner_wdt_read(void *opaque, hwaddr offset,
200*17b9730fSStrahinja Jankovic                                        unsigned size)
201*17b9730fSStrahinja Jankovic {
202*17b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(opaque);
203*17b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
204*17b9730fSStrahinja Jankovic     uint64_t r;
205*17b9730fSStrahinja Jankovic 
206*17b9730fSStrahinja Jankovic     if (offset >= c->regmap_size) {
207*17b9730fSStrahinja Jankovic         qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
208*17b9730fSStrahinja Jankovic                       __func__, (uint32_t)offset);
209*17b9730fSStrahinja Jankovic         return 0;
210*17b9730fSStrahinja Jankovic     }
211*17b9730fSStrahinja Jankovic 
212*17b9730fSStrahinja Jankovic     switch (c->regmap[offset]) {
213*17b9730fSStrahinja Jankovic     case REG_CTRL:
214*17b9730fSStrahinja Jankovic     case REG_MODE:
215*17b9730fSStrahinja Jankovic         r = s->regs[c->regmap[offset]];
216*17b9730fSStrahinja Jankovic         break;
217*17b9730fSStrahinja Jankovic     default:
218*17b9730fSStrahinja Jankovic         if (!c->read(s, offset)) {
219*17b9730fSStrahinja Jankovic             qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n",
220*17b9730fSStrahinja Jankovic                             __func__, (uint32_t)offset);
221*17b9730fSStrahinja Jankovic             return 0;
222*17b9730fSStrahinja Jankovic         }
223*17b9730fSStrahinja Jankovic         r = s->regs[c->regmap[offset]];
224*17b9730fSStrahinja Jankovic         break;
225*17b9730fSStrahinja Jankovic     }
226*17b9730fSStrahinja Jankovic 
227*17b9730fSStrahinja Jankovic     trace_allwinner_wdt_read(offset, r, size);
228*17b9730fSStrahinja Jankovic 
229*17b9730fSStrahinja Jankovic     return r;
230*17b9730fSStrahinja Jankovic }
231*17b9730fSStrahinja Jankovic 
allwinner_wdt_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)232*17b9730fSStrahinja Jankovic static void allwinner_wdt_write(void *opaque, hwaddr offset,
233*17b9730fSStrahinja Jankovic                                    uint64_t val, unsigned size)
234*17b9730fSStrahinja Jankovic {
235*17b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(opaque);
236*17b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
237*17b9730fSStrahinja Jankovic     uint32_t old_val;
238*17b9730fSStrahinja Jankovic 
239*17b9730fSStrahinja Jankovic     if (offset >= c->regmap_size) {
240*17b9730fSStrahinja Jankovic         qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
241*17b9730fSStrahinja Jankovic                       __func__, (uint32_t)offset);
242*17b9730fSStrahinja Jankovic         return;
243*17b9730fSStrahinja Jankovic     }
244*17b9730fSStrahinja Jankovic 
245*17b9730fSStrahinja Jankovic    trace_allwinner_wdt_write(offset, val, size);
246*17b9730fSStrahinja Jankovic 
247*17b9730fSStrahinja Jankovic     switch (c->regmap[offset]) {
248*17b9730fSStrahinja Jankovic     case REG_CTRL:
249*17b9730fSStrahinja Jankovic         if (c->is_key_valid(s, val)) {
250*17b9730fSStrahinja Jankovic             if (val & WDT_RESTART_MASK) {
251*17b9730fSStrahinja Jankovic                 /* Kick timer */
252*17b9730fSStrahinja Jankovic                 allwinner_wdt_update_timer(s);
253*17b9730fSStrahinja Jankovic             }
254*17b9730fSStrahinja Jankovic         }
255*17b9730fSStrahinja Jankovic         break;
256*17b9730fSStrahinja Jankovic     case REG_MODE:
257*17b9730fSStrahinja Jankovic         old_val = s->regs[REG_MODE];
258*17b9730fSStrahinja Jankovic         s->regs[REG_MODE] = (uint32_t)val;
259*17b9730fSStrahinja Jankovic 
260*17b9730fSStrahinja Jankovic         /* Check for rising edge on WDOG_MODE_EN */
261*17b9730fSStrahinja Jankovic         if ((s->regs[REG_MODE] & ~old_val) & WDT_EN_MASK) {
262*17b9730fSStrahinja Jankovic             allwinner_wdt_update_timer(s);
263*17b9730fSStrahinja Jankovic         }
264*17b9730fSStrahinja Jankovic         break;
265*17b9730fSStrahinja Jankovic     default:
266*17b9730fSStrahinja Jankovic         if (!c->write(s, offset, val)) {
267*17b9730fSStrahinja Jankovic             qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n",
268*17b9730fSStrahinja Jankovic                           __func__, (uint32_t)offset);
269*17b9730fSStrahinja Jankovic         }
270*17b9730fSStrahinja Jankovic         s->regs[c->regmap[offset]] = (uint32_t)val;
271*17b9730fSStrahinja Jankovic         break;
272*17b9730fSStrahinja Jankovic     }
273*17b9730fSStrahinja Jankovic }
274*17b9730fSStrahinja Jankovic 
275*17b9730fSStrahinja Jankovic static const MemoryRegionOps allwinner_wdt_ops = {
276*17b9730fSStrahinja Jankovic     .read = allwinner_wdt_read,
277*17b9730fSStrahinja Jankovic     .write = allwinner_wdt_write,
278*17b9730fSStrahinja Jankovic     .endianness = DEVICE_NATIVE_ENDIAN,
279*17b9730fSStrahinja Jankovic     .valid = {
280*17b9730fSStrahinja Jankovic         .min_access_size = 4,
281*17b9730fSStrahinja Jankovic         .max_access_size = 4,
282*17b9730fSStrahinja Jankovic     },
283*17b9730fSStrahinja Jankovic     .impl.min_access_size = 4,
284*17b9730fSStrahinja Jankovic };
285*17b9730fSStrahinja Jankovic 
allwinner_wdt_expired(void * opaque)286*17b9730fSStrahinja Jankovic static void allwinner_wdt_expired(void *opaque)
287*17b9730fSStrahinja Jankovic {
288*17b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(opaque);
289*17b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
290*17b9730fSStrahinja Jankovic 
291*17b9730fSStrahinja Jankovic     bool enabled = s->regs[REG_MODE] & WDT_EN_MASK;
292*17b9730fSStrahinja Jankovic     bool reset_enabled = c->can_reset_system(s);
293*17b9730fSStrahinja Jankovic 
294*17b9730fSStrahinja Jankovic     trace_allwinner_wdt_expired(enabled, reset_enabled);
295*17b9730fSStrahinja Jankovic 
296*17b9730fSStrahinja Jankovic     /* Perform watchdog action if watchdog is enabled and can trigger reset */
297*17b9730fSStrahinja Jankovic     if (enabled && reset_enabled) {
298*17b9730fSStrahinja Jankovic         watchdog_perform_action();
299*17b9730fSStrahinja Jankovic     }
300*17b9730fSStrahinja Jankovic }
301*17b9730fSStrahinja Jankovic 
allwinner_wdt_reset_enter(Object * obj,ResetType type)302*17b9730fSStrahinja Jankovic static void allwinner_wdt_reset_enter(Object *obj, ResetType type)
303*17b9730fSStrahinja Jankovic {
304*17b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(obj);
305*17b9730fSStrahinja Jankovic 
306*17b9730fSStrahinja Jankovic     trace_allwinner_wdt_reset_enter();
307*17b9730fSStrahinja Jankovic 
308*17b9730fSStrahinja Jankovic     /* Clear registers */
309*17b9730fSStrahinja Jankovic     memset(s->regs, 0, sizeof(s->regs));
310*17b9730fSStrahinja Jankovic }
311*17b9730fSStrahinja Jankovic 
312*17b9730fSStrahinja Jankovic static const VMStateDescription allwinner_wdt_vmstate = {
313*17b9730fSStrahinja Jankovic     .name = "allwinner-wdt",
314*17b9730fSStrahinja Jankovic     .version_id = 1,
315*17b9730fSStrahinja Jankovic     .minimum_version_id = 1,
316*17b9730fSStrahinja Jankovic     .fields = (VMStateField[]) {
317*17b9730fSStrahinja Jankovic         VMSTATE_PTIMER(timer, AwWdtState),
318*17b9730fSStrahinja Jankovic         VMSTATE_UINT32_ARRAY(regs, AwWdtState, AW_WDT_REGS_NUM),
319*17b9730fSStrahinja Jankovic         VMSTATE_END_OF_LIST()
320*17b9730fSStrahinja Jankovic     }
321*17b9730fSStrahinja Jankovic };
322*17b9730fSStrahinja Jankovic 
allwinner_wdt_init(Object * obj)323*17b9730fSStrahinja Jankovic static void allwinner_wdt_init(Object *obj)
324*17b9730fSStrahinja Jankovic {
325*17b9730fSStrahinja Jankovic     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
326*17b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(obj);
327*17b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
328*17b9730fSStrahinja Jankovic 
329*17b9730fSStrahinja Jankovic     /* Memory mapping */
330*17b9730fSStrahinja Jankovic     memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_wdt_ops, s,
331*17b9730fSStrahinja Jankovic                           TYPE_AW_WDT, c->regmap_size * 4);
332*17b9730fSStrahinja Jankovic     sysbus_init_mmio(sbd, &s->iomem);
333*17b9730fSStrahinja Jankovic }
334*17b9730fSStrahinja Jankovic 
allwinner_wdt_realize(DeviceState * dev,Error ** errp)335*17b9730fSStrahinja Jankovic static void allwinner_wdt_realize(DeviceState *dev, Error **errp)
336*17b9730fSStrahinja Jankovic {
337*17b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(dev);
338*17b9730fSStrahinja Jankovic 
339*17b9730fSStrahinja Jankovic     s->timer = ptimer_init(allwinner_wdt_expired, s,
340*17b9730fSStrahinja Jankovic                            PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
341*17b9730fSStrahinja Jankovic                            PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
342*17b9730fSStrahinja Jankovic                            PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
343*17b9730fSStrahinja Jankovic 
344*17b9730fSStrahinja Jankovic     ptimer_transaction_begin(s->timer);
345*17b9730fSStrahinja Jankovic     /* Set to 2Hz (0.5s period); other periods are multiples of 0.5s. */
346*17b9730fSStrahinja Jankovic     ptimer_set_freq(s->timer, 2);
347*17b9730fSStrahinja Jankovic     ptimer_set_limit(s->timer, 0xff, 1);
348*17b9730fSStrahinja Jankovic     ptimer_transaction_commit(s->timer);
349*17b9730fSStrahinja Jankovic }
350*17b9730fSStrahinja Jankovic 
allwinner_wdt_class_init(ObjectClass * klass,void * data)351*17b9730fSStrahinja Jankovic static void allwinner_wdt_class_init(ObjectClass *klass, void *data)
352*17b9730fSStrahinja Jankovic {
353*17b9730fSStrahinja Jankovic     DeviceClass *dc = DEVICE_CLASS(klass);
354*17b9730fSStrahinja Jankovic     ResettableClass *rc = RESETTABLE_CLASS(klass);
355*17b9730fSStrahinja Jankovic 
356*17b9730fSStrahinja Jankovic     rc->phases.enter = allwinner_wdt_reset_enter;
357*17b9730fSStrahinja Jankovic     dc->realize = allwinner_wdt_realize;
358*17b9730fSStrahinja Jankovic     dc->vmsd = &allwinner_wdt_vmstate;
359*17b9730fSStrahinja Jankovic }
360*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_class_init(ObjectClass * klass,void * data)361*17b9730fSStrahinja Jankovic static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, void *data)
362*17b9730fSStrahinja Jankovic {
363*17b9730fSStrahinja Jankovic     AwWdtClass *awc = AW_WDT_CLASS(klass);
364*17b9730fSStrahinja Jankovic 
365*17b9730fSStrahinja Jankovic     awc->regmap = allwinner_wdt_sun4i_regmap;
366*17b9730fSStrahinja Jankovic     awc->regmap_size = sizeof(allwinner_wdt_sun4i_regmap);
367*17b9730fSStrahinja Jankovic     awc->read = allwinner_wdt_sun4i_read;
368*17b9730fSStrahinja Jankovic     awc->write = allwinner_wdt_sun4i_write;
369*17b9730fSStrahinja Jankovic     awc->can_reset_system = allwinner_wdt_sun4i_can_reset_system;
370*17b9730fSStrahinja Jankovic     awc->is_key_valid = allwinner_wdt_sun4i_is_key_valid;
371*17b9730fSStrahinja Jankovic     awc->get_intv_value = allwinner_wdt_sun4i_get_intv_value;
372*17b9730fSStrahinja Jankovic }
373*17b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_class_init(ObjectClass * klass,void * data)374*17b9730fSStrahinja Jankovic static void allwinner_wdt_sun6i_class_init(ObjectClass *klass, void *data)
375*17b9730fSStrahinja Jankovic {
376*17b9730fSStrahinja Jankovic     AwWdtClass *awc = AW_WDT_CLASS(klass);
377*17b9730fSStrahinja Jankovic 
378*17b9730fSStrahinja Jankovic     awc->regmap = allwinner_wdt_sun6i_regmap;
379*17b9730fSStrahinja Jankovic     awc->regmap_size = sizeof(allwinner_wdt_sun6i_regmap);
380*17b9730fSStrahinja Jankovic     awc->read = allwinner_wdt_sun6i_read;
381*17b9730fSStrahinja Jankovic     awc->write = allwinner_wdt_sun6i_write;
382*17b9730fSStrahinja Jankovic     awc->can_reset_system = allwinner_wdt_sun6i_can_reset_system;
383*17b9730fSStrahinja Jankovic     awc->is_key_valid = allwinner_wdt_sun6i_is_key_valid;
384*17b9730fSStrahinja Jankovic     awc->get_intv_value = allwinner_wdt_sun6i_get_intv_value;
385*17b9730fSStrahinja Jankovic }
386*17b9730fSStrahinja Jankovic 
387*17b9730fSStrahinja Jankovic static const TypeInfo allwinner_wdt_info = {
388*17b9730fSStrahinja Jankovic     .name          = TYPE_AW_WDT,
389*17b9730fSStrahinja Jankovic     .parent        = TYPE_SYS_BUS_DEVICE,
390*17b9730fSStrahinja Jankovic     .instance_init = allwinner_wdt_init,
391*17b9730fSStrahinja Jankovic     .instance_size = sizeof(AwWdtState),
392*17b9730fSStrahinja Jankovic     .class_init    = allwinner_wdt_class_init,
393*17b9730fSStrahinja Jankovic     .class_size    = sizeof(AwWdtClass),
394*17b9730fSStrahinja Jankovic     .abstract      = true,
395*17b9730fSStrahinja Jankovic };
396*17b9730fSStrahinja Jankovic 
397*17b9730fSStrahinja Jankovic static const TypeInfo allwinner_wdt_sun4i_info = {
398*17b9730fSStrahinja Jankovic     .name          = TYPE_AW_WDT_SUN4I,
399*17b9730fSStrahinja Jankovic     .parent        = TYPE_AW_WDT,
400*17b9730fSStrahinja Jankovic     .class_init    = allwinner_wdt_sun4i_class_init,
401*17b9730fSStrahinja Jankovic };
402*17b9730fSStrahinja Jankovic 
403*17b9730fSStrahinja Jankovic static const TypeInfo allwinner_wdt_sun6i_info = {
404*17b9730fSStrahinja Jankovic     .name          = TYPE_AW_WDT_SUN6I,
405*17b9730fSStrahinja Jankovic     .parent        = TYPE_AW_WDT,
406*17b9730fSStrahinja Jankovic     .class_init    = allwinner_wdt_sun6i_class_init,
407*17b9730fSStrahinja Jankovic };
408*17b9730fSStrahinja Jankovic 
allwinner_wdt_register(void)409*17b9730fSStrahinja Jankovic static void allwinner_wdt_register(void)
410*17b9730fSStrahinja Jankovic {
411*17b9730fSStrahinja Jankovic     type_register_static(&allwinner_wdt_info);
412*17b9730fSStrahinja Jankovic     type_register_static(&allwinner_wdt_sun4i_info);
413*17b9730fSStrahinja Jankovic     type_register_static(&allwinner_wdt_sun6i_info);
414*17b9730fSStrahinja Jankovic }
415*17b9730fSStrahinja Jankovic 
416*17b9730fSStrahinja Jankovic type_init(allwinner_wdt_register)
417