- watchdog: arm_smc_wdt: add watchdog support (Lionel)
- watchdog: ftwdt010: return a previously deleted driver now ported to
  DM (Sergei)
- watchdog: Add a watchdog driver for Raspberry Pi boards (Etienne)
This commit is contained in:
Tom Rini 2023-04-22 18:31:21 -04:00
commit da142d1ec9
7 changed files with 415 additions and 3 deletions

View file

@ -323,6 +323,7 @@ CONFIG_WDT=y
CONFIG_WDT_GPIO=y
CONFIG_WDT_SANDBOX=y
CONFIG_WDT_ALARM_SANDBOX=y
CONFIG_WDT_FTWDT010=y
CONFIG_FS_CBFS=y
CONFIG_FS_CRAMFS=y
CONFIG_ADDR_MAP=y

View file

@ -31,6 +31,7 @@ config WATCHDOG_TIMEOUT_MSECS
default 30000 if ARCH_SOCFPGA
default 16000 if ARCH_SUNXI
default 5376 if ULP_WATCHDOG
default 15000 if ARCH_BCM283X
default 60000
help
Watchdog timeout in msec
@ -327,6 +328,14 @@ config WDT_SUNXI
help
Enable support for the watchdog timer in Allwinner sunxi SoCs.
config WDT_BCM2835
bool "Broadcom 2835 watchdog timer support"
depends on WDT && ARCH_BCM283X
default y
help
Enable support for the watchdog timer in Broadcom 283X SoCs such
as Raspberry Pi boards.
config XILINX_TB_WATCHDOG
bool "Xilinx Axi watchdog timer support"
depends on WDT
@ -352,6 +361,14 @@ config WDT_TANGIER
Intel Tangier SoC. If you're using a board with Intel Tangier
SoC, say Y here.
config WDT_ARM_SMC
bool "ARM SMC watchdog timer support"
depends on WDT && ARM_SMCCC
imply WATCHDOG
help
Select this to enable Arm SMC watchdog timer. This watchdog will manage
a watchdog based on ARM SMCCC communication.
config SPL_WDT
bool "Enable driver model for watchdog timer drivers in SPL"
depends on SPL_DM
@ -359,4 +376,11 @@ config SPL_WDT
Enable driver model for watchdog timer in SPL.
This is similar to CONFIG_WDT in U-Boot.
config WDT_FTWDT010
bool "Faraday Technology ftwdt010 watchdog timer support"
depends on WDT
imply WATCHDOG
help
Faraday Technology ftwdt010 watchdog is an architecture independent
watchdog. It is usually used in SoC chip design.
endmenu

View file

@ -18,14 +18,17 @@ obj-$(CONFIG_$(SPL_TPL_)WDT) += wdt-uclass.o
obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
obj-$(CONFIG_WDT_ALARM_SANDBOX) += sandbox_alarm-wdt.o
obj-$(CONFIG_WDT_APPLE) += apple_wdt.o
obj-$(CONFIG_WDT_ARM_SMC) += arm_smc_wdt.o
obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
obj-$(CONFIG_WDT_AST2600) += ast2600_wdt.o
obj-$(CONFIG_WDT_BCM2835) += bcm2835_wdt.o
obj-$(CONFIG_WDT_BCM6345) += bcm6345_wdt.o
obj-$(CONFIG_WDT_BOOKE) += booke_wdt.o
obj-$(CONFIG_WDT_CORTINA) += cortina_wdt.o
obj-$(CONFIG_WDT_ORION) += orion_wdt.o
obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o
obj-$(CONFIG_WDT_FTWDT010) += ftwdt010_wdt.o
obj-$(CONFIG_WDT_GPIO) += gpio_wdt.o
obj-$(CONFIG_WDT_MAX6370) += max6370_wdt.o
obj-$(CONFIG_WDT_MESON_GXBB) += meson_gxbb_wdt.o

View file

