mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-02 17:41:08 +00:00
ee6866e6c7
After "4b969deac0 watchdog: imx: Add DM support", the imx watchdog can be started by wdt command. But the imx watchdog driver only support start with the default timeout. This commit adds the support for setting the timeout which pass from the wdt command into the imx watchdog. If the timeout out of the valid range(0.5~128s), start the watchdog with a timeout within the valid range and the timeout is the one which closest to the passed timeout. Signed-off-by: Yuezhang.Mo <yuezhang.mo@sony.com> Reviewed-by: Andy.Wu <Andy.Wu@sony.com> Reviewed-by: stefano Babic <sbabic@denx.de>
176 lines
3.7 KiB
C
176 lines
3.7 KiB
C
/*
|
|
* watchdog.c - driver for i.mx on-chip watchdog
|
|
*
|
|
* Licensed under the GPL-2 or later.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <cpu_func.h>
|
|
#include <dm.h>
|
|
#include <hang.h>
|
|
#include <asm/io.h>
|
|
#include <wdt.h>
|
|
#include <watchdog.h>
|
|
#include <asm/arch/imx-regs.h>
|
|
#ifdef CONFIG_FSL_LSCH2
|
|
#include <asm/arch/immap_lsch2.h>
|
|
#endif
|
|
#include <fsl_wdog.h>
|
|
#include <div64.h>
|
|
|
|
#define TIMEOUT_MAX 128000
|
|
#define TIMEOUT_MIN 500
|
|
|
|
static void imx_watchdog_expire_now(struct watchdog_regs *wdog, bool ext_reset)
|
|
{
|
|
u16 wcr = WCR_WDE;
|
|
|
|
if (ext_reset)
|
|
wcr |= WCR_SRS; /* do not assert internal reset */
|
|
else
|
|
wcr |= WCR_WDA; /* do not assert external reset */
|
|
|
|
/* Write 3 times to ensure it works, due to IMX6Q errata ERR004346 */
|
|
writew(wcr, &wdog->wcr);
|
|
writew(wcr, &wdog->wcr);
|
|
writew(wcr, &wdog->wcr);
|
|
|
|
while (1) {
|
|
/*
|
|
* spin before reset
|
|
*/
|
|
}
|
|
}
|
|
|
|
#if !defined(CONFIG_IMX_WATCHDOG) || \
|
|
(defined(CONFIG_IMX_WATCHDOG) && !CONFIG_IS_ENABLED(WDT))
|
|
void __attribute__((weak)) reset_cpu(ulong addr)
|
|
{
|
|
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
|
|
|
|
imx_watchdog_expire_now(wdog, true);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_IMX_WATCHDOG)
|
|
static void imx_watchdog_reset(struct watchdog_regs *wdog)
|
|
{
|
|
#ifndef CONFIG_WATCHDOG_RESET_DISABLE
|
|
writew(0x5555, &wdog->wsr);
|
|
writew(0xaaaa, &wdog->wsr);
|
|
#endif /* CONFIG_WATCHDOG_RESET_DISABLE*/
|
|
}
|
|
|
|
static void imx_watchdog_init(struct watchdog_regs *wdog, bool ext_reset,
|
|
u64 timeout)
|
|
{
|
|
u16 wcr;
|
|
|
|
/*
|
|
* The timer watchdog can be set between
|
|
* 0.5 and 128 Seconds. If not defined
|
|
* in configuration file, sets 128 Seconds
|
|
*/
|
|
#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS
|
|
#define CONFIG_WATCHDOG_TIMEOUT_MSECS 128000
|
|
#endif
|
|
|
|
timeout = max_t(u64, timeout, TIMEOUT_MIN);
|
|
timeout = min_t(u64, timeout, TIMEOUT_MAX);
|
|
timeout = lldiv(timeout, 500) - 1;
|
|
|
|
#ifdef CONFIG_FSL_LSCH2
|
|
wcr = (WCR_WDA | WCR_SRS | WCR_WDE) << 8 | timeout;
|
|
#else
|
|
wcr = WCR_WDZST | WCR_WDBG | WCR_WDE | WCR_SRS |
|
|
WCR_WDA | SET_WCR_WT(timeout);
|
|
if (ext_reset)
|
|
wcr |= WCR_WDT;
|
|
#endif /* CONFIG_FSL_LSCH2*/
|
|
writew(wcr, &wdog->wcr);
|
|
imx_watchdog_reset(wdog);
|
|
}
|
|
|
|
#if !CONFIG_IS_ENABLED(WDT)
|
|
void hw_watchdog_reset(void)
|
|
{
|
|
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
|
|
|
|
imx_watchdog_reset(wdog);
|
|
}
|
|
|
|
void hw_watchdog_init(void)
|
|
{
|
|
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
|
|
|
|
imx_watchdog_init(wdog, true, CONFIG_WATCHDOG_TIMEOUT_MSECS);
|
|
}
|
|
#else
|
|
struct imx_wdt_priv {
|
|
void __iomem *base;
|
|
bool ext_reset;
|
|
};
|
|
|
|
static int imx_wdt_reset(struct udevice *dev)
|
|
{
|
|
struct imx_wdt_priv *priv = dev_get_priv(dev);
|
|
|
|
imx_watchdog_reset(priv->base);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int imx_wdt_expire_now(struct udevice *dev, ulong flags)
|
|
{
|
|
struct imx_wdt_priv *priv = dev_get_priv(dev);
|
|
|
|
imx_watchdog_expire_now(priv->base, priv->ext_reset);
|
|
hang();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int imx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
|
|
{
|
|
struct imx_wdt_priv *priv = dev_get_priv(dev);
|
|
|
|
imx_watchdog_init(priv->base, priv->ext_reset, timeout);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int imx_wdt_probe(struct udevice *dev)
|
|
{
|
|
struct imx_wdt_priv *priv = dev_get_priv(dev);
|
|
|
|
priv->base = dev_read_addr_ptr(dev);
|
|
if (!priv->base)
|
|
return -ENOENT;
|
|
|
|
priv->ext_reset = dev_read_bool(dev, "fsl,ext-reset-output");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct wdt_ops imx_wdt_ops = {
|
|
.start = imx_wdt_start,
|
|
.reset = imx_wdt_reset,
|
|
.expire_now = imx_wdt_expire_now,
|
|
};
|
|
|
|
static const struct udevice_id imx_wdt_ids[] = {
|
|
{ .compatible = "fsl,imx21-wdt" },
|
|
{}
|
|
};
|
|
|
|
U_BOOT_DRIVER(imx_wdt) = {
|
|
.name = "imx_wdt",
|
|
.id = UCLASS_WDT,
|
|
.of_match = imx_wdt_ids,
|
|
.probe = imx_wdt_probe,
|
|
.ops = &imx_wdt_ops,
|
|
.priv_auto_alloc_size = sizeof(struct imx_wdt_priv),
|
|
.flags = DM_FLAG_PRE_RELOC,
|
|
};
|
|
#endif
|
|
#endif
|