From 0ab55cb6f713abeb0b0f0cc1af2d2d16353b5ee0 Mon Sep 17 00:00:00 2001 From: Jim Liu Date: Tue, 13 Sep 2022 14:19:24 +0800 Subject: [PATCH 1/9] wdt: nuvoton: add expire function for generic reset Add expire_now function for generic sysreset request Signed-off-by: Jim Liu --- drivers/watchdog/npcm_wdt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c index 256020f5d32..e56aa0ebe1d 100644 --- a/drivers/watchdog/npcm_wdt.c +++ b/drivers/watchdog/npcm_wdt.c @@ -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, From 27836705839c4751ac28239ee6fa1f3984e46d4d Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 27 Sep 2022 11:54:03 +0200 Subject: [PATCH 2/9] watchdog: introduce a u-boot,autostart property This is a companion to u-boot,noautostart. If one has a single watchdog device that one does want to have auto-started, but several others that one doesn't, the only way currently is to set the CONFIG_WATCHDOG_AUTOSTART and then use the opt-out for the majority. The main motivation for this is to add an autostarted watchdog device to the sandbox (to test a fix) without having to set AUTOSTART in sandbox_defconfig and add the noautostart property to the existing devices. But it's also nice for symmetry, and the logic in init_watchdog_dev() becomes simpler to read because we avoid all the negations. Signed-off-by: Rasmus Villemoes --- doc/device-tree-bindings/watchdog/common.txt | 9 +++++---- drivers/watchdog/wdt-uclass.c | 15 +++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/doc/device-tree-bindings/watchdog/common.txt b/doc/device-tree-bindings/watchdog/common.txt index 9db6dd61465..d041fea2347 100644 --- a/doc/device-tree-bindings/watchdog/common.txt +++ b/doc/device-tree-bindings/watchdog/common.txt @@ -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. diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c index 3f342cd99fd..82df0ff0be5 100644 --- a/drivers/watchdog/wdt-uclass.c +++ b/drivers/watchdog/wdt-uclass.c @@ -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. From 10107efedd5ffe68b36a7d79bd561ee966cbb2f8 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 27 Sep 2022 11:54:04 +0200 Subject: [PATCH 3/9] sandbox: add SIGALRM-based watchdog device In order to test that U-Boot actually maintains the watchdog device(s) during long-running busy-loops, such as those where we wait for the user to stop autoboot, we need a watchdog device that actually does something during those loops; we cannot test that behaviour via the DM test framework. So introduce a relatively simple watchdog device which is simply based on calling the host OS' alarm() function; that has the nice property that a new call to alarm() simply sets a new deadline, and alarm(0) cancels any existing alarm. These properties are precisely what we need to implement start/reset/stop. We install our own handler so that we get a known message printed if and when the watchdog fires, and by just invoking that handler directly, we get expire_now for free. The actual calls to the various OS functions (alarm, signal, raise) need to be done in os.c, and since the driver code cannot get access to the values of SIGALRM or SIG_DFL (that would require including a host header, and that's only os.c which can do that), we cannot simply do trivial wrappers for signal() and raise(), but instead create specialized functions just for use by this driver. Apart from enabling this driver for sandbox{,64}_defconfig, also enable the wdt command which was useful for hand-testing this new driver (especially with running u-boot under strace). Signed-off-by: Rasmus Villemoes --- arch/sandbox/cpu/os.c | 17 ++++++ configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + drivers/watchdog/Kconfig | 8 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/sandbox_alarm-wdt.c | 79 ++++++++++++++++++++++++++++ include/os.h | 17 ++++++ 7 files changed, 126 insertions(+) create mode 100644 drivers/watchdog/sandbox_alarm-wdt.c diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index d6170adaf5e..5e66304e2b9 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -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; diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 48a7fd2bc22..04756729682 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -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 diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index e18e666f126..34e90674eec 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -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 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e55deaf906b..29e375e1178 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -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 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 0e2f582a5f9..446d961d7d2 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -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 diff --git a/drivers/watchdog/sandbox_alarm-wdt.c b/drivers/watchdog/sandbox_alarm-wdt.c new file mode 100644 index 00000000000..71bb5d924ea --- /dev/null +++ b/drivers/watchdog/sandbox_alarm-wdt.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +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), +}; diff --git a/include/os.h b/include/os.h index 5b353ae9d94..54874f5e0e8 100644 --- a/include/os.h +++ b/include/os.h @@ -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 * From 374d5d9971d70f1bf9443d75b5dea03701edcfeb Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 27 Sep 2022 11:54:05 +0200 Subject: [PATCH 4/9] sandbox.dtsi: add a sandbox,alarm-wdt instance In order to test that we properly handle watchdog(s) during the "wait for the user to interrupt autoboot" phase, we need a watchdog device to be watching us. Signed-off-by: Rasmus Villemoes --- arch/sandbox/dts/sandbox.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index d2db1ea5220..fc5bde4bcea 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -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>; From ddc8d36a745517fec57787ec9b17ab44c7ad973c Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 17 Oct 2022 09:00:58 +0200 Subject: [PATCH 5/9] cyclic: Don't disable cylic function upon exceeding CPU time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the migration of the watchdog infrastructure to cyclic functions it's been noticed, that at least one watchdog driver is broken now. As the execution time of it's watchdog reset function is quite long. In general it's not really necessary (right now) to disable the cyclic function upon exceeding CPU time usage. So instead of disabling the cylic function in this case, let's just print a warning once to show this potential problem to the user. Signed-off-by: Stefan Roese Suggested-by: Rasmus Villemoes Cc: Rasmus Villemoes Cc: Tom Rini Cc: Pali Rohár --- common/cyclic.c | 12 ++++++++---- include/cyclic.h | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/common/cyclic.c b/common/cyclic.c index b3c180bd1a6..7abb82c16a9 100644 --- a/common/cyclic.c +++ b/common/cyclic.c @@ -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; } } } diff --git a/include/cyclic.h b/include/cyclic.h index 76016364334..9c5c4fcc546 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -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 */ From a79f2007d018124c241a975bf522346d60e794a2 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Fri, 21 Oct 2022 16:41:15 +0800 Subject: [PATCH 6/9] ulp_wdog: Update ulp wdog driver for 32bits command To use 32bits refresh and unlock command as default, check the CMD32EN bit to select the corresponding commands. Signed-off-by: Ye Li Signed-off-by: Alice Guo Reviewed-by: Peng Fan Reviewed-by: Stefan Roese --- drivers/watchdog/ulp_wdog.c | 48 ++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/watchdog/ulp_wdog.c b/drivers/watchdog/ulp_wdog.c index ecd35ef22ac..820da0f9dd9 100644 --- a/drivers/watchdog/ulp_wdog.c +++ b/drivers/watchdog/ulp_wdog.c @@ -28,11 +28,15 @@ 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 WDGCS_CMD32EN BIT(13) #define WDGCS_FLG BIT(14) #define WDG_BUS_CLK (0x0) @@ -52,20 +56,30 @@ 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(); + 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 hw_watchdog_init(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)) @@ -75,7 +89,7 @@ void hw_watchdog_init(void) 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); + writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) | WDGCS_FLG), &wdog->cs); /* Wait WDOG reconfiguration */ while (!(readl(&wdog->cs) & WDGCS_RCS)) @@ -87,11 +101,17 @@ void hw_watchdog_init(void) 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)) @@ -101,7 +121,7 @@ void reset_cpu(void) writel(0, &wdog->win); /* enable counter running */ - writel((WDGCS_WDGE | (WDG_LPO_CLK << 8)), &wdog->cs); + writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8)), &wdog->cs); /* Wait WDOG reconfiguration */ while (!(readl(&wdog->cs) & WDGCS_RCS)) From ef0ad9b07da1595b33fa19ff96a11038c8753755 Mon Sep 17 00:00:00 2001 From: Alice Guo Date: Fri, 21 Oct 2022 16:41:16 +0800 Subject: [PATCH 7/9] watchdog: ulp_wdog: Update watchdog driver for imx93 The WDOG clocks are sourced from the fixed 32KHz (lpo_clk).When the timeout period exceeds 2 seconds, the value written to the TOVAL register is larger than 16-bit can represent. Enabling watchdog prescaler to solve this problem. Signed-off-by: Alice Guo Reviewed-by: Stefan Roese --- drivers/watchdog/ulp_wdog.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/ulp_wdog.c b/drivers/watchdog/ulp_wdog.c index 820da0f9dd9..17778587eea 100644 --- a/drivers/watchdog/ulp_wdog.c +++ b/drivers/watchdog/ulp_wdog.c @@ -36,6 +36,7 @@ struct wdog_regs { #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) @@ -89,7 +90,12 @@ void hw_watchdog_init(void) writel(0, &wdog->win); /* setting 1-kHz clock source, enable counter running, and clear interrupt */ - writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) | WDGCS_FLG), &wdog->cs); + if (IS_ENABLED(CONFIG_ARCH_IMX9)) + writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) | + WDGCS_FLG | WDOG_CS_PRES), &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)) @@ -117,11 +123,14 @@ void reset_cpu(void) 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((cmd32 | 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), &wdog->cs); + else + writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8)), &wdog->cs); /* Wait WDOG reconfiguration */ while (!(readl(&wdog->cs) & WDGCS_RCS)) From a7fd6335013148fe7e650e0c47b171d051336a31 Mon Sep 17 00:00:00 2001 From: Alice Guo Date: Fri, 21 Oct 2022 16:41:17 +0800 Subject: [PATCH 8/9] watchdog: ulp_wdog: enable watchdog interrupt on imx93 The reset source of the external PMIC on i.MX93 is WDOG_ANY PAD and the source of WDOG_ANY PAD is interrupt. Therefore, using PMIC to reset needs to enable the watchdog interrupt. Signed-off-by: Alice Guo Reviewed-by: Stefan Roese --- drivers/watchdog/ulp_wdog.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/ulp_wdog.c b/drivers/watchdog/ulp_wdog.c index 17778587eea..1b286816b57 100644 --- a/drivers/watchdog/ulp_wdog.c +++ b/drivers/watchdog/ulp_wdog.c @@ -39,6 +39,7 @@ struct wdog_regs { #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) @@ -92,7 +93,7 @@ void hw_watchdog_init(void) /* 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), &wdog->cs); + WDGCS_FLG | WDOG_CS_PRES | WDGCS_INT), &wdog->cs); else writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) | WDGCS_FLG), &wdog->cs); @@ -128,7 +129,8 @@ void reset_cpu(void) /* enable counter running */ if (IS_ENABLED(CONFIG_ARCH_IMX9)) - writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8) | WDOG_CS_PRES), &wdog->cs); + 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); From 5e112c7ca8ee45860e27f23059d9a319ba8eb6d3 Mon Sep 17 00:00:00 2001 From: Alice Guo Date: Fri, 21 Oct 2022 16:41:18 +0800 Subject: [PATCH 9/9] watchdog: ulp_wdog: add driver model for ulp watchdog driver Enable driver model for ulp watchdog timer. When CONFIG_WDT=y and the status of device node is "okay", initr_watchdog will be called and finally calls ulp_wdt_probe() and ulp_wdt_start(). Signed-off-by: Alice Guo Reviewed-by: Ye Li Reviewed-by: Stefan Roese --- drivers/watchdog/ulp_wdog.c | 94 ++++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 7 deletions(-) diff --git a/drivers/watchdog/ulp_wdog.c b/drivers/watchdog/ulp_wdog.c index 1b286816b57..e0810543048 100644 --- a/drivers/watchdog/ulp_wdog.c +++ b/drivers/watchdog/ulp_wdog.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include /* * 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 @@ -46,6 +53,9 @@ struct wdog_regs { #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 */ @@ -54,10 +64,8 @@ void hw_watchdog_set_timeout(u16 val) writel(val, &wdog->toval); } -void hw_watchdog_reset(void) +void ulp_watchdog_reset(struct wdog_regs *wdog) { - struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; - if (readl(&wdog->cs) & WDGCS_CMD32EN) { writel(REFRESH_WORD, &wdog->cnt); } else { @@ -68,9 +76,8 @@ void hw_watchdog_reset(void) } } -void hw_watchdog_init(void) +void ulp_watchdog_init(struct wdog_regs *wdog, u16 timeout) { - struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; u32 cmd32 = 0; if (readl(&wdog->cs) & WDGCS_CMD32EN) { @@ -87,7 +94,7 @@ void hw_watchdog_init(void) while (!(readl(&wdog->cs) & WDGCS_ULK)) ; - hw_watchdog_set_timeout(CONFIG_WATCHDOG_TIMEOUT_MSECS); + hw_watchdog_set_timeout(timeout); writel(0, &wdog->win); /* setting 1-kHz clock source, enable counter running, and clear interrupt */ @@ -102,7 +109,21 @@ void hw_watchdog_init(void) while (!(readl(&wdog->cs) & WDGCS_RCS)) ; - hw_watchdog_reset(); + ulp_watchdog_reset(wdog); +} + +void hw_watchdog_reset(void) +{ + struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; + + ulp_watchdog_reset(wdog); +} + +void hw_watchdog_init(void) +{ + struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; + + ulp_watchdog_init(wdog, CONFIG_WATCHDOG_TIMEOUT_MSECS); } void reset_cpu(void) @@ -142,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, +};