@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ARM Secure Monitor Call watchdog driver
* Copyright (C) 2022, STMicroelectronics - All Rights Reserved
* This file is based on Linux driver drivers/watchdog/arm_smc_wdt.c
*/
#define LOG_CATEGORY UCLASS_WDT
#include <dm.h>
#include <dm/device_compat.h>
#include <linux/arm-smccc.h>
#include <linux/psci.h>
#include <wdt.h>
#define DRV_NAME "arm_smc_wdt"
#define WDT_TIMEOUT_SECS(TIMEOUT) ((TIMEOUT) / 1000)
enum smcwd_call {
SMCWD_INIT = 0,
SMCWD_SET_TIMEOUT = 1,
SMCWD_ENABLE = 2,
SMCWD_PET = 3,
SMCWD_GET_TIMELEFT = 4,
};
struct smcwd_priv_data {
u32 smc_id;
unsigned int min_timeout;
unsigned int max_timeout;
};
static int smcwd_call(struct udevice *dev, enum smcwd_call call,
unsigned long arg, struct arm_smccc_res *res)
{
struct smcwd_priv_data *priv = dev_get_priv(dev);
struct arm_smccc_res local_res;
if (!res)
res = &local_res;
arm_smccc_smc(priv->smc_id, call, arg, 0, 0, 0, 0, 0, res);
if (res->a0 == PSCI_RET_NOT_SUPPORTED)
return -ENODEV;
if (res->a0 == PSCI_RET_INVALID_PARAMS)
return -EINVAL;
if (res->a0 != PSCI_RET_SUCCESS)
return -EIO;
return 0;
}
static int smcwd_reset(struct udevice *dev)
{
return smcwd_call(dev, SMCWD_PET, 0, NULL);
}
static int smcwd_stop(struct udevice *dev)
{
return smcwd_call(dev, SMCWD_ENABLE, 0, NULL);
}
static int smcwd_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct smcwd_priv_data *priv = dev_get_priv(dev);
u64 timeout_sec = WDT_TIMEOUT_SECS(timeout_ms);
int err;
if (timeout_sec < priv->min_timeout || timeout_sec > priv->max_timeout) {
dev_err(dev, "Timeout value not supported\n");
return -EINVAL;
}
err = smcwd_call(dev, SMCWD_SET_TIMEOUT, timeout_sec, NULL);
if (err) {
dev_err(dev, "Timeout out configuration failed\n");
return err;
}
return smcwd_call(dev, SMCWD_ENABLE, 1, NULL);
}
static int smcwd_probe(struct udevice *dev)
{
struct smcwd_priv_data *priv = dev_get_priv(dev);
struct arm_smccc_res res;
int err;
priv->smc_id = dev_read_u32_default(dev, "arm,smc-id", 0x82003D06);
err = smcwd_call(dev, SMCWD_INIT, 0, &res);
if (err < 0) {
dev_err(dev, "Init failed %i\n", err);
return err;
}
priv->min_timeout = res.a1;
priv->max_timeout = res.a2;
return 0;
}
static const struct wdt_ops smcwd_ops = {
.start = smcwd_start,
.stop = smcwd_stop,
.reset = smcwd_reset,
};
static const struct udevice_id smcwd_dt_ids[] = {
{ .compatible = "arm,smc-wdt" },
{}
};
U_BOOT_DRIVER(wdt_sandbox) = {
.name = "smcwd",
.id = UCLASS_WDT,
.of_match = smcwd_dt_ids,
.priv_auto = sizeof(struct smcwd_priv_data),
.probe = smcwd_probe,
.ops = &smcwd_ops,
};

View file

