From 4b2e7f99cdd314263c9d172bc17193b8b6bba463 Mon Sep 17 00:00:00 2001 From: Madhuparna Bhowmik Date: Fri, 7 Aug 2020 16:59:02 +0530 Subject: drivers: watchdog: rdc321x_wdt: Fix race condition bugs In rdc321x_wdt_probe(), rdc321x_wdt_device.queue is initialized after misc_register(), hence if ioctl is called before its initialization which can call rdc321x_wdt_start() function, it will see an uninitialized value of rdc321x_wdt_device.queue, hence initialize it before misc_register(). Also, rdc321x_wdt_device.default_ticks is accessed in reset() function called from write callback, thus initialize it before misc_register(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Madhuparna Bhowmik Reviewed-by: Guenter Roeck Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20200807112902.28764-1-madhuparnabhowmik10@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rdc321x_wdt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index 57187efeb86f..f0c94ea51c3e 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -231,6 +231,8 @@ static int rdc321x_wdt_probe(struct platform_device *pdev) rdc321x_wdt_device.sb_pdev = pdata->sb_pdev; rdc321x_wdt_device.base_reg = r->start; + rdc321x_wdt_device.queue = 0; + rdc321x_wdt_device.default_ticks = ticks; err = misc_register(&rdc321x_wdt_misc); if (err < 0) { @@ -245,14 +247,11 @@ static int rdc321x_wdt_probe(struct platform_device *pdev) rdc321x_wdt_device.base_reg, RDC_WDT_RST); init_completion(&rdc321x_wdt_device.stop); - rdc321x_wdt_device.queue = 0; clear_bit(0, &rdc321x_wdt_device.inuse); timer_setup(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0); - rdc321x_wdt_device.default_ticks = ticks; - dev_info(&pdev->dev, "watchdog init success\n"); return 0; -- cgit v1.2.3 From b65762108bad73255f5f28658b6d7b79bd178be8 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 9 Aug 2020 09:19:12 +0200 Subject: watchdog: pcwd_usb: Avoid GFP_ATOMIC where it is not needed There is no need to use GFP_ATOMIC here. It is a probe function, no spinlock is taken and GFP_KERNEL is used just before and just after this 'usb_alloc_coherent()' call. Signed-off-by: Christophe JAILLET Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200809071912.742836-1-christophe.jaillet@wanadoo.fr Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pcwd_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 41a928eb91ed..1bdaf17c1d38 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -656,7 +656,7 @@ static int usb_pcwd_probe(struct usb_interface *interface, /* set up the memory buffer's */ usb_pcwd->intr_buffer = usb_alloc_coherent(udev, usb_pcwd->intr_size, - GFP_ATOMIC, &usb_pcwd->intr_dma); + GFP_KERNEL, &usb_pcwd->intr_dma); if (!usb_pcwd->intr_buffer) { pr_err("Out of memory\n"); goto error; -- cgit v1.2.3 From beaabe0e2356648dfd08b52a78350bb531122ba8 Mon Sep 17 00:00:00 2001 From: Hanspeter Portner Date: Thu, 27 Aug 2020 12:59:40 +0200 Subject: watchdog: it87_wdt: add IT8772 ID IT8772 watchdog works as in IT872x Tested on SHAREVDI K6-F12 board. Signed-off-by: Hanspeter Portner Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200827105940.2835910-1-hanspeter.portner@livesystems.ch Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/it87_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index f3bf3ea50e39..2dac0ba551ce 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -15,7 +15,7 @@ * Support of the watchdog timers, which are available on * IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, - * and IT8783. + * IT8772 and IT8783. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -66,6 +66,7 @@ #define IT8721_ID 0x8721 #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ #define IT8728_ID 0x8728 +#define IT8772_ID 0x8772 #define IT8783_ID 0x8783 #define IT8786_ID 0x8786 @@ -294,6 +295,7 @@ static int __init it87_wdt_init(void) case IT8720_ID: case IT8721_ID: case IT8728_ID: + case IT8772_ID: case IT8783_ID: case IT8786_ID: max_units = 65535; -- cgit v1.2.3 From 743949466e55732ae7eef038abf291b9b39d3307 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 31 Jul 2020 07:03:32 +0800 Subject: watchdog: imx7ulp: Strictly follow the sequence for wdog operations According to reference manual, the i.MX7ULP WDOG's operations except refresh should follow below sequence: 1. disable global interrupts; 2. unlock the wdog and wait unlock bit set; 3. reconfigure the wdog and wait for reconfiguration bit set; 4. enabel global interrupts. Strictly follow the recommended sequence can make it more robust. Signed-off-by: Anson Huang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1596150213-31638-1-git-send-email-Anson.Huang@nxp.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/imx7ulp_wdt.c | 74 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index 7993c8c41b3a..badfc0be3f85 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #define DEFAULT_TIMEOUT 60 #define MAX_TIMEOUT 128 #define WDOG_CLOCK_RATE 1000 +#define WDOG_WAIT_TIMEOUT 20 static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0000); @@ -48,17 +50,40 @@ struct imx7ulp_wdt_device { struct clk *clk; }; -static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable) +static int imx7ulp_wdt_wait(void __iomem *base, u32 mask) +{ + u32 val = readl(base + WDOG_CS); + + if (!(val & mask) && readl_poll_timeout_atomic(base + WDOG_CS, val, + val & mask, 0, + WDOG_WAIT_TIMEOUT)) + return -ETIMEDOUT; + + return 0; +} + +static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable) { struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); u32 val = readl(wdt->base + WDOG_CS); + int ret; + local_irq_disable(); writel(UNLOCK, wdt->base + WDOG_CNT); + ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK); + if (ret) + goto enable_out; if (enable) writel(val | WDOG_CS_EN, wdt->base + WDOG_CS); else writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS); + imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS); + +enable_out: + local_irq_enable(); + + return ret; } static bool imx7ulp_wdt_is_enabled(void __iomem *base) @@ -79,17 +104,12 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog) static int imx7ulp_wdt_start(struct watchdog_device *wdog) { - - imx7ulp_wdt_enable(wdog, true); - - return 0; + return imx7ulp_wdt_enable(wdog, true); } static int imx7ulp_wdt_stop(struct watchdog_device *wdog) { - imx7ulp_wdt_enable(wdog, false); - - return 0; + return imx7ulp_wdt_enable(wdog, false); } static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog, @@ -97,22 +117,37 @@ static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog, { struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); u32 val = WDOG_CLOCK_RATE * timeout; + int ret; + local_irq_disable(); writel(UNLOCK, wdt->base + WDOG_CNT); + ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK); + if (ret) + goto timeout_out; writel(val, wdt->base + WDOG_TOVAL); + imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS); wdog->timeout = timeout; - return 0; +timeout_out: + local_irq_enable(); + + return ret; } static int imx7ulp_wdt_restart(struct watchdog_device *wdog, unsigned long action, void *data) { struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); + int ret; + + ret = imx7ulp_wdt_enable(wdog, true); + if (ret) + return ret; - imx7ulp_wdt_enable(wdog, true); - imx7ulp_wdt_set_timeout(&wdt->wdd, 1); + ret = imx7ulp_wdt_set_timeout(&wdt->wdd, 1); + if (ret) + return ret; /* wait for wdog to fire */ while (true) @@ -136,19 +171,30 @@ static const struct watchdog_info imx7ulp_wdt_info = { WDIOF_MAGICCLOSE, }; -static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) +static int imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) { u32 val; + int ret; + local_irq_disable(); /* unlock the wdog for reconfiguration */ writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT); writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT); + ret = imx7ulp_wdt_wait(base, WDOG_CS_ULK); + if (ret) + goto init_out; /* set an initial timeout value in TOVAL */ writel(timeout, base + WDOG_TOVAL); /* enable 32bit command sequence and reconfigure */ val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE; writel(val, base + WDOG_CS); + imx7ulp_wdt_wait(base, WDOG_CS_RCS); + +init_out: + local_irq_enable(); + + return ret; } static void imx7ulp_wdt_action(void *data) @@ -199,7 +245,9 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev) watchdog_stop_on_reboot(wdog); watchdog_stop_on_unregister(wdog); watchdog_set_drvdata(wdog, imx7ulp_wdt); - imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE); + ret = imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE); + if (ret) + return ret; return devm_watchdog_register_device(dev, wdog); } -- cgit v1.2.3 From 5afb6d203d0293512aa2c6ae098274a2a4f6ed02 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Mon, 24 Aug 2020 10:40:01 +0800 Subject: watchdog: Fix memleak in watchdog_cdev_register When watchdog_kworker is NULL, we should free wd_data before the function returns to prevent memleak. Fixes: 664a39236e718 ("watchdog: Introduce hardware maximum heartbeat in watchdog core") Signed-off-by: Dinghao Liu Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200824024001.25474-1-dinghao.liu@zju.edu.cn Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 6798addabd5a..785270ee337c 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -994,8 +994,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) wd_data->wdd = wdd; wdd->wd_data = wd_data; - if (IS_ERR_OR_NULL(watchdog_kworker)) + if (IS_ERR_OR_NULL(watchdog_kworker)) { + kfree(wd_data); return -ENODEV; + } device_initialize(&wd_data->dev); wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id); -- cgit v1.2.3 From 937425d4cd3ae4e2882b41e332bbbab616bcf0ad Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Mon, 24 Aug 2020 11:12:30 +0800 Subject: watchdog: Use put_device on error We should use put_device() instead of freeing device directly after device_initialize(). Fixes: cb36e29bb0e4b ("watchdog: initialize device before misc_register") Signed-off-by: Dinghao Liu Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200824031230.31050-1-dinghao.liu@zju.edu.cn Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 785270ee337c..bcf01af3fa6a 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -1023,7 +1023,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) pr_err("%s: a legacy watchdog module is probably present.\n", wdd->info->identity); old_wd_data = NULL; - kfree(wd_data); + put_device(&wd_data->dev); return err; } } -- cgit v1.2.3 From 2c904630e9b18c58a2261193e6d85a3d79d87033 Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Thu, 27 Aug 2020 14:21:54 +0800 Subject: watchdog: remove unneeded inclusion of There has been no reference to "struct sched_param" since commit 94beddacb53c ("sched,watchdog: Convert to sched_set_fifo()"), so there's no need to include any more, delete it. Signed-off-by: Zhen Lei Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200827062154.1847-1-thunder.leizhen@huawei.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index bcf01af3fa6a..2946f3a63110 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -43,8 +43,6 @@ #include /* For watchdog specific items */ #include /* For copy_to_user/put_user/... */ -#include /* For struct sched_param */ - #include "watchdog_core.h" #include "watchdog_pretimeout.h" -- cgit v1.2.3 From dab1122159a5e7be58e5f64099a8fe7d82ed834f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:31:39 +0200 Subject: watchdog: cadence: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200901153141.18960-1-krzk@kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/cadence_wdt.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 672b184da875..bc99e9164930 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -334,12 +334,9 @@ static int cdns_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(cdns_wdt_device, wdt); wdt->clk = devm_clk_get(dev, NULL); - if (IS_ERR(wdt->clk)) { - ret = PTR_ERR(wdt->clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "input clock not found\n"); - return ret; - } + if (IS_ERR(wdt->clk)) + return dev_err_probe(dev, PTR_ERR(wdt->clk), + "input clock not found\n"); ret = clk_prepare_enable(wdt->clk); if (ret) { -- cgit v1.2.3 From fc77204001d085050564bd6bd064e5cd51de2527 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:31:40 +0200 Subject: watchdog: davinci: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200901153141.18960-2-krzk@kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/davinci_wdt.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 2b3f3cd382ef..e6eaba6bae5b 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -206,12 +206,9 @@ static int davinci_wdt_probe(struct platform_device *pdev) return -ENOMEM; davinci_wdt->clk = devm_clk_get(dev, NULL); - - if (IS_ERR(davinci_wdt->clk)) { - if (PTR_ERR(davinci_wdt->clk) != -EPROBE_DEFER) - dev_err(dev, "failed to get clock node\n"); - return PTR_ERR(davinci_wdt->clk); - } + if (IS_ERR(davinci_wdt->clk)) + return dev_err_probe(dev, PTR_ERR(davinci_wdt->clk), + "failed to get clock node\n"); ret = clk_prepare_enable(davinci_wdt->clk); if (ret) { -- cgit v1.2.3 From d342951bcda6cd190309138385d5935ab1d08f92 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:31:41 +0200 Subject: watchdog: rti: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200901153141.18960-3-krzk@kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rti_wdt.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index 705e8f7523e8..836319cbaca9 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -205,11 +205,8 @@ static int rti_wdt_probe(struct platform_device *pdev) return -ENOMEM; clk = clk_get(dev, NULL); - if (IS_ERR(clk)) { - if (PTR_ERR(clk) != -EPROBE_DEFER) - dev_err(dev, "failed to get clock\n"); - return PTR_ERR(clk); - } + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n"); wdt->freq = clk_get_rate(clk); @@ -230,11 +227,8 @@ static int rti_wdt_probe(struct platform_device *pdev) pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "runtime pm failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "runtime pm failed\n"); platform_set_drvdata(pdev, wdt); -- cgit v1.2.3 From 0cfbe17959b69896a72dffbc2d37280859b0d086 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 31 Jul 2020 07:03:33 +0800 Subject: watchdog: imx7ulp: Watchdog should continue running for wait/stop mode When kernel idle, system will enter wait/stop mode, wdog should continue running in this scenario, and the refresh thread can wake up system from wait/stop mode. Signed-off-by: Anson Huang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1596150213-31638-2-git-send-email-Anson.Huang@nxp.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/imx7ulp_wdt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index badfc0be3f85..922b60374295 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -22,6 +22,8 @@ #define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT) #define WDOG_CS_EN BIT(7) #define WDOG_CS_UPDATE BIT(5) +#define WDOG_CS_WAIT BIT(1) +#define WDOG_CS_STOP BIT(0) #define WDOG_CNT 0x4 #define WDOG_TOVAL 0x8 @@ -187,7 +189,8 @@ static int imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) /* set an initial timeout value in TOVAL */ writel(timeout, base + WDOG_TOVAL); /* enable 32bit command sequence and reconfigure */ - val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE; + val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE | + WDOG_CS_WAIT | WDOG_CS_STOP; writel(val, base + WDOG_CS); imx7ulp_wdt_wait(base, WDOG_CS_RCS); -- cgit v1.2.3 From 962085a2bb823d8c88c6bc706c654648407e6408 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 8 Sep 2020 11:56:15 +0200 Subject: watchdog: renesas_wdt: support handover from bootloader Support an already running watchdog by checking its enable bit and set up the status accordingly before registering the device. Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200908095615.31376-1-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/renesas_wdt.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 00662a8e039c..47fce4de0110 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -194,6 +194,7 @@ static int rwdt_probe(struct platform_device *pdev) struct clk *clk; unsigned long clks_per_sec; int ret, i; + u8 csra; if (rwdt_blacklisted(dev)) return -ENODEV; @@ -213,8 +214,8 @@ static int rwdt_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); priv->clk_rate = clk_get_rate(clk); - priv->wdev.bootstatus = (readb_relaxed(priv->base + RWTCSRA) & - RWTCSRA_WOVF) ? WDIOF_CARDRESET : 0; + csra = readb_relaxed(priv->base + RWTCSRA); + priv->wdev.bootstatus = csra & RWTCSRA_WOVF ? WDIOF_CARDRESET : 0; pm_runtime_put(dev); if (!priv->clk_rate) { @@ -252,6 +253,13 @@ static int rwdt_probe(struct platform_device *pdev) /* This overrides the default timeout only if DT configuration was found */ watchdog_init_timeout(&priv->wdev, 0, dev); + /* Check if FW enabled the watchdog */ + if (csra & RWTCSRA_TME) { + /* Ensure properly initialized dividers */ + rwdt_start(&priv->wdev); + set_bit(WDOG_HW_RUNNING, &priv->wdev.status); + } + ret = watchdog_register_device(&priv->wdev); if (ret < 0) goto out_pm_disable; -- cgit v1.2.3 From 08c619b4923056b5dd2d5045757468c76ad0e3fe Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 10 Sep 2020 09:31:08 -0700 Subject: watchdog: sp5100: Fix definition of EFCH_PM_DECODEEN3 EFCH_PM_DECODEEN3 is supposed to access DECODEEN register bits 24..31, in other words the register at byte offset 3. Cc: Jan Kiszka Fixes: 887d2ec51e34b ("watchdog: sp5100_tco: Add support for recent FCH versions") Tested-by: Jan Kiszka Link: https://lore.kernel.org/r/20200910163109.235136-1-linux@roeck-us.net Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index 87eaf357ae01..adf015aa4126 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -70,7 +70,7 @@ #define EFCH_PM_DECODEEN_WDT_TMREN BIT(7) -#define EFCH_PM_DECODEEN3 0x00 +#define EFCH_PM_DECODEEN3 0x03 #define EFCH_PM_DECODEEN_SECOND_RES GENMASK(1, 0) #define EFCH_PM_WATCHDOG_DISABLE ((u8)GENMASK(3, 2)) -- cgit v1.2.3 From 09da89abe36a3cb3485ed765ec37ac50227e28b5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 10 Sep 2020 09:31:09 -0700 Subject: watchdog: sp5100_tco: Enable watchdog on Family 17h devices if disabled On Family 17h (Ryzen) devices, the WatchdogTmrEn bit of PmDecodeEn not only enables watchdog memory decoding at 0xfeb00000, it also enables the watchdog hardware itself. Use this information to enable the watchdog if it is not already enabled. Cc: Jan Kiszka Tested-by: Jan Kiszka Link: https://lore.kernel.org/r/20200910163109.235136-2-linux@roeck-us.net Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 85e9664318c9..a730ecbf78cd 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -17,6 +17,12 @@ * AMD Publication 51192 "AMD Bolton FCH Register Reference Guide" * AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG) * for AMD Family 16h Models 30h-3Fh Processors" + * AMD Publication 55570-B1-PUB "Processor Programming Reference (PPR) + * for AMD Family 17h Model 18h, Revision B1 + * Processors (PUB) + * AMD Publication 55772-A1-PUB "Processor Programming Reference (PPR) + * for AMD Family 17h Model 20h, Revision A1 + * Processors (PUB) */ /* @@ -241,6 +247,18 @@ static int sp5100_tco_setupdevice(struct device *dev, break; case efch: dev_name = SB800_DEVNAME; + /* + * On Family 17h devices, the EFCH_PM_DECODEEN_WDT_TMREN bit of + * EFCH_PM_DECODEEN not only enables the EFCH_PM_WDT_ADDR memory + * region, it also enables the watchdog itself. + */ + if (boot_cpu_data.x86 == 0x17) { + val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN); + if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) { + sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN, 0xff, + EFCH_PM_DECODEEN_WDT_TMREN); + } + } val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN); if (val & EFCH_PM_DECODEEN_WDT_TMREN) mmio_addr = EFCH_PM_WDT_ADDR; -- cgit v1.2.3 From c113739ce5d3b2fcdad3a7b6650dd4bcbf56118a Mon Sep 17 00:00:00 2001 From: Hanspeter Portner Date: Fri, 4 Sep 2020 23:16:39 +0200 Subject: watchdog: it87_wdt: add IT8784 ID IT8784 watchdog works as in IT878x Tested on SHAREVDY K10 board. Signed-off-by: Hanspeter Portner Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200904211639.18787-2-dev@open-music-kontrollers.ch Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/it87_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index 2dac0ba551ce..2b4831842162 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -15,7 +15,7 @@ * Support of the watchdog timers, which are available on * IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, - * IT8772 and IT8783. + * IT8772, IT8783 and IT8784. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -68,6 +68,7 @@ #define IT8728_ID 0x8728 #define IT8772_ID 0x8772 #define IT8783_ID 0x8783 +#define IT8784_ID 0x8784 #define IT8786_ID 0x8786 /* GPIO Configuration Registers LDN=0x07 */ @@ -297,6 +298,7 @@ static int __init it87_wdt_init(void) case IT8728_ID: case IT8772_ID: case IT8783_ID: + case IT8784_ID: case IT8786_ID: max_units = 65535; break; -- cgit v1.2.3 From 7e65fa4a49a6d46fea32a4e6cf8c6dd27c061890 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Mon, 5 Oct 2020 11:30:11 +0900 Subject: watchdog: bindings: Add binding documentation for Toshiba Visconti watchdog device Add documentation for the binding of Toshiba Visconti SoC's watchdog. Signed-off-by: Nobuhiro Iwamatsu Reviewed-by: Punit Agrawal Reviewed-by: Rob Herring Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20201005023012.603026-2-nobuhiro1.iwamatsu@toshiba.co.jp Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../bindings/watchdog/toshiba,visconti-wdt.yaml | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/toshiba,visconti-wdt.yaml diff --git a/Documentation/devicetree/bindings/watchdog/toshiba,visconti-wdt.yaml b/Documentation/devicetree/bindings/watchdog/toshiba,visconti-wdt.yaml new file mode 100644 index 000000000000..690e19ce4b87 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/toshiba,visconti-wdt.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2020 Toshiba Electronic Devices & Storage Corporation +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/watchdog/toshiba,visconti-wdt.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Toshiba Visconti SoCs PIUWDT Watchdog timer + +maintainers: + - Nobuhiro Iwamatsu + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + enum: + - toshiba,visconti-wdt + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + timeout-sec: true + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + wdt_clk: wdt-clk { + compatible = "fixed-clock"; + clock-frequency = <150000000>; + #clock-cells = <0>; + }; + + watchdog@28330000 { + compatible = "toshiba,visconti-wdt"; + reg = <0 0x28330000 0 0x1000>; + clocks = <&wdt_clk>; + timeout-sec = <20>; + }; + }; -- cgit v1.2.3 From c5b8e4644607b0319fd86a36fb0caad54ee2021e Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Mon, 5 Oct 2020 11:30:12 +0900 Subject: watchdog: Add Toshiba Visconti watchdog driver Add the watchdog driver for Toshiba Visconti series. Signed-off-by: Nobuhiro Iwamatsu Reviewed-by: Punit Agrawal Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20201005023012.603026-3-nobuhiro1.iwamatsu@toshiba.co.jp Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/visconti_wdt.c | 195 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 drivers/watchdog/visconti_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ab7aad5a1e69..0cb078ce5e9d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1004,6 +1004,14 @@ config PM8916_WATCHDOG Say Y here to include support watchdog timer embedded into the pm8916 module. +config VISCONTI_WATCHDOG + tristate "Toshiba Visconti series watchdog support" + depends on ARCH_VISCONTI || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer in Toshiba + Visconti SoCs. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 97bed1d3d97c..a7747e76fd29 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o +obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/visconti_wdt.c b/drivers/watchdog/visconti_wdt.c new file mode 100644 index 000000000000..83ef55e66ca8 --- /dev/null +++ b/drivers/watchdog/visconti_wdt.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 TOSHIBA CORPORATION + * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation + * Copyright (c) 2020 Nobuhiro Iwamatsu + */ + +#include +#include +#include +#include +#include +#include +#include + +#define WDT_CNT 0x00 +#define WDT_MIN 0x04 +#define WDT_MAX 0x08 +#define WDT_CTL 0x0c +#define WDT_CMD 0x10 +#define WDT_CMD_CLEAR 0x4352 +#define WDT_CMD_START_STOP 0x5354 +#define WDT_DIV 0x30 + +#define VISCONTI_WDT_FREQ 2000000 /* 2MHz */ +#define WDT_DEFAULT_TIMEOUT 10U /* in seconds */ + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC( + nowayout, + "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT)")"); + +struct visconti_wdt_priv { + struct watchdog_device wdev; + void __iomem *base; + u32 div; +}; + +static int visconti_wdt_start(struct watchdog_device *wdev) +{ + struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev); + u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ; + + writel(priv->div, priv->base + WDT_DIV); + writel(0, priv->base + WDT_MIN); + writel(timeout, priv->base + WDT_MAX); + writel(0, priv->base + WDT_CTL); + writel(WDT_CMD_START_STOP, priv->base + WDT_CMD); + + return 0; +} + +static int visconti_wdt_stop(struct watchdog_device *wdev) +{ + struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev); + + writel(1, priv->base + WDT_CTL); + writel(WDT_CMD_START_STOP, priv->base + WDT_CMD); + + return 0; +} + +static int visconti_wdt_ping(struct watchdog_device *wdd) +{ + struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdd); + + writel(WDT_CMD_CLEAR, priv->base + WDT_CMD); + + return 0; +} + +static unsigned int visconti_wdt_get_timeleft(struct watchdog_device *wdev) +{ + struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev); + u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ; + u32 cnt = readl(priv->base + WDT_CNT); + + if (timeout <= cnt) + return 0; + timeout -= cnt; + + return timeout / VISCONTI_WDT_FREQ; +} + +static int visconti_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout) +{ + u32 val; + struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev); + + wdev->timeout = timeout; + val = wdev->timeout * VISCONTI_WDT_FREQ; + + /* Clear counter before setting timeout because WDT expires */ + writel(WDT_CMD_CLEAR, priv->base + WDT_CMD); + writel(val, priv->base + WDT_MAX); + + return 0; +} + +static const struct watchdog_info visconti_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .identity = "Visconti Watchdog", +}; + +static const struct watchdog_ops visconti_wdt_ops = { + .owner = THIS_MODULE, + .start = visconti_wdt_start, + .stop = visconti_wdt_stop, + .ping = visconti_wdt_ping, + .get_timeleft = visconti_wdt_get_timeleft, + .set_timeout = visconti_wdt_set_timeout, +}; + +static void visconti_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +static int visconti_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdev; + struct visconti_wdt_priv *priv; + struct device *dev = &pdev->dev; + struct clk *clk; + int ret; + unsigned long clk_freq; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n"); + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "Could not enable clock\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, visconti_clk_disable_unprepare, clk); + if (ret) + return ret; + + clk_freq = clk_get_rate(clk); + if (!clk_freq) + return -EINVAL; + + priv->div = clk_freq / VISCONTI_WDT_FREQ; + + /* Initialize struct watchdog_device. */ + wdev = &priv->wdev; + wdev->info = &visconti_wdt_info; + wdev->ops = &visconti_wdt_ops; + wdev->parent = dev; + wdev->min_timeout = 1; + wdev->max_timeout = 0xffffffff / VISCONTI_WDT_FREQ; + wdev->timeout = min(wdev->max_timeout, WDT_DEFAULT_TIMEOUT); + + watchdog_set_drvdata(wdev, priv); + watchdog_set_nowayout(wdev, nowayout); + watchdog_stop_on_unregister(wdev); + + /* This overrides the default timeout only if DT configuration was found */ + ret = watchdog_init_timeout(wdev, 0, dev); + if (ret) + dev_warn(dev, "Specified timeout value invalid, using default\n"); + + return devm_watchdog_register_device(dev, wdev); +} + +static const struct of_device_id visconti_wdt_of_match[] = { + { .compatible = "toshiba,visconti-wdt", }, + {} +}; +MODULE_DEVICE_TABLE(of, visconti_wdt_of_match); + +static struct platform_driver visconti_wdt_driver = { + .driver = { + .name = "visconti_wdt", + .of_match_table = visconti_wdt_of_match, + }, + .probe = visconti_wdt_probe, +}; +module_platform_driver(visconti_wdt_driver); + +MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver"); +MODULE_AUTHOR("Nobuhiro Iwamatsu