1 // SPDX-License-Identifier: GPL-2.0+
3 * Derived from linux/drivers/watchdog/sunxi_wdt.c:
4 * Copyright (C) 2013 Carlo Caione
5 * Copyright (C) 2012 Henrik Nordstrom
11 #include <linux/delay.h>
13 #define MSEC_PER_SEC 1000
15 #define WDT_MAX_TIMEOUT 16
16 #define WDT_TIMEOUT_MASK 0xf
18 #define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1))
20 #define WDT_MODE_EN BIT(0)
22 struct sunxi_wdt_reg {
32 struct sunxi_wdt_priv {
34 const struct sunxi_wdt_reg *regs;
37 /* Map of timeout in seconds to register value */
38 static const u8 wdt_timeout_map[1 + WDT_MAX_TIMEOUT] = {
58 static int sunxi_wdt_reset(struct udevice *dev)
60 struct sunxi_wdt_priv *priv = dev_get_priv(dev);
61 const struct sunxi_wdt_reg *regs = priv->regs;
62 void __iomem *base = priv->base;
64 writel(WDT_CTRL_RELOAD, base + regs->wdt_ctrl);
69 static int sunxi_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
71 struct sunxi_wdt_priv *priv = dev_get_priv(dev);
72 const struct sunxi_wdt_reg *regs = priv->regs;
73 void __iomem *base = priv->base;
76 timeout /= MSEC_PER_SEC;
77 if (timeout > WDT_MAX_TIMEOUT)
78 timeout = WDT_MAX_TIMEOUT;
80 /* Set system reset function */
81 val = readl(base + regs->wdt_cfg);
82 val &= ~regs->wdt_reset_mask;
83 val |= regs->wdt_reset_val;
84 val |= regs->wdt_key_val;
85 writel(val, base + regs->wdt_cfg);
87 /* Set timeout and enable watchdog */
88 val = readl(base + regs->wdt_mode);
89 val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
90 val |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
92 val |= regs->wdt_key_val;
93 writel(val, base + regs->wdt_mode);
95 return sunxi_wdt_reset(dev);
98 static int sunxi_wdt_stop(struct udevice *dev)
100 struct sunxi_wdt_priv *priv = dev_get_priv(dev);
101 const struct sunxi_wdt_reg *regs = priv->regs;
102 void __iomem *base = priv->base;
104 writel(regs->wdt_key_val, base + regs->wdt_mode);
109 static int sunxi_wdt_expire_now(struct udevice *dev, ulong flags)
113 ret = sunxi_wdt_start(dev, 0, flags);
122 static const struct wdt_ops sunxi_wdt_ops = {
123 .reset = sunxi_wdt_reset,
124 .start = sunxi_wdt_start,
125 .stop = sunxi_wdt_stop,
126 .expire_now = sunxi_wdt_expire_now,
129 static const struct sunxi_wdt_reg sun4i_wdt_reg = {
133 .wdt_timeout_shift = 3,
134 .wdt_reset_mask = 0x2,
135 .wdt_reset_val = 0x2,
138 static const struct sunxi_wdt_reg sun6i_wdt_reg = {
142 .wdt_timeout_shift = 4,
143 .wdt_reset_mask = 0x3,
144 .wdt_reset_val = 0x1,
147 static const struct sunxi_wdt_reg sun20i_wdt_reg = {
151 .wdt_timeout_shift = 4,
152 .wdt_reset_mask = 0x03,
153 .wdt_reset_val = 0x01,
154 .wdt_key_val = 0x16aa0000,
157 static const struct udevice_id sunxi_wdt_ids[] = {
158 { .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg },
159 { .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg },
160 { .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg },
164 static int sunxi_wdt_probe(struct udevice *dev)
166 struct sunxi_wdt_priv *priv = dev_get_priv(dev);
168 priv->base = dev_remap_addr(dev);
172 priv->regs = (void *)dev_get_driver_data(dev);
181 U_BOOT_DRIVER(sunxi_wdt) = {
184 .of_match = sunxi_wdt_ids,
185 .probe = sunxi_wdt_probe,
186 .priv_auto = sizeof(struct sunxi_wdt_priv),
187 .ops = &sunxi_wdt_ops,