diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-21 11:28:43 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-21 11:28:43 -0700 |
commit | f804b3159482eedbb4250b1e9248c308fb63b805 (patch) | |
tree | bec45a23c8322424e1a57302dc640fa17882c777 /drivers/watchdog | |
parent | b7769c45b8d95f1c70b9dec38c8de8ad229ce222 (diff) | |
parent | c5b8e4644607b0319fd86a36fb0caad54ee2021e (diff) |
Merge tag 'linux-watchdog-5.10-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
- Add Toshiba Visconti watchdog driver
- it87_wdt: add IT8772 + IT8784
- several fixes and improvements
* tag 'linux-watchdog-5.10-rc1' of git://www.linux-watchdog.org/linux-watchdog:
watchdog: Add Toshiba Visconti watchdog driver
watchdog: bindings: Add binding documentation for Toshiba Visconti watchdog device
watchdog: it87_wdt: add IT8784 ID
watchdog: sp5100_tco: Enable watchdog on Family 17h devices if disabled
watchdog: sp5100: Fix definition of EFCH_PM_DECODEEN3
watchdog: renesas_wdt: support handover from bootloader
watchdog: imx7ulp: Watchdog should continue running for wait/stop mode
watchdog: rti: Simplify with dev_err_probe()
watchdog: davinci: Simplify with dev_err_probe()
watchdog: cadence: Simplify with dev_err_probe()
watchdog: remove unneeded inclusion of <uapi/linux/sched/types.h>
watchdog: Use put_device on error
watchdog: Fix memleak in watchdog_cdev_register
watchdog: imx7ulp: Strictly follow the sequence for wdog operations
watchdog: it87_wdt: add IT8772 ID
watchdog: pcwd_usb: Avoid GFP_ATOMIC where it is not needed
drivers: watchdog: rdc321x_wdt: Fix race condition bugs
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 8 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/cadence_wdt.c | 9 | ||||
-rw-r--r-- | drivers/watchdog/davinci_wdt.c | 9 | ||||
-rw-r--r-- | drivers/watchdog/imx7ulp_wdt.c | 79 | ||||
-rw-r--r-- | drivers/watchdog/it87_wdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/pcwd_usb.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/rdc321x_wdt.c | 5 | ||||
-rw-r--r-- | drivers/watchdog/renesas_wdt.c | 12 | ||||
-rw-r--r-- | drivers/watchdog/rti_wdt.c | 14 | ||||
-rw-r--r-- | drivers/watchdog/sp5100_tco.c | 18 | ||||
-rw-r--r-- | drivers/watchdog/sp5100_tco.h | 2 | ||||
-rw-r--r-- | drivers/watchdog/visconti_wdt.c | 195 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 8 |
14 files changed, 320 insertions, 48 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ba698340ed6f..21865e27499a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1015,6 +1015,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 aa6e41126901..071a2e50be98 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/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) { 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) { diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index 7993c8c41b3a..922b60374295 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -5,6 +5,7 @@ #include <linux/clk.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -21,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 @@ -36,6 +39,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 +52,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 +106,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 +119,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 +173,31 @@ 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; + 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); + +init_out: + local_irq_enable(); + + return ret; } static void imx7ulp_wdt_action(void *data) @@ -199,7 +248,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); } diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index f3bf3ea50e39..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, - * and IT8783. + * IT8772, IT8783 and IT8784. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -66,7 +66,9 @@ #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 IT8784_ID 0x8784 #define IT8786_ID 0x8786 /* GPIO Configuration Registers LDN=0x07 */ @@ -294,7 +296,9 @@ static int __init it87_wdt_init(void) case IT8720_ID: case IT8721_ID: case IT8728_ID: + case IT8772_ID: case IT8783_ID: + case IT8784_ID: case IT8786_ID: max_units = 65535; break; 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; 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; 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; 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); 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; 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)) 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 <nobuhiro1.iwamatsu@toshiba.co.jp> + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +#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 <nobuhiro1.iwamatsu@toshiba.co.jp"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 6798addabd5a..2946f3a63110 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -43,8 +43,6 @@ #include <linux/watchdog.h> /* For watchdog specific items */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ -#include <uapi/linux/sched/types.h> /* For struct sched_param */ - #include "watchdog_core.h" #include "watchdog_pretimeout.h" @@ -994,8 +992,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); @@ -1021,7 +1021,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; } } |