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