- nuvoton: add expire function for generic reset (Jim)
- handle watchdogs during keyed autoboot (Rasmus)
- cyclic: Don't disable cylic function upon exceeding CPU time (Stefan)
- ulp wdog: Updates to support iMX93 and DM (Alice)
This commit is contained in:
Tom Rini 2022-10-26 07:45:55 -04:00
commit c2c485db45
14 changed files with 303 additions and 44 deletions

View File

@ -130,6 +130,23 @@ void os_exit(int exit_code)
exit(exit_code);
}
unsigned int os_alarm(unsigned int seconds)
{
return alarm(seconds);
}
void os_set_alarm_handler(void (*handler)(int))
{
if (!handler)
handler = SIG_DFL;
signal(SIGALRM, handler);
}
void os_raise_sigalrm(void)
{
raise(SIGALRM);
}
int os_write_file(const char *fname, const void *buf, int size)
{
int fd;

View File

@ -27,6 +27,12 @@
};
};
alarm_wdt: alarm-wdt {
compatible = "sandbox,alarm-wdt";
timeout-sec = <5>;
u-boot,autostart;
};
audio: audio-codec {
compatible = "sandbox,audio-codec";
#sound-dai-cells = <1>;

View File

@ -85,13 +85,17 @@ void cyclic_run(void)
cyclic->cpu_time_us += cpu_time;
/* Check if cpu-time exceeds max allowed time */
if (cpu_time > CONFIG_CYCLIC_MAX_CPU_TIME_US) {
pr_err("cyclic function %s took too long: %lldus vs %dus max, disabling\n",
if ((cpu_time > CONFIG_CYCLIC_MAX_CPU_TIME_US) &&
(!cyclic->already_warned)) {
pr_err("cyclic function %s took too long: %lldus vs %dus max\n",
cyclic->name, cpu_time,
CONFIG_CYCLIC_MAX_CPU_TIME_US);
/* Unregister this cyclic function */
cyclic_unregister(cyclic);
/*
* Don't disable this function, just warn once
* about this exceeding CPU time usage
*/
cyclic->already_warned = true;
}
}
}

View File

@ -56,6 +56,7 @@ CONFIG_CMD_REMOTEPROC=y
CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y
CONFIG_CMD_CAT=y
CONFIG_CMD_WDT=y
CONFIG_BOOTP_DNS2=y
CONFIG_CMD_TFTPPUT=y
CONFIG_CMD_TFTPSRV=y
@ -238,6 +239,7 @@ CONFIG_SPLASH_SCREEN_ALIGN=y
CONFIG_WDT=y
CONFIG_WDT_GPIO=y
CONFIG_WDT_SANDBOX=y
CONFIG_WDT_ALARM_SANDBOX=y
CONFIG_FS_CBFS=y
CONFIG_FS_CRAMFS=y
CONFIG_CMD_DHRYSTONE=y

View File

@ -80,6 +80,7 @@ CONFIG_CMD_REMOTEPROC=y
CONFIG_CMD_SPI=y
CONFIG_CMD_TEMPERATURE=y
CONFIG_CMD_USB=y
CONFIG_CMD_WDT=y
CONFIG_CMD_AXI=y
CONFIG_CMD_CAT=y
CONFIG_CMD_SETEXPR_FMT=y
@ -314,6 +315,7 @@ CONFIG_W1_EEPROM_SANDBOX=y
CONFIG_WDT=y
CONFIG_WDT_GPIO=y
CONFIG_WDT_SANDBOX=y
CONFIG_WDT_ALARM_SANDBOX=y
CONFIG_FS_CBFS=y
CONFIG_FS_CRAMFS=y
CONFIG_ADDR_MAP=y

View File

@ -6,7 +6,8 @@ Optional properties:
be used instead.
- hw_margin_ms : Period used to reset the watchdog in ms
If this period is not defined, the default value is 1000.
- u-boot,noautostart : Specify that this watchdog should not autostart
When the config option WATCHDOG_AUTOSTART is set, all enabled
watchdogs are started. This property allows specifying that this
watchdog should NOT be started.
- u-boot,noautostart :
- u-boot,autostart : These (mutually exclusive) boolean properties can be used to control
whether the watchdog is automatically started when probed. If neither
are present, the behaviour is determined by the config option
WATCHDOG_AUTOSTART.

View File

@ -281,6 +281,14 @@ config WDT_SANDBOX
can be probed and supports all of the methods of WDT, but does not
really do anything.
config WDT_ALARM_SANDBOX
bool "Enable SIGALRM-based Watchdog Timer support for Sandbox"
depends on SANDBOX && WDT
help
Enable support for a SIGALRM-based watchdog timer in Sandbox. This is
a watchdog device based on the host OS' alarm() function, which will
kill the sandbox with SIGALRM unless properly maintained.
config WDT_SBSA
bool "SBSA watchdog timer support"
depends on WDT

View File

@ -16,6 +16,7 @@ obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o
obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
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_ARMADA_37XX) += armada-37xx-wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o

