The Orion watchdog can be found on some Marvell Armada chips. This driver is based on the code by Tomas Hlavacek in the CZ.NIC turris-omnia-uboot repository, which can be found at https://gitlab.labs.nic.cz/turris/turris-omnia-uboot, and that one is based on code by Sylver Bruneau. His code is already in mainline Linux kernel. The code uses the new driver model API. Signed-off-by: Tomas Hlavacek <tomas.hlavacek@nic.cz> Signed-off-by: Marek Behun <marek.behun@nic.cz> create mode 100644 drivers/watchdog/orion_wdt.c Signed-off-by: Stefan Roese <sr@denx.de>master
parent
90bcc3d38d
commit
2ab7704a6d
@ -0,0 +1,177 @@ |
||||
/*
|
||||
* drivers/watchdog/orion_wdt.c |
||||
* |
||||
* Watchdog driver for Orion/Kirkwood processors |
||||
* |
||||
* Authors: Tomas Hlavacek <tmshlvck@gmail.com> |
||||
* Sylver Bruneau <sylver.bruneau@googlemail.com> |
||||
* Marek Behun <marek.behun@nic.cz> |
||||
* |
||||
* This file is licensed under the terms of the GNU General Public |
||||
* License version 2. This program is licensed "as is" without any |
||||
* warranty of any kind, whether express or implied. |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <wdt.h> |
||||
#include <asm/io.h> |
||||
#include <asm/arch/cpu.h> |
||||
#include <asm/arch/soc.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
struct orion_wdt_priv { |
||||
void __iomem *reg; |
||||
int wdt_counter_offset; |
||||
void __iomem *rstout; |
||||
void __iomem *rstout_mask; |
||||
u32 timeout; |
||||
}; |
||||
|
||||
#define RSTOUT_ENABLE_BIT BIT(8) |
||||
#define RSTOUT_MASK_BIT BIT(10) |
||||
#define WDT_ENABLE_BIT BIT(8) |
||||
|
||||
#define TIMER_CTRL 0x0000 |
||||
#define TIMER_A370_STATUS 0x04 |
||||
|
||||
#define WDT_AXP_FIXED_ENABLE_BIT BIT(10) |
||||
#define WDT_A370_EXPIRED BIT(31) |
||||
|
||||
static int orion_wdt_reset(struct udevice *dev) |
||||
{ |
||||
struct orion_wdt_priv *priv = dev_get_priv(dev); |
||||
|
||||
/* Reload watchdog duration */ |
||||
writel(priv->timeout, priv->reg + priv->wdt_counter_offset); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int orion_wdt_start(struct udevice *dev, u64 timeout, ulong flags) |
||||
{ |
||||
struct orion_wdt_priv *priv = dev_get_priv(dev); |
||||
u32 reg; |
||||
|
||||
priv->timeout = (u32) timeout; |
||||
|
||||
/* Enable the fixed watchdog clock input */ |
||||
reg = readl(priv->reg + TIMER_CTRL); |
||||
reg |= WDT_AXP_FIXED_ENABLE_BIT; |
||||
writel(reg, priv->reg + TIMER_CTRL); |
||||
|
||||
/* Set watchdog duration */ |
||||
writel(priv->timeout, priv->reg + priv->wdt_counter_offset); |
||||
|
||||
/* Clear the watchdog expiration bit */ |
||||
reg = readl(priv->reg + TIMER_A370_STATUS); |
||||
reg &= ~WDT_A370_EXPIRED; |
||||
writel(reg, priv->reg + TIMER_A370_STATUS); |
||||
|
||||
/* Enable watchdog timer */ |
||||
reg = readl(priv->reg + TIMER_CTRL); |
||||
reg |= WDT_ENABLE_BIT; |
||||
writel(reg, priv->reg + TIMER_CTRL); |
||||
|
||||
/* Enable reset on watchdog */ |
||||
reg = readl(priv->rstout); |
||||
reg |= RSTOUT_ENABLE_BIT; |
||||
writel(reg, priv->rstout); |
||||
|
||||
reg = readl(priv->rstout_mask); |
||||
reg &= ~RSTOUT_MASK_BIT; |
||||
writel(reg, priv->rstout_mask); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int orion_wdt_stop(struct udevice *dev) |
||||
{ |
||||
struct orion_wdt_priv *priv = dev_get_priv(dev); |
||||
u32 reg; |
||||
|
||||
/* Disable reset on watchdog */ |
||||
reg = readl(priv->rstout_mask); |
||||
reg |= RSTOUT_MASK_BIT; |
||||
writel(reg, priv->rstout_mask); |
||||
|
||||
reg = readl(priv->rstout); |
||||
reg &= ~RSTOUT_ENABLE_BIT; |
||||
writel(reg, priv->rstout); |
||||
|
||||
/* Disable watchdog timer */ |
||||
reg = readl(priv->reg + TIMER_CTRL); |
||||
reg &= ~WDT_ENABLE_BIT; |
||||
writel(reg, priv->reg + TIMER_CTRL); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static inline bool save_reg_from_ofdata(struct udevice *dev, int index, |
||||
void __iomem **reg, int *offset) |
||||
{ |
||||
fdt_addr_t addr; |
||||
fdt_size_t off; |
||||
|
||||
addr = fdtdec_get_addr_size_auto_noparent( |
||||
gd->fdt_blob, dev_of_offset(dev), "reg", index, &off, true); |
||||
|
||||
if (addr == FDT_ADDR_T_NONE) |
||||
return false; |
||||
|
||||
*reg = (void __iomem *) addr; |
||||
if (offset) |
||||
*offset = off; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
static int orion_wdt_ofdata_to_platdata(struct udevice *dev) |
||||
{ |
||||
struct orion_wdt_priv *priv = dev_get_priv(dev); |
||||
|
||||
if (!save_reg_from_ofdata(dev, 0, &priv->reg, |
||||
&priv->wdt_counter_offset)) |
||||
goto err; |
||||
|
||||
if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL)) |
||||
goto err; |
||||
|
||||
if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL)) |
||||
goto err; |
||||
|
||||
return 0; |
||||
err: |
||||
debug("%s: Could not determine Orion wdt IO addresses\n", __func__); |
||||
return -ENXIO; |
||||
} |
||||
|
||||
static int orion_wdt_probe(struct udevice *dev) |
||||
{ |
||||
debug("%s: Probing wdt%u\n", __func__, dev->seq); |
||||
orion_wdt_stop(dev); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct wdt_ops orion_wdt_ops = { |
||||
.start = orion_wdt_start, |
||||
.reset = orion_wdt_reset, |
||||
.stop = orion_wdt_stop, |
||||
}; |
||||
|
||||
static const struct udevice_id orion_wdt_ids[] = { |
||||
{ .compatible = "marvell,armada-380-wdt" }, |
||||
{} |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(orion_wdt) = { |
||||
.name = "orion_wdt", |
||||
.id = UCLASS_WDT, |
||||
.of_match = orion_wdt_ids, |
||||
.probe = orion_wdt_probe, |
||||
.priv_auto_alloc_size = sizeof(struct orion_wdt_priv), |
||||
.ofdata_to_platdata = orion_wdt_ofdata_to_platdata, |
||||
.ops = &orion_wdt_ops, |
||||
}; |
Loading…
Reference in new issue