From d2fc8db691bf3197d43b2afb553311a9bf257bff Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 9 Mar 2018 15:58:19 -0600 Subject: watchdog: aspeed: Fix translation of reset mode to ctrl register Assert RESET_SYSTEM bit for any reset and set MODE field from reset type. The watchdog control register has a RESET_SYSTEM bit that is really closer to activate a reset, and RESET_SYSTEM_MODE field that chooses how much to reset. Before this patch, a node without these optional property would do a SOC reset, but a node with properties requesting a cpu or SOC reset would do nothing and a node requesting a system reset would do a SOC reset. Fixes: b7f0b8ad25f3 ("drivers/watchdog: ASPEED reference dev tree properties for config") Signed-off-by: Milton Miller Signed-off-by: Eddie James Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/aspeed_wdt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index ca5b91e2eb92..d1987d63b37c 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -232,11 +232,14 @@ static int aspeed_wdt_probe(struct platform_device *pdev) wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | WDT_CTRL_RESET_SYSTEM; } else { if (!strcmp(reset_type, "cpu")) - wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU; + wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU | + WDT_CTRL_RESET_SYSTEM; else if (!strcmp(reset_type, "soc")) - wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC; + wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | + WDT_CTRL_RESET_SYSTEM; else if (!strcmp(reset_type, "system")) - wdt->ctrl |= WDT_CTRL_RESET_SYSTEM; + wdt->ctrl |= WDT_CTRL_RESET_MODE_FULL_CHIP | + WDT_CTRL_RESET_SYSTEM; else if (strcmp(reset_type, "none")) return -EINVAL; } -- cgit v1.2.3 From 3c578cd4bc52b6e65d65be1abad9a8aa489ec207 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 9 Mar 2018 00:21:48 +0300 Subject: watchdog: sprd_wdt: Fix error handling in sprd_wdt_enable() If clk_prepare_enable(wdt->rtc_enable) fails, wdt->enable clock is left enabled. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sprd_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index a8b280ff33e0..b4d484a42b70 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -154,8 +154,10 @@ static int sprd_wdt_enable(struct sprd_wdt *wdt) if (ret) return ret; ret = clk_prepare_enable(wdt->rtc_enable); - if (ret) + if (ret) { + clk_disable_unprepare(wdt->enable); return ret; + } sprd_wdt_unlock(wdt->base); val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); -- cgit v1.2.3 From a81abbb412341e9e3b2d42ed7d310cf71fbb84a8 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 9 Mar 2018 19:46:06 -0800 Subject: watchdog: dw: RMW the control register RK3399 has rst_pulse_length in CONTROL_REG[4:2], determining the length of pulse to issue for system reset. We shouldn't clobber this value, because that might make the system reset ineffective. On RK3399, we're seeing that a value of 000b (meaning 2 cycles) yields an unreliable (partial?) reset, and so we only fully reset after the watchdog fires a second time. If we retain the system default (010b, or 8 clock cycles), then the watchdog reset is much more reliable. Read-modify-write retains the system value and improves reset reliability. It seems we were intentionally clobbering the response mode previously, to ensure we performed a system reset (we don't support an interrupt notification), so retain that explicitly. Signed-off-by: Brian Norris Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index c2f4ff516230..918357bccf5e 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -34,6 +34,7 @@ #define WDOG_CONTROL_REG_OFFSET 0x00 #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 +#define WDOG_CONTROL_REG_RESP_MODE_MASK 0x02 #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 #define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4 #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 @@ -121,14 +122,23 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) return 0; } +static void dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt) +{ + u32 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + + /* Disable interrupt mode; always perform system reset. */ + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; + /* Enable watchdog. */ + val |= WDOG_CONTROL_REG_WDT_EN_MASK; + writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); +} + static int dw_wdt_start(struct watchdog_device *wdd) { struct dw_wdt *dw_wdt = to_dw_wdt(wdd); dw_wdt_set_timeout(wdd, wdd->timeout); - - writel(WDOG_CONTROL_REG_WDT_EN_MASK, - dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + dw_wdt_arm_system_reset(dw_wdt); return 0; } @@ -152,16 +162,13 @@ static int dw_wdt_restart(struct watchdog_device *wdd, unsigned long action, void *data) { struct dw_wdt *dw_wdt = to_dw_wdt(wdd); - u32 val; writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); - val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); - if (val & WDOG_CONTROL_REG_WDT_EN_MASK) + if (dw_wdt_is_enabled(dw_wdt)) writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); else - writel(WDOG_CONTROL_REG_WDT_EN_MASK, - dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + dw_wdt_arm_system_reset(dw_wdt); /* wait for reset to assert... */ mdelay(500); -- cgit v1.2.3 From 8c088370c2a684acad45e0035d9f2dfd85087db0 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 9 Mar 2018 19:46:07 -0800 Subject: watchdog: dw: save/restore control and timeout across suspend/resume Some platforms lose this state in suspend. It should be safe to do this unconditionally. Signed-off-by: Brian Norris Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 918357bccf5e..501aebb5b81f 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -57,6 +57,9 @@ struct dw_wdt { unsigned long rate; struct watchdog_device wdd; struct reset_control *rst; + /* Save/restore */ + u32 control; + u32 timeout; }; #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) @@ -205,6 +208,9 @@ static int dw_wdt_suspend(struct device *dev) { struct dw_wdt *dw_wdt = dev_get_drvdata(dev); + dw_wdt->control = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + dw_wdt->timeout = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + clk_disable_unprepare(dw_wdt->clk); return 0; @@ -218,6 +224,9 @@ static int dw_wdt_resume(struct device *dev) if (err) return err; + writel(dw_wdt->timeout, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + writel(dw_wdt->control, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + dw_wdt_ping(&dw_wdt->wdd); return 0; -- cgit v1.2.3