mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-06-12 09:41:36 +00:00
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>
This commit is contained in:
parent
0753bc2d30
commit
1eb0a464b7
5 changed files with 217 additions and 13 deletions
|
@ -67,15 +67,60 @@ struct ast_wdt {
|
||||||
u32 timeout_status;
|
u32 timeout_status;
|
||||||
u32 clr_timeout_status;
|
u32 clr_timeout_status;
|
||||||
u32 reset_width;
|
u32 reset_width;
|
||||||
#ifdef CONFIG_ASPEED_AST2500
|
/* On pre-ast2500 SoCs this register is reserved. */
|
||||||
u32 reset_mask;
|
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);
|
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);
|
void wdt_start(struct ast_wdt *wdt, u32 timeout);
|
||||||
|
#endif /* CONFIG_WDT */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset peripherals specified by mask
|
* Reset peripherals specified by mask
|
||||||
|
|
|
@ -9,6 +9,27 @@
|
||||||
#include <asm/arch/wdt.h>
|
#include <asm/arch/wdt.h>
|
||||||
#include <linux/err.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)
|
void wdt_stop(struct ast_wdt *wdt)
|
||||||
{
|
{
|
||||||
clrbits_le32(&wdt->ctrl, WDT_CTRL_EN);
|
clrbits_le32(&wdt->ctrl, WDT_CTRL_EN);
|
||||||
|
@ -26,15 +47,7 @@ void wdt_start(struct ast_wdt *wdt, u32 timeout)
|
||||||
setbits_le32(&wdt->ctrl,
|
setbits_le32(&wdt->ctrl,
|
||||||
WDT_CTRL_EN | WDT_CTRL_RESET | WDT_CTRL_CLK1MHZ);
|
WDT_CTRL_EN | WDT_CTRL_RESET | WDT_CTRL_CLK1MHZ);
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_WDT */
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ast_wdt_reset_masked(struct ast_wdt *wdt, u32 mask)
|
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;
|
return -EINVAL;
|
||||||
#endif
|
#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
|
can be probed and supports all of the methods of WDT, but does not
|
||||||
really do anything.
|
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
|
endmenu
|
||||||
|
|
|
@ -17,3 +17,4 @@ obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o
|
||||||
obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
|
obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
|
||||||
obj-$(CONFIG_WDT) += wdt-uclass.o
|
obj-$(CONFIG_WDT) += wdt-uclass.o
|
||||||
obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
|
obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
|
||||||
|
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
|
||||||
|
|
125
drivers/watchdog/ast_wdt.c
Normal file
125
drivers/watchdog/ast_wdt.c
Normal file
|
@ -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…
Add table
Reference in a new issue