xref: /openbmc/qemu/hw/watchdog/allwinner-wdt.c (revision d328fef93ae757a0dd65ed786a4086e27952eef3)
117b9730fSStrahinja Jankovic /*
217b9730fSStrahinja Jankovic  * Allwinner Watchdog emulation
317b9730fSStrahinja Jankovic  *
417b9730fSStrahinja Jankovic  * Copyright (C) 2023 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
517b9730fSStrahinja Jankovic  *
617b9730fSStrahinja Jankovic  *  This file is derived from Allwinner RTC,
717b9730fSStrahinja Jankovic  *  by Niek Linnenbank.
817b9730fSStrahinja Jankovic  *
917b9730fSStrahinja Jankovic  * This program is free software: you can redistribute it and/or modify
1017b9730fSStrahinja Jankovic  * it under the terms of the GNU General Public License as published by
1117b9730fSStrahinja Jankovic  * the Free Software Foundation, either version 2 of the License, or
1217b9730fSStrahinja Jankovic  * (at your option) any later version.
1317b9730fSStrahinja Jankovic  *
1417b9730fSStrahinja Jankovic  * This program is distributed in the hope that it will be useful,
1517b9730fSStrahinja Jankovic  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1617b9730fSStrahinja Jankovic  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1717b9730fSStrahinja Jankovic  * GNU General Public License for more details.
1817b9730fSStrahinja Jankovic  *
1917b9730fSStrahinja Jankovic  * You should have received a copy of the GNU General Public License
2017b9730fSStrahinja Jankovic  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
2117b9730fSStrahinja Jankovic  */
2217b9730fSStrahinja Jankovic 
2317b9730fSStrahinja Jankovic #include "qemu/osdep.h"
2417b9730fSStrahinja Jankovic #include "qemu/log.h"
2517b9730fSStrahinja Jankovic #include "qemu/units.h"
2617b9730fSStrahinja Jankovic #include "qemu/module.h"
2717b9730fSStrahinja Jankovic #include "trace.h"
2817b9730fSStrahinja Jankovic #include "hw/sysbus.h"
2917b9730fSStrahinja Jankovic #include "hw/registerfields.h"
3017b9730fSStrahinja Jankovic #include "hw/watchdog/allwinner-wdt.h"
3117b9730fSStrahinja Jankovic #include "sysemu/watchdog.h"
3217b9730fSStrahinja Jankovic #include "migration/vmstate.h"
3317b9730fSStrahinja Jankovic 
3417b9730fSStrahinja Jankovic /* WDT registers */
3517b9730fSStrahinja Jankovic enum {
3617b9730fSStrahinja Jankovic     REG_IRQ_EN = 0,     /* Watchdog interrupt enable */
3717b9730fSStrahinja Jankovic     REG_IRQ_STA,        /* Watchdog interrupt status */
3817b9730fSStrahinja Jankovic     REG_CTRL,           /* Watchdog control register */
3917b9730fSStrahinja Jankovic     REG_CFG,            /* Watchdog configuration register */
4017b9730fSStrahinja Jankovic     REG_MODE,           /* Watchdog mode register */
4117b9730fSStrahinja Jankovic };
4217b9730fSStrahinja Jankovic 
4317b9730fSStrahinja Jankovic /* Universal WDT register flags */
4417b9730fSStrahinja Jankovic #define WDT_RESTART_MASK    (1 << 0)
4517b9730fSStrahinja Jankovic #define WDT_EN_MASK         (1 << 0)
4617b9730fSStrahinja Jankovic 
4717b9730fSStrahinja Jankovic /* sun4i specific WDT register flags */
4817b9730fSStrahinja Jankovic #define RST_EN_SUN4I_MASK       (1 << 1)
4917b9730fSStrahinja Jankovic #define INTV_VALUE_SUN4I_SHIFT  (3)
5017b9730fSStrahinja Jankovic #define INTV_VALUE_SUN4I_MASK   (0xfu << INTV_VALUE_SUN4I_SHIFT)
5117b9730fSStrahinja Jankovic 
5217b9730fSStrahinja Jankovic /* sun6i specific WDT register flags */
5317b9730fSStrahinja Jankovic #define RST_EN_SUN6I_MASK       (1 << 0)
5417b9730fSStrahinja Jankovic #define KEY_FIELD_SUN6I_SHIFT   (1)
5517b9730fSStrahinja Jankovic #define KEY_FIELD_SUN6I_MASK    (0xfffu << KEY_FIELD_SUN6I_SHIFT)
5617b9730fSStrahinja Jankovic #define KEY_FIELD_SUN6I         (0xA57u)
5717b9730fSStrahinja Jankovic #define INTV_VALUE_SUN6I_SHIFT  (4)
5817b9730fSStrahinja Jankovic #define INTV_VALUE_SUN6I_MASK   (0xfu << INTV_VALUE_SUN6I_SHIFT)
5917b9730fSStrahinja Jankovic 
6017b9730fSStrahinja Jankovic /* Map of INTV_VALUE to 0.5s units. */
6117b9730fSStrahinja Jankovic static const uint8_t allwinner_wdt_count_map[] = {
6217b9730fSStrahinja Jankovic     1,
6317b9730fSStrahinja Jankovic     2,
6417b9730fSStrahinja Jankovic     4,
6517b9730fSStrahinja Jankovic     6,
6617b9730fSStrahinja Jankovic     8,
6717b9730fSStrahinja Jankovic     10,
6817b9730fSStrahinja Jankovic     12,
6917b9730fSStrahinja Jankovic     16,
7017b9730fSStrahinja Jankovic     20,
7117b9730fSStrahinja Jankovic     24,
7217b9730fSStrahinja Jankovic     28,
7317b9730fSStrahinja Jankovic     32
7417b9730fSStrahinja Jankovic };
7517b9730fSStrahinja Jankovic 
7617b9730fSStrahinja Jankovic /* WDT sun4i register map (offset to name) */
7717b9730fSStrahinja Jankovic const uint8_t allwinner_wdt_sun4i_regmap[] = {
7817b9730fSStrahinja Jankovic     [0x0000] = REG_CTRL,
7917b9730fSStrahinja Jankovic     [0x0004] = REG_MODE,
8017b9730fSStrahinja Jankovic };
8117b9730fSStrahinja Jankovic 
8217b9730fSStrahinja Jankovic /* WDT sun6i register map (offset to name) */
8317b9730fSStrahinja Jankovic const uint8_t allwinner_wdt_sun6i_regmap[] = {
8417b9730fSStrahinja Jankovic     [0x0000] = REG_IRQ_EN,
8517b9730fSStrahinja Jankovic     [0x0004] = REG_IRQ_STA,
8617b9730fSStrahinja Jankovic     [0x0010] = REG_CTRL,
8717b9730fSStrahinja Jankovic     [0x0014] = REG_CFG,
8817b9730fSStrahinja Jankovic     [0x0018] = REG_MODE,
8917b9730fSStrahinja Jankovic };
9017b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_read(AwWdtState * s,uint32_t offset)9117b9730fSStrahinja Jankovic static bool allwinner_wdt_sun4i_read(AwWdtState *s, uint32_t offset)
9217b9730fSStrahinja Jankovic {
9317b9730fSStrahinja Jankovic     /* no sun4i specific registers currently implemented */
9417b9730fSStrahinja Jankovic     return false;
9517b9730fSStrahinja Jankovic }
9617b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_write(AwWdtState * s,uint32_t offset,uint32_t data)9717b9730fSStrahinja Jankovic static bool allwinner_wdt_sun4i_write(AwWdtState *s, uint32_t offset,
9817b9730fSStrahinja Jankovic                                       uint32_t data)
9917b9730fSStrahinja Jankovic {
10017b9730fSStrahinja Jankovic     /* no sun4i specific registers currently implemented */
10117b9730fSStrahinja Jankovic     return false;
10217b9730fSStrahinja Jankovic }
10317b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_can_reset_system(AwWdtState * s)10417b9730fSStrahinja Jankovic static bool allwinner_wdt_sun4i_can_reset_system(AwWdtState *s)
10517b9730fSStrahinja Jankovic {
10617b9730fSStrahinja Jankovic     if (s->regs[REG_MODE] & RST_EN_SUN4I_MASK) {
10717b9730fSStrahinja Jankovic         return true;
10817b9730fSStrahinja Jankovic     } else {
10917b9730fSStrahinja Jankovic         return false;
11017b9730fSStrahinja Jankovic     }
11117b9730fSStrahinja Jankovic }
11217b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_is_key_valid(AwWdtState * s,uint32_t val)11317b9730fSStrahinja Jankovic static bool allwinner_wdt_sun4i_is_key_valid(AwWdtState *s, uint32_t val)
11417b9730fSStrahinja Jankovic {
11517b9730fSStrahinja Jankovic     /* sun4i has no key */
11617b9730fSStrahinja Jankovic     return true;
11717b9730fSStrahinja Jankovic }
11817b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_get_intv_value(AwWdtState * s)11917b9730fSStrahinja Jankovic static uint8_t allwinner_wdt_sun4i_get_intv_value(AwWdtState *s)
12017b9730fSStrahinja Jankovic {
12117b9730fSStrahinja Jankovic     return ((s->regs[REG_MODE] & INTV_VALUE_SUN4I_MASK) >>
12217b9730fSStrahinja Jankovic             INTV_VALUE_SUN4I_SHIFT);
12317b9730fSStrahinja Jankovic }
12417b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_read(AwWdtState * s,uint32_t offset)12517b9730fSStrahinja Jankovic static bool allwinner_wdt_sun6i_read(AwWdtState *s, uint32_t offset)
12617b9730fSStrahinja Jankovic {
12717b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
12817b9730fSStrahinja Jankovic 
12917b9730fSStrahinja Jankovic     switch (c->regmap[offset]) {
13017b9730fSStrahinja Jankovic     case REG_IRQ_EN:
13117b9730fSStrahinja Jankovic     case REG_IRQ_STA:
13217b9730fSStrahinja Jankovic     case REG_CFG:
13317b9730fSStrahinja Jankovic         return true;
13417b9730fSStrahinja Jankovic     default:
13517b9730fSStrahinja Jankovic         break;
13617b9730fSStrahinja Jankovic     }
13717b9730fSStrahinja Jankovic     return false;
13817b9730fSStrahinja Jankovic }
13917b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_write(AwWdtState * s,uint32_t offset,uint32_t data)14017b9730fSStrahinja Jankovic static bool allwinner_wdt_sun6i_write(AwWdtState *s, uint32_t offset,
14117b9730fSStrahinja Jankovic                                       uint32_t data)
14217b9730fSStrahinja Jankovic {
14317b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
14417b9730fSStrahinja Jankovic 
14517b9730fSStrahinja Jankovic     switch (c->regmap[offset]) {
14617b9730fSStrahinja Jankovic     case REG_IRQ_EN:
14717b9730fSStrahinja Jankovic     case REG_IRQ_STA:
14817b9730fSStrahinja Jankovic     case REG_CFG:
14917b9730fSStrahinja Jankovic         return true;
15017b9730fSStrahinja Jankovic     default:
15117b9730fSStrahinja Jankovic         break;
15217b9730fSStrahinja Jankovic     }
15317b9730fSStrahinja Jankovic     return false;
15417b9730fSStrahinja Jankovic }
15517b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_can_reset_system(AwWdtState * s)15617b9730fSStrahinja Jankovic static bool allwinner_wdt_sun6i_can_reset_system(AwWdtState *s)
15717b9730fSStrahinja Jankovic {
15817b9730fSStrahinja Jankovic     if (s->regs[REG_CFG] & RST_EN_SUN6I_MASK) {
15917b9730fSStrahinja Jankovic         return true;
16017b9730fSStrahinja Jankovic     } else {
16117b9730fSStrahinja Jankovic         return false;
16217b9730fSStrahinja Jankovic     }
16317b9730fSStrahinja Jankovic }
16417b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_is_key_valid(AwWdtState * s,uint32_t val)16517b9730fSStrahinja Jankovic static bool allwinner_wdt_sun6i_is_key_valid(AwWdtState *s, uint32_t val)
16617b9730fSStrahinja Jankovic {
16717b9730fSStrahinja Jankovic     uint16_t key = (val & KEY_FIELD_SUN6I_MASK) >> KEY_FIELD_SUN6I_SHIFT;
16817b9730fSStrahinja Jankovic     return (key == KEY_FIELD_SUN6I);
16917b9730fSStrahinja Jankovic }
17017b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_get_intv_value(AwWdtState * s)17117b9730fSStrahinja Jankovic static uint8_t allwinner_wdt_sun6i_get_intv_value(AwWdtState *s)
17217b9730fSStrahinja Jankovic {
17317b9730fSStrahinja Jankovic     return ((s->regs[REG_MODE] & INTV_VALUE_SUN6I_MASK) >>
17417b9730fSStrahinja Jankovic             INTV_VALUE_SUN6I_SHIFT);
17517b9730fSStrahinja Jankovic }
17617b9730fSStrahinja Jankovic 
allwinner_wdt_update_timer(AwWdtState * s)17717b9730fSStrahinja Jankovic static void allwinner_wdt_update_timer(AwWdtState *s)
17817b9730fSStrahinja Jankovic {
17917b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
18017b9730fSStrahinja Jankovic     uint8_t count = c->get_intv_value(s);
18117b9730fSStrahinja Jankovic 
18217b9730fSStrahinja Jankovic     ptimer_transaction_begin(s->timer);
18317b9730fSStrahinja Jankovic     ptimer_stop(s->timer);
18417b9730fSStrahinja Jankovic 
18517b9730fSStrahinja Jankovic     /* Use map to convert. */
18617b9730fSStrahinja Jankovic     if (count < sizeof(allwinner_wdt_count_map)) {
18717b9730fSStrahinja Jankovic         ptimer_set_count(s->timer, allwinner_wdt_count_map[count]);
18817b9730fSStrahinja Jankovic     } else {
18917b9730fSStrahinja Jankovic         qemu_log_mask(LOG_GUEST_ERROR, "%s: incorrect INTV_VALUE 0x%02x\n",
19017b9730fSStrahinja Jankovic                 __func__, count);
19117b9730fSStrahinja Jankovic     }
19217b9730fSStrahinja Jankovic 
19317b9730fSStrahinja Jankovic     ptimer_run(s->timer, 1);
19417b9730fSStrahinja Jankovic     ptimer_transaction_commit(s->timer);
19517b9730fSStrahinja Jankovic 
19617b9730fSStrahinja Jankovic     trace_allwinner_wdt_update_timer(count);
19717b9730fSStrahinja Jankovic }
19817b9730fSStrahinja Jankovic 
allwinner_wdt_read(void * opaque,hwaddr offset,unsigned size)19917b9730fSStrahinja Jankovic static uint64_t allwinner_wdt_read(void *opaque, hwaddr offset,
20017b9730fSStrahinja Jankovic                                        unsigned size)
20117b9730fSStrahinja Jankovic {
20217b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(opaque);
20317b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
20417b9730fSStrahinja Jankovic     uint64_t r;
20517b9730fSStrahinja Jankovic 
20617b9730fSStrahinja Jankovic     if (offset >= c->regmap_size) {
20717b9730fSStrahinja Jankovic         qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
20817b9730fSStrahinja Jankovic                       __func__, (uint32_t)offset);
20917b9730fSStrahinja Jankovic         return 0;
21017b9730fSStrahinja Jankovic     }
21117b9730fSStrahinja Jankovic 
21217b9730fSStrahinja Jankovic     switch (c->regmap[offset]) {
21317b9730fSStrahinja Jankovic     case REG_CTRL:
21417b9730fSStrahinja Jankovic     case REG_MODE:
21517b9730fSStrahinja Jankovic         r = s->regs[c->regmap[offset]];
21617b9730fSStrahinja Jankovic         break;
21717b9730fSStrahinja Jankovic     default:
21817b9730fSStrahinja Jankovic         if (!c->read(s, offset)) {
21917b9730fSStrahinja Jankovic             qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n",
22017b9730fSStrahinja Jankovic                             __func__, (uint32_t)offset);
22117b9730fSStrahinja Jankovic             return 0;
22217b9730fSStrahinja Jankovic         }
22317b9730fSStrahinja Jankovic         r = s->regs[c->regmap[offset]];
22417b9730fSStrahinja Jankovic         break;
22517b9730fSStrahinja Jankovic     }
22617b9730fSStrahinja Jankovic 
22717b9730fSStrahinja Jankovic     trace_allwinner_wdt_read(offset, r, size);
22817b9730fSStrahinja Jankovic 
22917b9730fSStrahinja Jankovic     return r;
23017b9730fSStrahinja Jankovic }
23117b9730fSStrahinja Jankovic 
allwinner_wdt_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)23217b9730fSStrahinja Jankovic static void allwinner_wdt_write(void *opaque, hwaddr offset,
23317b9730fSStrahinja Jankovic                                    uint64_t val, unsigned size)
23417b9730fSStrahinja Jankovic {
23517b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(opaque);
23617b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
23717b9730fSStrahinja Jankovic     uint32_t old_val;
23817b9730fSStrahinja Jankovic 
23917b9730fSStrahinja Jankovic     if (offset >= c->regmap_size) {
24017b9730fSStrahinja Jankovic         qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
24117b9730fSStrahinja Jankovic                       __func__, (uint32_t)offset);
24217b9730fSStrahinja Jankovic         return;
24317b9730fSStrahinja Jankovic     }
24417b9730fSStrahinja Jankovic 
24517b9730fSStrahinja Jankovic    trace_allwinner_wdt_write(offset, val, size);
24617b9730fSStrahinja Jankovic 
24717b9730fSStrahinja Jankovic     switch (c->regmap[offset]) {
24817b9730fSStrahinja Jankovic     case REG_CTRL:
24917b9730fSStrahinja Jankovic         if (c->is_key_valid(s, val)) {
25017b9730fSStrahinja Jankovic             if (val & WDT_RESTART_MASK) {
25117b9730fSStrahinja Jankovic                 /* Kick timer */
25217b9730fSStrahinja Jankovic                 allwinner_wdt_update_timer(s);
25317b9730fSStrahinja Jankovic             }
25417b9730fSStrahinja Jankovic         }
25517b9730fSStrahinja Jankovic         break;
25617b9730fSStrahinja Jankovic     case REG_MODE:
25717b9730fSStrahinja Jankovic         old_val = s->regs[REG_MODE];
25817b9730fSStrahinja Jankovic         s->regs[REG_MODE] = (uint32_t)val;
25917b9730fSStrahinja Jankovic 
26017b9730fSStrahinja Jankovic         /* Check for rising edge on WDOG_MODE_EN */
26117b9730fSStrahinja Jankovic         if ((s->regs[REG_MODE] & ~old_val) & WDT_EN_MASK) {
26217b9730fSStrahinja Jankovic             allwinner_wdt_update_timer(s);
26317b9730fSStrahinja Jankovic         }
26417b9730fSStrahinja Jankovic         break;
26517b9730fSStrahinja Jankovic     default:
26617b9730fSStrahinja Jankovic         if (!c->write(s, offset, val)) {
26717b9730fSStrahinja Jankovic             qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n",
26817b9730fSStrahinja Jankovic                           __func__, (uint32_t)offset);
26917b9730fSStrahinja Jankovic         }
27017b9730fSStrahinja Jankovic         s->regs[c->regmap[offset]] = (uint32_t)val;
27117b9730fSStrahinja Jankovic         break;
27217b9730fSStrahinja Jankovic     }
27317b9730fSStrahinja Jankovic }
27417b9730fSStrahinja Jankovic 
27517b9730fSStrahinja Jankovic static const MemoryRegionOps allwinner_wdt_ops = {
27617b9730fSStrahinja Jankovic     .read = allwinner_wdt_read,
27717b9730fSStrahinja Jankovic     .write = allwinner_wdt_write,
27817b9730fSStrahinja Jankovic     .endianness = DEVICE_NATIVE_ENDIAN,
27917b9730fSStrahinja Jankovic     .valid = {
28017b9730fSStrahinja Jankovic         .min_access_size = 4,
28117b9730fSStrahinja Jankovic         .max_access_size = 4,
28217b9730fSStrahinja Jankovic     },
28317b9730fSStrahinja Jankovic     .impl.min_access_size = 4,
28417b9730fSStrahinja Jankovic };
28517b9730fSStrahinja Jankovic 
allwinner_wdt_expired(void * opaque)28617b9730fSStrahinja Jankovic static void allwinner_wdt_expired(void *opaque)
28717b9730fSStrahinja Jankovic {
28817b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(opaque);
28917b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
29017b9730fSStrahinja Jankovic 
29117b9730fSStrahinja Jankovic     bool enabled = s->regs[REG_MODE] & WDT_EN_MASK;
29217b9730fSStrahinja Jankovic     bool reset_enabled = c->can_reset_system(s);
29317b9730fSStrahinja Jankovic 
29417b9730fSStrahinja Jankovic     trace_allwinner_wdt_expired(enabled, reset_enabled);
29517b9730fSStrahinja Jankovic 
29617b9730fSStrahinja Jankovic     /* Perform watchdog action if watchdog is enabled and can trigger reset */
29717b9730fSStrahinja Jankovic     if (enabled && reset_enabled) {
29817b9730fSStrahinja Jankovic         watchdog_perform_action();
29917b9730fSStrahinja Jankovic     }
30017b9730fSStrahinja Jankovic }
30117b9730fSStrahinja Jankovic 
allwinner_wdt_reset_enter(Object * obj,ResetType type)30217b9730fSStrahinja Jankovic static void allwinner_wdt_reset_enter(Object *obj, ResetType type)
30317b9730fSStrahinja Jankovic {
30417b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(obj);
30517b9730fSStrahinja Jankovic 
30617b9730fSStrahinja Jankovic     trace_allwinner_wdt_reset_enter();
30717b9730fSStrahinja Jankovic 
30817b9730fSStrahinja Jankovic     /* Clear registers */
30917b9730fSStrahinja Jankovic     memset(s->regs, 0, sizeof(s->regs));
31017b9730fSStrahinja Jankovic }
31117b9730fSStrahinja Jankovic 
31217b9730fSStrahinja Jankovic static const VMStateDescription allwinner_wdt_vmstate = {
31317b9730fSStrahinja Jankovic     .name = "allwinner-wdt",
31417b9730fSStrahinja Jankovic     .version_id = 1,
31517b9730fSStrahinja Jankovic     .minimum_version_id = 1,
316*45bc669eSRichard Henderson     .fields = (const VMStateField[]) {
31717b9730fSStrahinja Jankovic         VMSTATE_PTIMER(timer, AwWdtState),
31817b9730fSStrahinja Jankovic         VMSTATE_UINT32_ARRAY(regs, AwWdtState, AW_WDT_REGS_NUM),
31917b9730fSStrahinja Jankovic         VMSTATE_END_OF_LIST()
32017b9730fSStrahinja Jankovic     }
32117b9730fSStrahinja Jankovic };
32217b9730fSStrahinja Jankovic 
allwinner_wdt_init(Object * obj)32317b9730fSStrahinja Jankovic static void allwinner_wdt_init(Object *obj)
32417b9730fSStrahinja Jankovic {
32517b9730fSStrahinja Jankovic     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
32617b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(obj);
32717b9730fSStrahinja Jankovic     const AwWdtClass *c = AW_WDT_GET_CLASS(s);
32817b9730fSStrahinja Jankovic 
32917b9730fSStrahinja Jankovic     /* Memory mapping */
33017b9730fSStrahinja Jankovic     memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_wdt_ops, s,
33117b9730fSStrahinja Jankovic                           TYPE_AW_WDT, c->regmap_size * 4);
33217b9730fSStrahinja Jankovic     sysbus_init_mmio(sbd, &s->iomem);
33317b9730fSStrahinja Jankovic }
33417b9730fSStrahinja Jankovic 
allwinner_wdt_realize(DeviceState * dev,Error ** errp)33517b9730fSStrahinja Jankovic static void allwinner_wdt_realize(DeviceState *dev, Error **errp)
33617b9730fSStrahinja Jankovic {
33717b9730fSStrahinja Jankovic     AwWdtState *s = AW_WDT(dev);
33817b9730fSStrahinja Jankovic 
33917b9730fSStrahinja Jankovic     s->timer = ptimer_init(allwinner_wdt_expired, s,
34017b9730fSStrahinja Jankovic                            PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
34117b9730fSStrahinja Jankovic                            PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
34217b9730fSStrahinja Jankovic                            PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
34317b9730fSStrahinja Jankovic 
34417b9730fSStrahinja Jankovic     ptimer_transaction_begin(s->timer);
34517b9730fSStrahinja Jankovic     /* Set to 2Hz (0.5s period); other periods are multiples of 0.5s. */
34617b9730fSStrahinja Jankovic     ptimer_set_freq(s->timer, 2);
34717b9730fSStrahinja Jankovic     ptimer_set_limit(s->timer, 0xff, 1);
34817b9730fSStrahinja Jankovic     ptimer_transaction_commit(s->timer);
34917b9730fSStrahinja Jankovic }
35017b9730fSStrahinja Jankovic 
allwinner_wdt_class_init(ObjectClass * klass,void * data)35117b9730fSStrahinja Jankovic static void allwinner_wdt_class_init(ObjectClass *klass, void *data)
35217b9730fSStrahinja Jankovic {
35317b9730fSStrahinja Jankovic     DeviceClass *dc = DEVICE_CLASS(klass);
35417b9730fSStrahinja Jankovic     ResettableClass *rc = RESETTABLE_CLASS(klass);
35517b9730fSStrahinja Jankovic 
35617b9730fSStrahinja Jankovic     rc->phases.enter = allwinner_wdt_reset_enter;
35717b9730fSStrahinja Jankovic     dc->realize = allwinner_wdt_realize;
35817b9730fSStrahinja Jankovic     dc->vmsd = &allwinner_wdt_vmstate;
35917b9730fSStrahinja Jankovic }
36017b9730fSStrahinja Jankovic 
allwinner_wdt_sun4i_class_init(ObjectClass * klass,void * data)36117b9730fSStrahinja Jankovic static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, void *data)
36217b9730fSStrahinja Jankovic {
36317b9730fSStrahinja Jankovic     AwWdtClass *awc = AW_WDT_CLASS(klass);
36417b9730fSStrahinja Jankovic 
36517b9730fSStrahinja Jankovic     awc->regmap = allwinner_wdt_sun4i_regmap;
36617b9730fSStrahinja Jankovic     awc->regmap_size = sizeof(allwinner_wdt_sun4i_regmap);
36717b9730fSStrahinja Jankovic     awc->read = allwinner_wdt_sun4i_read;
36817b9730fSStrahinja Jankovic     awc->write = allwinner_wdt_sun4i_write;
36917b9730fSStrahinja Jankovic     awc->can_reset_system = allwinner_wdt_sun4i_can_reset_system;
37017b9730fSStrahinja Jankovic     awc->is_key_valid = allwinner_wdt_sun4i_is_key_valid;
37117b9730fSStrahinja Jankovic     awc->get_intv_value = allwinner_wdt_sun4i_get_intv_value;
37217b9730fSStrahinja Jankovic }
37317b9730fSStrahinja Jankovic 
allwinner_wdt_sun6i_class_init(ObjectClass * klass,void * data)37417b9730fSStrahinja Jankovic static void allwinner_wdt_sun6i_class_init(ObjectClass *klass, void *data)
37517b9730fSStrahinja Jankovic {
37617b9730fSStrahinja Jankovic     AwWdtClass *awc = AW_WDT_CLASS(klass);
37717b9730fSStrahinja Jankovic 
37817b9730fSStrahinja Jankovic     awc->regmap = allwinner_wdt_sun6i_regmap;
37917b9730fSStrahinja Jankovic     awc->regmap_size = sizeof(allwinner_wdt_sun6i_regmap);
38017b9730fSStrahinja Jankovic     awc->read = allwinner_wdt_sun6i_read;
38117b9730fSStrahinja Jankovic     awc->write = allwinner_wdt_sun6i_write;
38217b9730fSStrahinja Jankovic     awc->can_reset_system = allwinner_wdt_sun6i_can_reset_system;
38317b9730fSStrahinja Jankovic     awc->is_key_valid = allwinner_wdt_sun6i_is_key_valid;
38417b9730fSStrahinja Jankovic     awc->get_intv_value = allwinner_wdt_sun6i_get_intv_value;
38517b9730fSStrahinja Jankovic }
38617b9730fSStrahinja Jankovic 
38717b9730fSStrahinja Jankovic static const TypeInfo allwinner_wdt_info = {
38817b9730fSStrahinja Jankovic     .name          = TYPE_AW_WDT,
38917b9730fSStrahinja Jankovic     .parent        = TYPE_SYS_BUS_DEVICE,
39017b9730fSStrahinja Jankovic     .instance_init = allwinner_wdt_init,
39117b9730fSStrahinja Jankovic     .instance_size = sizeof(AwWdtState),
39217b9730fSStrahinja Jankovic     .class_init    = allwinner_wdt_class_init,
39317b9730fSStrahinja Jankovic     .class_size    = sizeof(AwWdtClass),
39417b9730fSStrahinja Jankovic     .abstract      = true,
39517b9730fSStrahinja Jankovic };
39617b9730fSStrahinja Jankovic 
39717b9730fSStrahinja Jankovic static const TypeInfo allwinner_wdt_sun4i_info = {
39817b9730fSStrahinja Jankovic     .name          = TYPE_AW_WDT_SUN4I,
39917b9730fSStrahinja Jankovic     .parent        = TYPE_AW_WDT,
40017b9730fSStrahinja Jankovic     .class_init    = allwinner_wdt_sun4i_class_init,
40117b9730fSStrahinja Jankovic };
40217b9730fSStrahinja Jankovic 
40317b9730fSStrahinja Jankovic static const TypeInfo allwinner_wdt_sun6i_info = {
40417b9730fSStrahinja Jankovic     .name          = TYPE_AW_WDT_SUN6I,
40517b9730fSStrahinja Jankovic     .parent        = TYPE_AW_WDT,
40617b9730fSStrahinja Jankovic     .class_init    = allwinner_wdt_sun6i_class_init,
40717b9730fSStrahinja Jankovic };
40817b9730fSStrahinja Jankovic 
allwinner_wdt_register(void)40917b9730fSStrahinja Jankovic static void allwinner_wdt_register(void)
41017b9730fSStrahinja Jankovic {
41117b9730fSStrahinja Jankovic     type_register_static(&allwinner_wdt_info);
41217b9730fSStrahinja Jankovic     type_register_static(&allwinner_wdt_sun4i_info);
41317b9730fSStrahinja Jankovic     type_register_static(&allwinner_wdt_sun6i_info);
41417b9730fSStrahinja Jankovic }
41517b9730fSStrahinja Jankovic 
41617b9730fSStrahinja Jankovic type_init(allwinner_wdt_register)
417