View File

@ -75,6 +75,11 @@ static int npcm_wdt_reset(struct udevice *dev)
return 0;
}
static int npcm_wdt_expire_now(struct udevice *dev, ulong flags)
{
return npcm_wdt_reset(dev);
}
static int npcm_wdt_of_to_plat(struct udevice *dev)
{
struct npcm_wdt_priv *priv = dev_get_priv(dev);
@ -87,6 +92,7 @@ static int npcm_wdt_of_to_plat(struct udevice *dev)
}
static const struct wdt_ops npcm_wdt_ops = {
.expire_now = npcm_wdt_expire_now,
.start = npcm_wdt_start,
.reset = npcm_wdt_reset,
.stop = npcm_wdt_stop,

View File

@ -0,0 +1,79 @@
#include <common.h>
#include <dm.h>
#include <os.h>
#include <wdt.h>
struct alarm_wdt_priv {
unsigned int timeout_sec;
};
static void alarm_handler(int sig)
{
const char *msg = "!!! ALARM !!!\n";
os_write(2, msg, strlen(msg));
os_fd_restore();
os_set_alarm_handler(NULL);
os_raise_sigalrm();
}
static int alarm_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct alarm_wdt_priv *priv = dev_get_priv(dev);
unsigned int sec;
timeout = DIV_ROUND_UP(timeout, 1000);
sec = min_t(u64, UINT_MAX, timeout);
priv->timeout_sec = sec;
os_alarm(0);
os_set_alarm_handler(alarm_handler);
os_alarm(sec);
return 0;
}
static int alarm_wdt_stop(struct udevice *dev)
{
os_alarm(0);
os_set_alarm_handler(NULL);
return 0;
}
static int alarm_wdt_reset(struct udevice *dev)
{
struct alarm_wdt_priv *priv = dev_get_priv(dev);
os_alarm(priv->timeout_sec);
return 0;
}
static int alarm_wdt_expire_now(struct udevice *dev, ulong flags)
{
alarm_handler(0);
return 0;
}
static const struct wdt_ops alarm_wdt_ops = {
.start = alarm_wdt_start,
.reset = alarm_wdt_reset,
.stop = alarm_wdt_stop,
.expire_now = alarm_wdt_expire_now,
};
static const struct udevice_id alarm_wdt_ids[] = {
{ .compatible = "sandbox,alarm-wdt" },
{}
};
U_BOOT_DRIVER(alarm_wdt_sandbox) = {
.name = "alarm_wdt_sandbox",
.id = UCLASS_WDT,
.of_match = alarm_wdt_ids,
.ops = &alarm_wdt_ops,
.priv_auto = sizeof(struct alarm_wdt_priv),
};

View File

