aspeed: Watchdog Timer Driver

This driver supports ast2500 and ast2400 SoCs.
Only ast2500 supports reset_mask and thus the option of resettting
individual peripherals using WDT.

Signed-off-by: Maxim Sloyko <maxims@google.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
master
maxims@google.com 7 years ago committed by Tom Rini
parent 0753bc2d30
commit 1eb0a464b7
  1. 53
      arch/arm/include/asm/arch-aspeed/wdt.h
  2. 40
      arch/arm/mach-aspeed/ast_wdt.c
  3. 11
      drivers/watchdog/Kconfig
  4. 1
      drivers/watchdog/Makefile
  5. 125
      drivers/watchdog/ast_wdt.c

@ -67,15 +67,60 @@ struct ast_wdt {
u32 timeout_status;
u32 clr_timeout_status;
u32 reset_width;
#ifdef CONFIG_ASPEED_AST2500
/* On pre-ast2500 SoCs this register is reserved. */
u32 reset_mask;
#else
u32 reserved0;
#endif
};
/**
* Given flags parameter passed to wdt_reset or wdt_start uclass functions,
* gets Reset Mode value from it.
*
* @flags: flags parameter passed into wdt_reset or wdt_start
* @return Reset Mode value
*/
u32 ast_reset_mode_from_flags(ulong flags);
/**
* Given flags parameter passed to wdt_reset or wdt_start uclass functions,
* gets Reset Mask value from it. Reset Mask is only supported on ast2500
*
* @flags: flags parameter passed into wdt_reset or wdt_start
* @return Reset Mask value
*/
u32 ast_reset_mask_from_flags(ulong flags);
/**
* Given Reset Mask and Reset Mode values, converts them to flags,
* suitable for passing into wdt_start or wdt_reset uclass functions.
*
* On ast2500 Reset Mask is 25 bits wide and Reset Mode is 2 bits wide, so they
* can both be packed into single 32 bits wide value.
*
* @reset_mode: Reset Mode
* @reset_mask: Reset Mask
*/
ulong ast_flags_from_reset_mode_mask(u32 reset_mode, u32 reset_mask);
#ifndef CONFIG_WDT
/**
* Stop WDT
*
* @wdt: watchdog to stop
*
* When using driver model this function has different signature
*/
void wdt_stop(struct ast_wdt *wdt);
/**
* Stop WDT
*
* @wdt: watchdog to start
* @timeout watchdog timeout in number of clock ticks
*
* When using driver model this function has different signature
*/
void wdt_start(struct ast_wdt *wdt, u32 timeout);
#endif /* CONFIG_WDT */
/**
* Reset peripherals specified by mask

@ -9,6 +9,27 @@
#include <asm/arch/wdt.h>
#include <linux/err.h>
u32 ast_reset_mode_from_flags(ulong flags)
{
return flags & WDT_CTRL_RESET_MASK;
}
u32 ast_reset_mask_from_flags(ulong flags)
{
return flags >> 2;
}
ulong ast_flags_from_reset_mode_mask(u32 reset_mode, u32 reset_mask)
{
ulong ret = reset_mode & WDT_CTRL_RESET_MASK;
if (ret == WDT_CTRL_RESET_SOC)
ret |= (reset_mask << 2);
return ret;
}
#ifndef CONFIG_WDT
void wdt_stop(struct ast_wdt *wdt)
{
clrbits_le32(&wdt->ctrl, WDT_CTRL_EN);
@ -26,15 +47,7 @@ void wdt_start(struct ast_wdt *wdt, u32 timeout)
setbits_le32(&wdt->ctrl,
WDT_CTRL_EN | WDT_CTRL_RESET | WDT_CTRL_CLK1MHZ);
}
struct ast_wdt *ast_get_wdt(u8 wdt_number)
{
if (wdt_number > CONFIG_WDT_NUM - 1)
return ERR_PTR(-EINVAL);
return (struct ast_wdt *)(WDT_BASE +
sizeof(struct ast_wdt) * wdt_number);
}
#endif /* CONFIG_WDT */
int ast_wdt_reset_masked(struct ast_wdt *wdt, u32 mask)
{
@ -57,3 +70,12 @@ int ast_wdt_reset_masked(struct ast_wdt *wdt, u32 mask)
return -EINVAL;
#endif
}
struct ast_wdt *ast_get_wdt(u8 wdt_number)
{
if (wdt_number > CONFIG_WDT_NUM - 1)
return ERR_PTR(-EINVAL);
return (struct ast_wdt *)(WDT_BASE +
sizeof(struct ast_wdt) * wdt_number);
}