@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2013 Lubomir Rintel <lkundrak@v3.sk>
* Copyright (C) 2023 Etienne Dublé (CNRS) <etienne.duble@imag.fr>
*
* This code is mostly derived from the linux driver.
*/
#include <dm.h>
#include <wdt.h>
#include <asm/io.h>
#include <linux/delay.h>
#define PM_RSTC 0x1c
#define PM_WDOG 0x24
#define PM_PASSWORD 0x5a000000
/* The hardware supports a maximum timeout value of 0xfffff ticks
* (just below 16 seconds).
*/
#define PM_WDOG_MAX_TICKS 0x000fffff
#define PM_RSTC_WRCFG_CLR 0xffffffcf
#define PM_RSTC_WRCFG_FULL_RESET 0x00000020
#define PM_RSTC_RESET 0x00000102
#define MS_TO_WDOG_TICKS(x) (((x) << 16) / 1000)
struct bcm2835_wdt_priv {
void __iomem *base;
u64 timeout_ticks;
};
static int bcm2835_wdt_start_ticks(struct udevice *dev,
u64 timeout_ticks, ulong flags)
{
struct bcm2835_wdt_priv *priv = dev_get_priv(dev);
void __iomem *base = priv->base;
u32 cur;
writel(PM_PASSWORD | timeout_ticks, base + PM_WDOG);
cur = readl(base + PM_RSTC);
writel(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET,
base + PM_RSTC);
return 0;
}
static int bcm2835_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct bcm2835_wdt_priv *priv = dev_get_priv(dev);
priv->timeout_ticks = MS_TO_WDOG_TICKS(timeout_ms);
if (priv->timeout_ticks > PM_WDOG_MAX_TICKS) {
printf("bcm2835_wdt: the timeout value is too high, using ~16s instead.\n");
priv->timeout_ticks = PM_WDOG_MAX_TICKS;
}
return bcm2835_wdt_start_ticks(dev, priv->timeout_ticks, flags);
}
static int bcm2835_wdt_reset(struct udevice *dev)
{
struct bcm2835_wdt_priv *priv = dev_get_priv(dev);
/* restart the timer with the value of priv->timeout_ticks
* saved from the last bcm2835_wdt_start() call.
*/
return bcm2835_wdt_start_ticks(dev, priv->timeout_ticks, 0);
}
static int bcm2835_wdt_stop(struct udevice *dev)
{
struct bcm2835_wdt_priv *priv = dev_get_priv(dev);
void __iomem *base = priv->base;
writel(PM_PASSWORD | PM_RSTC_RESET, base + PM_RSTC);
return 0;
}
static int bcm2835_wdt_expire_now(struct udevice *dev, ulong flags)
{
int ret;
/* use a timeout of 10 ticks (~150us) */
ret = bcm2835_wdt_start_ticks(dev, 10, flags);
if (ret)
return ret;
mdelay(500);
return 0;
}
static const struct wdt_ops bcm2835_wdt_ops = {
.reset = bcm2835_wdt_reset,
.start = bcm2835_wdt_start,
.stop = bcm2835_wdt_stop,
.expire_now = bcm2835_wdt_expire_now,
};
static const struct udevice_id bcm2835_wdt_ids[] = {
{ .compatible = "brcm,bcm2835-pm" },
{ .compatible = "brcm,bcm2835-pm-wdt" },
{ /* sentinel */ }
};
static int bcm2835_wdt_probe(struct udevice *dev)
{
struct bcm2835_wdt_priv *priv = dev_get_priv(dev);
priv->base = dev_remap_addr(dev);
if (!priv->base)
return -EINVAL;
priv->timeout_ticks = PM_WDOG_MAX_TICKS;
bcm2835_wdt_stop(dev);
return 0;
}
U_BOOT_DRIVER(bcm2835_wdt) = {
.name = "bcm2835_wdt",
.id = UCLASS_WDT,
.of_match = bcm2835_wdt_ids,
.probe = bcm2835_wdt_probe,
.priv_auto = sizeof(struct bcm2835_wdt_priv),
.ops = &bcm2835_wdt_ops,
};

View file