@ -7,6 +7,8 @@
#include <cpu_func.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <dm.h>
#include <wdt.h>
/*
* MX7ULP WDOG Register Map
@ -18,6 +20,11 @@ struct wdog_regs {
u32 win;
};
struct ulp_wdt_priv {
struct wdog_regs *wdog;
u32 clk_rate;
};
#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS
#define CONFIG_WATCHDOG_TIMEOUT_MSECS 0x1500
#endif
@ -28,18 +35,27 @@ struct wdog_regs {
#define UNLOCK_WORD0 0xC520 /* 1st unlock word */
#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */
#define UNLOCK_WORD 0xD928C520 /* unlock word */
#define REFRESH_WORD 0xB480A602 /* refresh word */
#define WDGCS_WDGE BIT(7)
#define WDGCS_WDGUPDATE BIT(5)
#define WDGCS_RCS BIT(10)
#define WDGCS_ULK BIT(11)
#define WDOG_CS_PRES BIT(12)
#define WDGCS_CMD32EN BIT(13)
#define WDGCS_FLG BIT(14)
#define WDGCS_INT BIT(6)
#define WDG_BUS_CLK (0x0)
#define WDG_LPO_CLK (0x1)
#define WDG_32KHZ_CLK (0x2)
#define WDG_EXT_CLK (0x3)
#define CLK_RATE_1KHZ 1000
#define CLK_RATE_32KHZ 125
void hw_watchdog_set_timeout(u16 val)
{
/* setting timeout value */
@ -48,60 +64,96 @@ void hw_watchdog_set_timeout(u16 val)
writel(val, &wdog->toval);
}
void ulp_watchdog_reset(struct wdog_regs *wdog)
{
if (readl(&wdog->cs) & WDGCS_CMD32EN) {
writel(REFRESH_WORD, &wdog->cnt);
} else {
dmb();
__raw_writel(REFRESH_WORD0, &wdog->cnt);
__raw_writel(REFRESH_WORD1, &wdog->cnt);
dmb();
}
}
void ulp_watchdog_init(struct wdog_regs *wdog, u16 timeout)
{
u32 cmd32 = 0;
if (readl(&wdog->cs) & WDGCS_CMD32EN) {
writel(UNLOCK_WORD, &wdog->cnt);
cmd32 = WDGCS_CMD32EN;
} else {
dmb();
__raw_writel(UNLOCK_WORD0, &wdog->cnt);
__raw_writel(UNLOCK_WORD1, &wdog->cnt);
dmb();
}
/* Wait WDOG Unlock */
while (!(readl(&wdog->cs) & WDGCS_ULK))
;
hw_watchdog_set_timeout(timeout);
writel(0, &wdog->win);
/* setting 1-kHz clock source, enable counter running, and clear interrupt */
if (IS_ENABLED(CONFIG_ARCH_IMX9))
writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) |
WDGCS_FLG | WDOG_CS_PRES | WDGCS_INT), &wdog->cs);
else
writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) |
WDGCS_FLG), &wdog->cs);
/* Wait WDOG reconfiguration */
while (!(readl(&wdog->cs) & WDGCS_RCS))
;
ulp_watchdog_reset(wdog);
}
void hw_watchdog_reset(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
dmb();
__raw_writel(REFRESH_WORD0, &wdog->cnt);
__raw_writel(REFRESH_WORD1, &wdog->cnt);
dmb();
ulp_watchdog_reset(wdog);
}
void hw_watchdog_init(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
dmb();
__raw_writel(UNLOCK_WORD0, &wdog->cnt);
__raw_writel(UNLOCK_WORD1, &wdog->cnt);
dmb();
/* Wait WDOG Unlock */
while (!(readl(&wdog->cs) & WDGCS_ULK))
;
hw_watchdog_set_timeout(CONFIG_WATCHDOG_TIMEOUT_MSECS);
writel(0, &wdog->win);
/* setting 1-kHz clock source, enable counter running, and clear interrupt */
writel((WDGCS_WDGE | WDGCS_WDGUPDATE |(WDG_LPO_CLK << 8) | WDGCS_FLG), &wdog->cs);
/* Wait WDOG reconfiguration */
while (!(readl(&wdog->cs) & WDGCS_RCS))
;
hw_watchdog_reset();
ulp_watchdog_init(wdog, CONFIG_WATCHDOG_TIMEOUT_MSECS);
}
void reset_cpu(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
u32 cmd32 = 0;
dmb();
__raw_writel(UNLOCK_WORD0, &wdog->cnt);
__raw_writel(UNLOCK_WORD1, &wdog->cnt);
dmb();
if (readl(&wdog->cs) & WDGCS_CMD32EN) {
writel(UNLOCK_WORD, &wdog->cnt);
cmd32 = WDGCS_CMD32EN;
} else {
dmb();
__raw_writel(UNLOCK_WORD0, &wdog->cnt);
__raw_writel(UNLOCK_WORD1, &wdog->cnt);
dmb();
}
/* Wait WDOG Unlock */
while (!(readl(&wdog->cs) & WDGCS_ULK))
;
hw_watchdog_set_timeout(5); /* 5ms timeout */
hw_watchdog_set_timeout(5); /* 5ms timeout for general; 40ms timeout for imx93 */
writel(0, &wdog->win);
/* enable counter running */
writel((WDGCS_WDGE | (WDG_LPO_CLK << 8)), &wdog->cs);
if (IS_ENABLED(CONFIG_ARCH_IMX9))
writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8) | WDOG_CS_PRES |
WDGCS_INT), &wdog->cs);
else
writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8)), &wdog->cs);
/* Wait WDOG reconfiguration */
while (!(readl(&wdog->cs) & WDGCS_RCS))
@ -111,3 +163,62 @@ void reset_cpu(void)
while (1);
}
static int ulp_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct ulp_wdt_priv *priv = dev_get_priv(dev);
u64 timeout = 0;
timeout = (timeout_ms * priv->clk_rate) / 1000;
if (timeout > U16_MAX)
return -EINVAL;
ulp_watchdog_init(priv->wdog, (u16)timeout);
return 0;
}
static int ulp_wdt_reset(struct udevice *dev)
{
struct ulp_wdt_priv *priv = dev_get_priv(dev);
ulp_watchdog_reset(priv->wdog);
return 0;
}
static int ulp_wdt_probe(struct udevice *dev)
{
struct ulp_wdt_priv *priv = dev_get_priv(dev);
priv->wdog = dev_read_addr_ptr(dev);
if (!priv->wdog)
return -EINVAL;
priv->clk_rate = (u32)dev_get_driver_data(dev);
if (!priv->clk_rate)
return -EINVAL;
return 0;
}
static const struct wdt_ops ulp_wdt_ops = {
.start = ulp_wdt_start,
.reset = ulp_wdt_reset,
};
static const struct udevice_id ulp_wdt_ids[] = {
{ .compatible = "fsl,imx7ulp-wdt", .data = CLK_RATE_1KHZ },
{ .compatible = "fsl,imx8ulp-wdt", .data = CLK_RATE_1KHZ },
{ .compatible = "fsl,imx93-wdt", .data = CLK_RATE_32KHZ },
{}
};
U_BOOT_DRIVER(ulp_wdt) = {
.name = "ulp_wdt",
.id = UCLASS_WDT,
.of_match = ulp_wdt_ids,
.priv_auto = sizeof(struct ulp_wdt_priv),
.probe = ulp_wdt_probe,
.ops = &ulp_wdt_ops,
};