@ -23,4 +23,15 @@ config WDT_SANDBOX
can be probed and supports all of the methods of WDT, but does not
really do anything.
config WDT_ASPEED
bool "Aspeed ast2400/ast2500 watchdog timer support"
depends on WDT
default y if ARCH_ASPEED
help
Select this to enable watchdog timer for Aspeed ast2500/ast2400 devices.
The watchdog timer is stopped when initialized. It performs reset, either
full SoC reset or CPU or just some peripherals, based on the flags.
It currently does not support Boot Flash Addressing Mode Detection or
Second Boot.
endmenu

@ -17,3 +17,4 @@ obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o
obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
obj-$(CONFIG_WDT) += wdt-uclass.o
obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o

@ -0,0 +1,125 @@
/*
* Copyright 2017 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <wdt.h>
#include <asm/io.h>
#include <asm/arch/wdt.h>
#define WDT_AST2500 2500
#define WDT_AST2400 2400
DECLARE_GLOBAL_DATA_PTR;
struct ast_wdt_priv {
struct ast_wdt *regs;
};
static int ast_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
ulong driver_data = dev_get_driver_data(dev);
u32 reset_mode = ast_reset_mode_from_flags(flags);
clrsetbits_le32(&priv->regs->ctrl,
WDT_CTRL_RESET_MASK << WDT_CTRL_RESET_MODE_SHIFT,
reset_mode << WDT_CTRL_RESET_MODE_SHIFT);
if (driver_data >= WDT_AST2500 && reset_mode == WDT_CTRL_RESET_SOC)
writel(ast_reset_mask_from_flags(flags),
&priv->regs->reset_mask);
writel((u32) timeout, &priv->regs->counter_reload_val);
writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
/*
* Setting CLK1MHZ bit is just for compatibility with ast2400 part.
* On ast2500 watchdog timer clock is fixed at 1MHz and the bit is
* read-only
*/
setbits_le32(&priv->regs->ctrl,
WDT_CTRL_EN | WDT_CTRL_RESET | WDT_CTRL_CLK1MHZ);
return 0;
}
static int ast_wdt_stop(struct udevice *dev)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
clrbits_le32(&priv->regs->ctrl, WDT_CTRL_EN);
return 0;
}
static int ast_wdt_reset(struct udevice *dev)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
return 0;
}
static int ast_wdt_expire_now(struct udevice *dev, ulong flags)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
int ret;
ret = ast_wdt_start(dev, 1, flags);
if (ret)
return ret;
while (readl(&priv->regs->ctrl) & WDT_CTRL_EN)
;
return ast_wdt_stop(dev);
}
static int ast_wdt_ofdata_to_platdata(struct udevice *dev)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
priv->regs = dev_get_addr_ptr(dev);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
return 0;
}
static const struct wdt_ops ast_wdt_ops = {
.start = ast_wdt_start,
.reset = ast_wdt_reset,
.stop = ast_wdt_stop,
.expire_now = ast_wdt_expire_now,
};
static const struct udevice_id ast_wdt_ids[] = {
{ .compatible = "aspeed,wdt", .data = WDT_AST2500 },
{ .compatible = "aspeed,ast2500-wdt", .data = WDT_AST2500 },
{ .compatible = "aspeed,ast2400-wdt", .data = WDT_AST2400 },
{}
};
static int ast_wdt_probe(struct udevice *dev)
{
debug("%s() wdt%u\n", __func__, dev->seq);
ast_wdt_stop(dev);
return 0;
}
U_BOOT_DRIVER(ast_wdt) = {
.name = "ast_wdt",
.id = UCLASS_WDT,
.of_match = ast_wdt_ids,
.probe = ast_wdt_probe,
.priv_auto_alloc_size = sizeof(struct ast_wdt_priv),
.ofdata_to_platdata = ast_wdt_ofdata_to_platdata,
.ops = &ast_wdt_ops,
.flags = DM_FLAG_PRE_RELOC,
};
Loading…
Cancel
Save