diff options
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 19 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
-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/sl28cpld_wdt.c | 229 | ||||
-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 |
15 files changed, 561 insertions, 48 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ab7aad5a1e69..21865e27499a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -340,6 +340,17 @@ config MLX_WDT To compile this driver as a module, choose M here: the module will be called mlx-wdt. +config SL28CPLD_WATCHDOG + tristate "Kontron sl28cpld Watchdog" + depends on MFD_SL28CPLD || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + on the Kontron sl28 CPLD. + + To compile this driver as a module, choose M here: the + module will be called sl28cpld_wdt. + # ALPHA Architecture # ARM Architecture @@ -1004,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 97bed1d3d97c..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 @@ -225,3 +226,4 @@ obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o +obj-$(CONFIG_SL28CPLD_WATCHDOG) += sl28cpld_wdt.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/sl28cpld_wdt.c b/drivers/watchdog/sl28cpld_wdt.c new file mode 100644 index 000000000000..a45047d8d9ab --- /dev/null +++ b/drivers/watchdog/sl28cpld_wdt.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * sl28cpld watchdog driver + * + * Copyright 2020 Kontron Europe GmbH + */ + +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/watchdog.h> + +/* + * Watchdog timer block registers. + */ +#define WDT_CTRL 0x00 +#define WDT_CTRL_EN BIT(0) +#define WDT_CTRL_LOCK BIT(2) +#define WDT_CTRL_ASSERT_SYS_RESET BIT(6) +#define WDT_CTRL_ASSERT_WDT_TIMEOUT BIT(7) +#define WDT_TIMEOUT 0x01 +#define WDT_KICK 0x02 +#define WDT_KICK_VALUE 0x6b +#define WDT_COUNT 0x03 + +#define WDT_DEFAULT_TIMEOUT 10 + +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) ")"); + +static int timeout; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds"); + +struct sl28cpld_wdt { + struct watchdog_device wdd; + struct regmap *regmap; + u32 offset; + bool assert_wdt_timeout; +}; + +static int sl28cpld_wdt_ping(struct watchdog_device *wdd) +{ + struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_write(wdt->regmap, wdt->offset + WDT_KICK, + WDT_KICK_VALUE); +} + +static int sl28cpld_wdt_start(struct watchdog_device *wdd) +{ + struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned int val; + + val = WDT_CTRL_EN | WDT_CTRL_ASSERT_SYS_RESET; + if (wdt->assert_wdt_timeout) + val |= WDT_CTRL_ASSERT_WDT_TIMEOUT; + if (nowayout) + val |= WDT_CTRL_LOCK; + + return regmap_update_bits(wdt->regmap, wdt->offset + WDT_CTRL, + val, val); +} + +static int sl28cpld_wdt_stop(struct watchdog_device *wdd) +{ + struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->regmap, wdt->offset + WDT_CTRL, + WDT_CTRL_EN, 0); +} + +static unsigned int sl28cpld_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned int val; + int ret; + + ret = regmap_read(wdt->regmap, wdt->offset + WDT_COUNT, &val); + if (ret) + return 0; + + return val; +} + +static int sl28cpld_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd); + int ret; + + ret = regmap_write(wdt->regmap, wdt->offset + WDT_TIMEOUT, timeout); + if (ret) + return ret; + + wdd->timeout = timeout; + + return 0; +} + +static const struct watchdog_info sl28cpld_wdt_info = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "sl28cpld watchdog", +}; + +static struct watchdog_ops sl28cpld_wdt_ops = { + .owner = THIS_MODULE, + .start = sl28cpld_wdt_start, + .stop = sl28cpld_wdt_stop, + .ping = sl28cpld_wdt_ping, + .set_timeout = sl28cpld_wdt_set_timeout, + .get_timeleft = sl28cpld_wdt_get_timeleft, +}; + +static int sl28cpld_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdd; + struct sl28cpld_wdt *wdt; + unsigned int status; + unsigned int val; + int ret; + + if (!pdev->dev.parent) + return -ENODEV; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!wdt->regmap) + return -ENODEV; + + ret = device_property_read_u32(&pdev->dev, "reg", &wdt->offset); + if (ret) + return -EINVAL; + + wdt->assert_wdt_timeout = device_property_read_bool(&pdev->dev, + "kontron,assert-wdt-timeout-pin"); + + /* initialize struct watchdog_device */ + wdd = &wdt->wdd; + wdd->parent = &pdev->dev; + wdd->info = &sl28cpld_wdt_info; + wdd->ops = &sl28cpld_wdt_ops; + wdd->min_timeout = 1; + wdd->max_timeout = 255; + + watchdog_set_drvdata(wdd, wdt); + watchdog_stop_on_reboot(wdd); + + /* + * Read the status early, in case of an error, we haven't modified the + * hardware. + */ + ret = regmap_read(wdt->regmap, wdt->offset + WDT_CTRL, &status); + if (ret) + return ret; + + /* + * Initial timeout value, may be overwritten by device tree or module + * parmeter in watchdog_init_timeout(). + * + * Reading a zero here means that either the hardware has a default + * value of zero (which is very unlikely and definitely a hardware + * bug) or the bootloader set it to zero. In any case, we handle + * this case gracefully and set out own timeout. + */ + ret = regmap_read(wdt->regmap, wdt->offset + WDT_TIMEOUT, &val); + if (ret) + return ret; + + if (val) + wdd->timeout = val; + else + wdd->timeout = WDT_DEFAULT_TIMEOUT; + + watchdog_init_timeout(wdd, timeout, &pdev->dev); + sl28cpld_wdt_set_timeout(wdd, wdd->timeout); + + /* if the watchdog is locked, we set nowayout */ + if (status & WDT_CTRL_LOCK) + nowayout = true; + watchdog_set_nowayout(wdd, nowayout); + + /* + * If watchdog is already running, keep it enabled, but make + * sure its mode is set correctly. + */ + if (status & WDT_CTRL_EN) { + sl28cpld_wdt_start(wdd); + set_bit(WDOG_HW_RUNNING, &wdd->status); + } + + ret = devm_watchdog_register_device(&pdev->dev, wdd); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register watchdog device\n"); + return ret; + } + + dev_info(&pdev->dev, "initial timeout %d sec%s\n", + wdd->timeout, nowayout ? ", nowayout" : ""); + + return 0; +} + +static const struct of_device_id sl28cpld_wdt_of_match[] = { + { .compatible = "kontron,sl28cpld-wdt" }, + {} +}; +MODULE_DEVICE_TABLE(of, sl28cpld_wdt_of_match); + +static struct platform_driver sl28cpld_wdt_driver = { + .probe = sl28cpld_wdt_probe, + .driver = { + .name = "sl28cpld-wdt", + .of_match_table = sl28cpld_wdt_of_match, + }, +}; +module_platform_driver(sl28cpld_wdt_driver); + +MODULE_DESCRIPTION("sl28cpld Watchdog Driver"); +MODULE_AUTHOR("Michael Walle <michael@walle.cc>"); +MODULE_LICENSE("GPL"); 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; } } |