@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for the FTWDT010 Watch Dog Driver
*
* (c) Copyright 2004 Faraday Technology Corp. (www.faraday-tech.com)
* Based on sa1100_wdt.c by Oleg Drokin <green@crimea.edu>
* Based on SoftDog driver by Alan Cox <alan@redhat.com>
*
* Copyright (C) 2011 Andes Technology Corporation
* Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com>
*
* 27/11/2004 Initial release, Faraday.
* 12/01/2011 Port to u-boot, Macpaul Lin.
* 22/08/2022 Port to DM
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <log.h>
#include <asm/io.h>
#include <faraday/ftwdt010_wdt.h>
struct ftwdt010_wdt_priv {
struct ftwdt010_wdt __iomem *regs;
};
/*
* Set the watchdog time interval.
* Counter is 32 bit.
*/
static int ftwdt010_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct ftwdt010_wdt_priv *priv = dev_get_priv(dev);
struct ftwdt010_wdt *wd = priv->regs;
unsigned int reg;
debug("Activating WDT %llu ms\n", timeout_ms);
/* Check if disabled */
if (readl(&wd->wdcr) & ~FTWDT010_WDCR_ENABLE) {
printf("sorry, watchdog is disabled\n");
return -1;
}
/*
* In a 66MHz system,
* if you set WDLOAD as 0x03EF1480 (66000000)
* the reset timer is 1 second.
*/
reg = FTWDT010_WDLOAD(timeout_ms * FTWDT010_TIMEOUT_FACTOR);
writel(reg, &wd->wdload);
return 0;
}
static int ftwdt010_wdt_reset(struct udevice *dev)
{
struct ftwdt010_wdt_priv *priv = dev_get_priv(dev);
struct ftwdt010_wdt *wd = priv->regs;
/* clear control register */
writel(0, &wd->wdcr);
/* Write Magic number */
writel(FTWDT010_WDRESTART_MAGIC, &wd->wdrestart);
/* Enable WDT */
writel((FTWDT010_WDCR_RST | FTWDT010_WDCR_ENABLE), &wd->wdcr);
return 0;
}
static int ftwdt010_wdt_stop(struct udevice *dev)
{
struct ftwdt010_wdt_priv *priv = dev_get_priv(dev);
struct ftwdt010_wdt *wd = priv->regs;
debug("Deactivating WDT..\n");
/*
* It was defined with CONFIG_WATCHDOG_NOWAYOUT in Linux
*
* Shut off the timer.
* Lock it in if it's a module and we defined ...NOWAYOUT
*/
writel(0, &wd->wdcr);
return 0;
}
static int ftwdt010_wdt_expire_now(struct udevice *dev, ulong flags)
{
struct ftwdt010_wdt_priv *priv = dev_get_priv(dev);
struct ftwdt010_wdt *wd = priv->regs;
debug("Expiring WDT..\n");
writel(FTWDT010_WDLOAD(0), &wd->wdload);
return ftwdt010_wdt_reset(dev);
}
static int ftwdt010_wdt_probe(struct udevice *dev)
{
struct ftwdt010_wdt_priv *priv = dev_get_priv(dev);
priv->regs = dev_read_addr_ptr(dev);
if (!priv->regs)
return -EINVAL;
return 0;
}
static const struct wdt_ops ftwdt010_wdt_ops = {
.start = ftwdt010_wdt_start,
.reset = ftwdt010_wdt_reset,
.stop = ftwdt010_wdt_stop,
.expire_now = ftwdt010_wdt_expire_now,
};
static const struct udevice_id ftwdt010_wdt_ids[] = {
{ .compatible = "faraday,ftwdt010" },
{}
};
U_BOOT_DRIVER(ftwdt010_wdt) = {
.name = "ftwdt010_wdt",
.id = UCLASS_WDT,
.of_match = ftwdt010_wdt_ids,
.ops = &ftwdt010_wdt_ops,
.probe = ftwdt010_wdt_probe,
.priv_auto = sizeof(struct ftwdt010_wdt_priv),
};

View file

@ -89,7 +89,4 @@ struct ftwdt010_wdt {
*/
#define FTWDT010_TIMEOUT_FACTOR (get_board_sys_clk() / 1000) /* 1 ms */
void ftwdt010_wdt_reset(void);
void ftwdt010_wdt_disable(void);
#endif /* __FTWDT010_H */