watchdog: Add a driver for the sunxi watchdog
This driver supports the sun4i/sun6i/sun20i watchdog timers. They have a maximum timeout of 16 seconds. Signed-off-by: Samuel Holland <samuel@sholland.org> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
This commit is contained in:
parent
e0c628d728
commit
93d34faeda
3 changed files with 197 additions and 0 deletions
|
@ -27,6 +27,7 @@ config WATCHDOG_TIMEOUT_MSECS
|
|||
default 128000 if ARCH_MX31 || ARCH_MX5 || ARCH_MX6
|
||||
default 128000 if ARCH_MX7 || ARCH_VF610
|
||||
default 30000 if ARCH_SOCFPGA
|
||||
default 16000 if ARCH_SUNXI
|
||||
default 60000
|
||||
help
|
||||
Watchdog timeout in msec
|
||||
|
@ -270,6 +271,13 @@ config WDT_STM32MP
|
|||
Enable the STM32 watchdog (IWDG) driver. Enable support to
|
||||
configure STM32's on-SoC watchdog.
|
||||
|
||||
config WDT_SUNXI
|
||||
bool "Allwinner sunxi watchdog timer support"
|
||||
depends on WDT && ARCH_SUNXI
|
||||
default y
|
||||
help
|
||||
Enable support for the watchdog timer in Allwinner sunxi SoCs.
|
||||
|
||||
config XILINX_TB_WATCHDOG
|
||||
bool "Xilinx Axi watchdog timer support"
|
||||
depends on WDT
|
||||
|
|
|
@ -36,5 +36,6 @@ obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o
|
|||
obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o
|
||||
obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
|
||||
obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
|
||||
obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o
|
||||
obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
|
||||
obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o
|
||||
|
|
188
drivers/watchdog/sunxi_wdt.c
Normal file
188
drivers/watchdog/sunxi_wdt.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Derived from linux/drivers/watchdog/sunxi_wdt.c:
|
||||
* Copyright (C) 2013 Carlo Caione
|
||||
* Copyright (C) 2012 Henrik Nordstrom
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <wdt.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define MSEC_PER_SEC 1000
|
||||
|
||||
#define WDT_MAX_TIMEOUT 16
|
||||
#define WDT_TIMEOUT_MASK 0xf
|
||||
|
||||
#define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1))
|
||||
|
||||
#define WDT_MODE_EN BIT(0)
|
||||
|
||||
struct sunxi_wdt_reg {
|
||||
u8 wdt_ctrl;
|
||||
u8 wdt_cfg;
|
||||
u8 wdt_mode;
|
||||
u8 wdt_timeout_shift;
|
||||
u8 wdt_reset_mask;
|
||||
u8 wdt_reset_val;
|
||||
u32 wdt_key_val;
|
||||
};
|
||||
|
||||
struct sunxi_wdt_priv {
|
||||
void __iomem *base;
|
||||
const struct sunxi_wdt_reg *regs;
|
||||
};
|
||||
|
||||
/* Map of timeout in seconds to register value */
|
||||
static const u8 wdt_timeout_map[1 + WDT_MAX_TIMEOUT] = {
|
||||
[0] = 0x0,
|
||||
[1] = 0x1,
|
||||
[2] = 0x2,
|
||||
[3] = 0x3,
|
||||
[4] = 0x4,
|
||||
[5] = 0x5,
|
||||
[6] = 0x6,
|
||||
[7] = 0x7,
|
||||
[8] = 0x7,
|
||||
[9] = 0x8,
|
||||
[10] = 0x8,
|
||||
[11] = 0x9,
|
||||
[12] = 0x9,
|
||||
[13] = 0xa,
|
||||
[14] = 0xa,
|
||||
[15] = 0xb,
|
||||
[16] = 0xb,
|
||||
};
|
||||
|
||||
static int sunxi_wdt_reset(struct udevice *dev)
|
||||
{
|
||||
struct sunxi_wdt_priv *priv = dev_get_priv(dev);
|
||||
const struct sunxi_wdt_reg *regs = priv->regs;
|
||||
void __iomem *base = priv->base;
|
||||
|
||||
writel(WDT_CTRL_RELOAD, base + regs->wdt_ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
|
||||
{
|
||||
struct sunxi_wdt_priv *priv = dev_get_priv(dev);
|
||||
const struct sunxi_wdt_reg *regs = priv->regs;
|
||||
void __iomem *base = priv->base;
|
||||
u32 val;
|
||||
|
||||
timeout /= MSEC_PER_SEC;
|
||||
if (timeout > WDT_MAX_TIMEOUT)
|
||||
timeout = WDT_MAX_TIMEOUT;
|
||||
|
||||
/* Set system reset function */
|
||||
val = readl(base + regs->wdt_cfg);
|
||||
val &= ~regs->wdt_reset_mask;
|
||||
val |= regs->wdt_reset_val;
|
||||
val |= regs->wdt_key_val;
|
||||
writel(val, base + regs->wdt_cfg);
|
||||
|
||||
/* Set timeout and enable watchdog */
|
||||
val = readl(base + regs->wdt_mode);
|
||||
val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
|
||||
val |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
|
||||
val |= WDT_MODE_EN;
|
||||
val |= regs->wdt_key_val;
|
||||
writel(val, base + regs->wdt_mode);
|
||||
|
||||
return sunxi_wdt_reset(dev);
|
||||
}
|
||||
|
||||
static int sunxi_wdt_stop(struct udevice *dev)
|
||||
{
|
||||
struct sunxi_wdt_priv *priv = dev_get_priv(dev);
|
||||
const struct sunxi_wdt_reg *regs = priv->regs;
|
||||
void __iomem *base = priv->base;
|
||||
|
||||
writel(regs->wdt_key_val, base + regs->wdt_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_wdt_expire_now(struct udevice *dev, ulong flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sunxi_wdt_start(dev, 0, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mdelay(500);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wdt_ops sunxi_wdt_ops = {
|
||||
.reset = sunxi_wdt_reset,
|
||||
.start = sunxi_wdt_start,
|
||||
.stop = sunxi_wdt_stop,
|
||||
.expire_now = sunxi_wdt_expire_now,
|
||||
};
|
||||
|
||||
static const struct sunxi_wdt_reg sun4i_wdt_reg = {
|
||||
.wdt_ctrl = 0x00,
|
||||
.wdt_cfg = 0x04,
|
||||
.wdt_mode = 0x04,
|
||||
.wdt_timeout_shift = 3,
|
||||
.wdt_reset_mask = 0x2,
|
||||
.wdt_reset_val = 0x2,
|
||||
};
|
||||
|
||||
static const struct sunxi_wdt_reg sun6i_wdt_reg = {
|
||||
.wdt_ctrl = 0x10,
|
||||
.wdt_cfg = 0x14,
|
||||
.wdt_mode = 0x18,
|
||||
.wdt_timeout_shift = 4,
|
||||
.wdt_reset_mask = 0x3,
|
||||
.wdt_reset_val = 0x1,
|
||||
};
|
||||
|
||||
static const struct sunxi_wdt_reg sun20i_wdt_reg = {
|
||||
.wdt_ctrl = 0x10,
|
||||
.wdt_cfg = 0x14,
|
||||
.wdt_mode = 0x18,
|
||||
.wdt_timeout_shift = 4,
|
||||
.wdt_reset_mask = 0x03,
|
||||
.wdt_reset_val = 0x01,
|
||||
.wdt_key_val = 0x16aa0000,
|
||||
};
|
||||
|
||||
static const struct udevice_id sunxi_wdt_ids[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg },
|
||||
{ .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg },
|
||||
{ .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int sunxi_wdt_probe(struct udevice *dev)
|
||||
{
|
||||
struct sunxi_wdt_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->base = dev_remap_addr(dev);
|
||||
if (!priv->base)
|
||||
return -EINVAL;
|
||||
|
||||
priv->regs = (void *)dev_get_driver_data(dev);
|
||||
if (!priv->regs)
|
||||
return -EINVAL;
|
||||
|
||||
sunxi_wdt_stop(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(sunxi_wdt) = {
|
||||
.name = "sunxi_wdt",
|
||||
.id = UCLASS_WDT,
|
||||
.of_match = sunxi_wdt_ids,
|
||||
.probe = sunxi_wdt_probe,
|
||||
.priv_auto = sizeof(struct sunxi_wdt_priv),
|
||||
.ops = &sunxi_wdt_ops,
|
||||
};
|
Loading…
Reference in a new issue