View File

@ -37,8 +37,8 @@ struct wdt_priv {
ulong next_reset;
/* Whether watchdog_start() has been called on the device. */
bool running;
/* No autostart */
bool noautostart;
/* autostart */
bool autostart;
struct cyclic_info *cyclic;
};
@ -72,7 +72,7 @@ static void init_watchdog_dev(struct udevice *dev)
dev->name);
}
if (!IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART) || priv->noautostart) {
if (!priv->autostart) {
printf("WDT: Not starting %s\n", dev->name);
return;
}
@ -267,19 +267,22 @@ static int wdt_pre_probe(struct udevice *dev)
* indicated by a hw_margin_ms property.
*/
ulong reset_period = 1000;
bool noautostart = false;
bool autostart = IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART);
struct wdt_priv *priv;
if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
timeout = dev_read_u32_default(dev, "timeout-sec", timeout);
reset_period = dev_read_u32_default(dev, "hw_margin_ms",
4 * reset_period) / 4;
noautostart = dev_read_bool(dev, "u-boot,noautostart");
if (dev_read_bool(dev, "u-boot,noautostart"))
autostart = false;
else if (dev_read_bool(dev, "u-boot,autostart"))
autostart = true;
}
priv = dev_get_uclass_priv(dev);
priv->timeout = timeout;
priv->reset_period = reset_period;
priv->noautostart = noautostart;
priv->autostart = autostart;
/*
* Pretend this device was last reset "long" ago so the first
* watchdog_reset will actually call its ->reset method.

View File

@ -39,6 +39,7 @@ struct cyclic_drv {
* @run_cnt: Counter of executions occurances
* @next_call: Next time in us, when the function shall be executed again
* @list: List node
* @already_warned: Flag that we've warned about exceeding CPU time usage
*/
struct cyclic_info {
void (*func)(void *ctx);
@ -50,6 +51,7 @@ struct cyclic_info {
uint64_t run_cnt;
uint64_t next_call;
struct list_head list;
bool already_warned;
};
/** Function type for cyclic functions */

View File

@ -108,6 +108,23 @@ int os_unlink(const char *pathname);
*/
void os_exit(int exit_code) __attribute__((noreturn));
/**
* os_alarm() - access to the OS alarm() system call
*/
unsigned int os_alarm(unsigned int seconds);
/**
* os_set_alarm_handler() - set handler for SIGALRM
*
* @handler: The handler function. Pass NULL for SIG_DFL.
*/
void os_set_alarm_handler(void (*handler)(int));
/**
* os_raise_sigalrm() - do raise(SIGALRM)
*/
void os_raise_sigalrm(void);
/**
* os_tty_raw() - put tty into raw mode to mimic serial console better
*