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