summaryrefslogtreecommitdiff
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig12
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/aspeed_wdt.c30
-rw-r--r--drivers/watchdog/diag288_wdt.c9
-rw-r--r--drivers/watchdog/loongson1_wdt.c89
-rw-r--r--drivers/watchdog/renesas_wwdt.c163
-rw-r--r--drivers/watchdog/starfive-wdt.c4
-rw-r--r--drivers/watchdog/via_wdt.c1
-rw-r--r--drivers/watchdog/wdat_wdt.c64
9 files changed, 313 insertions, 60 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 05008d937e40..d3b9df7d466b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -969,6 +969,14 @@ config RENESAS_WDT
This driver adds watchdog support for the integrated watchdogs in the
Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT).
+config RENESAS_WWDT
+ tristate "Renesas Window WWDT Watchdog"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ This driver adds watchdog support for a window timer found in some
+ Renesas R-Car Gen3 and later SoCs.
+
config RENESAS_RZAWDT
tristate "Renesas RZ/A WDT Watchdog"
depends on ARCH_RENESAS || COMPILE_TEST
@@ -1976,10 +1984,10 @@ config LANTIQ_WDT
config LOONGSON1_WDT
tristate "Loongson1 SoC hardware watchdog"
- depends on MACH_LOONGSON32 || COMPILE_TEST
+ depends on MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST
select WATCHDOG_CORE
help
- Hardware driver for the Loongson1 SoC Watchdog Timer.
+ Hardware driver for the Loongson family Watchdog Timer.
config RALINK_WDT
tristate "Ralink SoC watchdog"
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index b680e4d3c1bc..ba52099b1253 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
+obj-$(CONFIG_RENESAS_WWDT) += renesas_wwdt.o
obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o
obj-$(CONFIG_RENESAS_RZN1WDT) += rzn1_wdt.o
obj-$(CONFIG_RENESAS_RZG2LWDT) += rzg2l_wdt.o
diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
index 837e15701c0e..c9e79851504c 100644
--- a/drivers/watchdog/aspeed_wdt.c
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -35,6 +35,7 @@ struct aspeed_wdt_config {
u32 irq_shift;
u32 irq_mask;
struct aspeed_wdt_scu scu;
+ u32 num_reset_masks;
};
struct aspeed_wdt {
@@ -66,6 +67,7 @@ static const struct aspeed_wdt_config ast2500_config = {
.wdt_reset_mask = 0x1,
.wdt_reset_mask_shift = 2,
},
+ .num_reset_masks = 1,
};
static const struct aspeed_wdt_config ast2600_config = {
@@ -78,12 +80,27 @@ static const struct aspeed_wdt_config ast2600_config = {
.wdt_reset_mask = 0xf,
.wdt_reset_mask_shift = 16,
},
+ .num_reset_masks = 2,
+};
+
+static const struct aspeed_wdt_config ast2700_config = {
+ .ext_pulse_width_mask = 0xfffff,
+ .irq_shift = 0,
+ .irq_mask = GENMASK(31, 10),
+ .scu = {
+ .compatible = "aspeed,ast2700-scu0",
+ .reset_status_reg = 0x70,
+ .wdt_reset_mask = 0xf,
+ .wdt_reset_mask_shift = 0,
+ },
+ .num_reset_masks = 5,
};
static const struct of_device_id aspeed_wdt_of_table[] = {
{ .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
{ .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
{ .compatible = "aspeed,ast2600-wdt", .data = &ast2600_config },
+ { .compatible = "aspeed,ast2700-wdt", .data = &ast2700_config },
{ },
};
MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
@@ -479,11 +496,11 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
}
- if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) ||
- (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
- u32 reset_mask[2];
- size_t nrstmask = of_device_is_compatible(np, "aspeed,ast2600-wdt") ? 2 : 1;
+ if (!of_device_is_compatible(np, "aspeed,ast2400-wdt")) {
+ u32 reset_mask[5];
+ size_t nrstmask = wdt->cfg->num_reset_masks;
u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
+ int i;
reg &= wdt->cfg->ext_pulse_width_mask;
if (of_property_read_bool(np, "aspeed,ext-active-high"))
@@ -503,9 +520,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
ret = of_property_read_u32_array(np, "aspeed,reset-mask", reset_mask, nrstmask);
if (!ret) {
- writel(reset_mask[0], wdt->base + WDT_RESET_MASK1);
- if (nrstmask > 1)
- writel(reset_mask[1], wdt->base + WDT_RESET_MASK2);
+ for (i = 0; i < nrstmask; i++)
+ writel(reset_mask[i], wdt->base + WDT_RESET_MASK1 + i * 4);
}
}
diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c
index 887d5a6c155b..ea2b0171e70b 100644
--- a/drivers/watchdog/diag288_wdt.c
+++ b/drivers/watchdog/diag288_wdt.c
@@ -6,10 +6,10 @@
* to CP.
*
* The command can be altered using the module parameter "cmd". This is
- * not recommended because it's only supported on z/VM but not whith LPAR.
+ * not recommended because it's only supported on z/VM but not with LPAR.
*
- * On LPAR, the watchdog will always trigger a system restart. the module
- * paramter cmd is meaningless here.
+ * On LPAR, the watchdog will always trigger a system restart. The module
+ * parameter cmd is meaningless here.
*
*
* Copyright IBM Corp. 2004, 2013
@@ -18,8 +18,7 @@
*
*/
-#define KMSG_COMPONENT "diag288_wdt"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "diag288_wdt: " fmt
#include <linux/init.h>
#include <linux/kernel.h>
diff --git a/drivers/watchdog/loongson1_wdt.c b/drivers/watchdog/loongson1_wdt.c
index 0587ff44d3a1..2417519717cc 100644
--- a/drivers/watchdog/loongson1_wdt.c
+++ b/drivers/watchdog/loongson1_wdt.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
+ * Copyright (C) 2025 Binbin Zhou <zhoubinbin@loongson.cn>
*/
#include <linux/clk.h>
@@ -10,31 +11,52 @@
#include <linux/platform_device.h>
#include <linux/watchdog.h>
-/* Loongson 1 Watchdog Register Definitions */
+/* Loongson Watchdog Register Definitions */
#define WDT_EN 0x0
-#define WDT_TIMER 0x4
-#define WDT_SET 0x8
#define DEFAULT_HEARTBEAT 30
static bool nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, bool, 0444);
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static unsigned int heartbeat;
-module_param(heartbeat, uint, 0444);
+module_param(heartbeat, uint, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
+ __MODULE_STRING(DEFAULT_HEARTBEAT) ")");
+
+struct ls1x_wdt_pdata {
+ u32 timer_offset;
+ u32 set_offset;
+ u32 wdt_en_bit;
+};
+
+static const struct ls1x_wdt_pdata ls1b_wdt_pdata = {
+ .timer_offset = 0x4,
+ .set_offset = 0x8,
+ .wdt_en_bit = BIT(0),
+};
+
+static const struct ls1x_wdt_pdata ls2k0300_wdt_pdata = {
+ .timer_offset = 0x8,
+ .set_offset = 0x4,
+ .wdt_en_bit = BIT(1),
+};
struct ls1x_wdt_drvdata {
void __iomem *base;
struct clk *clk;
unsigned long clk_rate;
struct watchdog_device wdt;
+ const struct ls1x_wdt_pdata *pdata;
};
static int ls1x_wdt_ping(struct watchdog_device *wdt_dev)
{
struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
- writel(0x1, drvdata->base + WDT_SET);
+ writel(0x1, drvdata->base + drvdata->pdata->set_offset);
return 0;
}
@@ -49,7 +71,7 @@ static int ls1x_wdt_set_timeout(struct watchdog_device *wdt_dev,
wdt_dev->timeout = timeout;
counts = drvdata->clk_rate * min(timeout, max_hw_heartbeat);
- writel(counts, drvdata->base + WDT_TIMER);
+ writel(counts, drvdata->base + drvdata->pdata->timer_offset);
return 0;
}
@@ -58,7 +80,7 @@ static int ls1x_wdt_start(struct watchdog_device *wdt_dev)
{
struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
- writel(0x1, drvdata->base + WDT_EN);
+ writel(drvdata->pdata->wdt_en_bit, drvdata->base + WDT_EN);
return 0;
}
@@ -66,8 +88,10 @@ static int ls1x_wdt_start(struct watchdog_device *wdt_dev)
static int ls1x_wdt_stop(struct watchdog_device *wdt_dev)
{
struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
+ u32 val = readl(drvdata->base + WDT_EN);
- writel(0x0, drvdata->base + WDT_EN);
+ val &= ~(drvdata->pdata->wdt_en_bit);
+ writel(val, drvdata->base + WDT_EN);
return 0;
}
@@ -77,9 +101,9 @@ static int ls1x_wdt_restart(struct watchdog_device *wdt_dev,
{
struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
- writel(0x1, drvdata->base + WDT_EN);
- writel(0x1, drvdata->base + WDT_TIMER);
- writel(0x1, drvdata->base + WDT_SET);
+ writel(drvdata->pdata->wdt_en_bit, drvdata->base + WDT_EN);
+ writel(0x1, drvdata->base + drvdata->pdata->timer_offset);
+ writel(0x1, drvdata->base + drvdata->pdata->set_offset);
return 0;
}
@@ -104,11 +128,13 @@ static int ls1x_wdt_probe(struct platform_device *pdev)
struct ls1x_wdt_drvdata *drvdata;
struct watchdog_device *ls1x_wdt;
unsigned long clk_rate;
- int err;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
+ platform_set_drvdata(pdev, drvdata);
+
+ drvdata->pdata = of_device_get_match_data(dev);
drvdata->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(drvdata->base))
@@ -135,36 +161,51 @@ static int ls1x_wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(ls1x_wdt, nowayout);
watchdog_set_drvdata(ls1x_wdt, drvdata);
- err = devm_watchdog_register_device(dev, &drvdata->wdt);
- if (err)
- return err;
+ return devm_watchdog_register_device(dev, &drvdata->wdt);
+}
- platform_set_drvdata(pdev, drvdata);
+static int ls1x_wdt_resume(struct device *dev)
+{
+ struct ls1x_wdt_drvdata *data = dev_get_drvdata(dev);
+
+ if (watchdog_active(&data->wdt))
+ ls1x_wdt_start(&data->wdt);
+
+ return 0;
+}
+
+static int ls1x_wdt_suspend(struct device *dev)
+{
+ struct ls1x_wdt_drvdata *data = dev_get_drvdata(dev);
- dev_info(dev, "Loongson1 Watchdog driver registered\n");
+ if (watchdog_active(&data->wdt))
+ ls1x_wdt_stop(&data->wdt);
return 0;
}
-#ifdef CONFIG_OF
+static DEFINE_SIMPLE_DEV_PM_OPS(ls1x_wdt_pm_ops, ls1x_wdt_suspend, ls1x_wdt_resume);
+
static const struct of_device_id ls1x_wdt_dt_ids[] = {
- { .compatible = "loongson,ls1b-wdt", },
- { .compatible = "loongson,ls1c-wdt", },
+ { .compatible = "loongson,ls1b-wdt", .data = &ls1b_wdt_pdata },
+ { .compatible = "loongson,ls1c-wdt", .data = &ls1b_wdt_pdata },
+ { .compatible = "loongson,ls2k0300-wdt", .data = &ls2k0300_wdt_pdata },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ls1x_wdt_dt_ids);
-#endif
static struct platform_driver ls1x_wdt_driver = {
.probe = ls1x_wdt_probe,
.driver = {
.name = "ls1x-wdt",
- .of_match_table = of_match_ptr(ls1x_wdt_dt_ids),
+ .of_match_table = ls1x_wdt_dt_ids,
+ .pm = pm_ptr(&ls1x_wdt_pm_ops),
},
};
module_platform_driver(ls1x_wdt_driver);
MODULE_AUTHOR("Yang Ling <gnaygnil@gmail.com>");
-MODULE_DESCRIPTION("Loongson1 Watchdog Driver");
+MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
+MODULE_DESCRIPTION("Loongson Watchdog Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/renesas_wwdt.c b/drivers/watchdog/renesas_wwdt.c
new file mode 100644
index 000000000000..b250913c349a
--- /dev/null
+++ b/drivers/watchdog/renesas_wwdt.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Renesas Window Watchdog Timer (WWDT)
+ *
+ * The WWDT can only be setup once after boot. Because we cannot know if this
+ * already happened in early boot stages, it is mandated that the firmware
+ * configures the watchdog. Linux then adapts according to the given setup.
+ * Note that this watchdog reports in the default configuration an overflow to
+ * the Error Control Module which then decides further actions. Or the WWDT is
+ * configured to generate an interrupt.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.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 WDTA0WDTE 0x00
+#define WDTA0RUN BIT(7)
+#define WDTA0_KEY 0x2c
+
+#define WDTA0MD 0x0c
+#define WDTA0OVF(x) FIELD_GET(GENMASK(6, 4), x)
+#define WDTA0WIE BIT(3)
+#define WDTA0ERM BIT(2)
+#define WDTA0WS(x) FIELD_GET(GENMASK(1, 0), x)
+
+struct wwdt_priv {
+ void __iomem *base;
+ struct watchdog_device wdev;
+};
+
+static int wwdt_start(struct watchdog_device *wdev)
+{
+ struct wwdt_priv *priv = container_of(wdev, struct wwdt_priv, wdev);
+
+ writeb(WDTA0RUN | WDTA0_KEY, priv->base + WDTA0WDTE);
+ return 0;
+}
+
+static const struct watchdog_info wwdt_ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_ALARMONLY,
+ .identity = "Renesas Window Watchdog",
+};
+
+static const struct watchdog_ops wwdt_ops = {
+ .owner = THIS_MODULE,
+ .start = wwdt_start,
+};
+
+static irqreturn_t wwdt_error_irq(int irq, void *dev_id)
+{
+ struct device *dev = dev_id;
+
+ dev_warn(dev, "Watchdog timed out\n");
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wwdt_pretimeout_irq(int irq, void *dev_id)
+{
+ struct watchdog_device *wdev = dev_id;
+
+ watchdog_notify_pretimeout(wdev);
+ return IRQ_HANDLED;
+}
+
+static int wwdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wwdt_priv *priv;
+ struct watchdog_device *wdev;
+ struct clk *clk;
+ unsigned long rate;
+ unsigned int interval, window_size;
+ int ret;
+ u8 val;
+
+ 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, "cnt");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ rate = clk_get_rate(clk);
+ if (!rate)
+ return -EINVAL;
+
+ wdev = &priv->wdev;
+
+ val = readb(priv->base + WDTA0WDTE);
+ if (val & WDTA0RUN)
+ set_bit(WDOG_HW_RUNNING, &wdev->status);
+
+ val = readb(priv->base + WDTA0MD);
+ interval = 1 << (9 + WDTA0OVF(val));
+ /* size of the closed(!) window per mille */
+ window_size = 250 * (3 - WDTA0WS(val));
+
+ wdev->info = &wwdt_ident;
+ wdev->ops = &wwdt_ops;
+ wdev->parent = dev;
+ wdev->min_hw_heartbeat_ms = window_size * interval / rate;
+ wdev->max_hw_heartbeat_ms = 1000 * interval / rate;
+ wdev->timeout = DIV_ROUND_UP(wdev->max_hw_heartbeat_ms, 1000);
+ watchdog_set_nowayout(wdev, true);
+
+ if (!(val & WDTA0ERM)) {
+ ret = platform_get_irq_byname(pdev, "error");
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, ret, NULL, wwdt_error_irq,
+ IRQF_ONESHOT, NULL, dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (val & WDTA0WIE) {
+ ret = platform_get_irq_byname(pdev, "pretimeout");
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, ret, NULL, wwdt_pretimeout_irq,
+ IRQF_ONESHOT, NULL, wdev);
+ if (ret < 0)
+ return ret;
+ }
+
+ devm_watchdog_register_device(dev, wdev);
+
+ return 0;
+}
+
+static const struct of_device_id renesas_wwdt_ids[] = {
+ { .compatible = "renesas,rcar-gen3-wwdt", },
+ { .compatible = "renesas,rcar-gen4-wwdt", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, renesas_wwdt_ids);
+
+static struct platform_driver renesas_wwdt_driver = {
+ .driver = {
+ .name = "renesas_wwdt",
+ .of_match_table = renesas_wwdt_ids,
+ },
+ .probe = wwdt_probe,
+};
+module_platform_driver(renesas_wwdt_driver);
+
+MODULE_DESCRIPTION("Renesas Window Watchdog (WWDT) Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>");
diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index 355918d62f63..ed71d3960a0f 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -500,12 +500,14 @@ static int starfive_wdt_probe(struct platform_device *pdev)
if (pm_runtime_enabled(&pdev->dev)) {
ret = pm_runtime_put_sync(&pdev->dev);
if (ret)
- goto err_exit;
+ goto err_unregister_wdt;
}
}
return 0;
+err_unregister_wdt:
+ watchdog_unregister_device(&wdt->wdd);
err_exit:
starfive_wdt_disable_clock(wdt);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
index d647923d68fe..f55576392651 100644
--- a/drivers/watchdog/via_wdt.c
+++ b/drivers/watchdog/via_wdt.c
@@ -165,6 +165,7 @@ static int wdt_probe(struct pci_dev *pdev,
dev_err(&pdev->dev, "cannot enable PCI device\n");
return -ENODEV;
}
+ wdt_res.name = "via_wdt";
/*
* Allocate a MMIO region which contains watchdog control register
diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c
index 650fdc7996e1..dd3c2d69c9df 100644
--- a/drivers/watchdog/wdat_wdt.c
+++ b/drivers/watchdog/wdat_wdt.c
@@ -326,19 +326,27 @@ static int wdat_wdt_probe(struct platform_device *pdev)
return -ENODEV;
wdat = devm_kzalloc(dev, sizeof(*wdat), GFP_KERNEL);
- if (!wdat)
- return -ENOMEM;
+ if (!wdat) {
+ ret = -ENOMEM;
+ goto out_put_table;
+ }
regs = devm_kcalloc(dev, pdev->num_resources, sizeof(*regs),
GFP_KERNEL);
- if (!regs)
- return -ENOMEM;
+ if (!regs) {
+ ret = -ENOMEM;
+ goto out_put_table;
+ }
/* WDAT specification wants to have >= 1ms period */
- if (tbl->timer_period < 1)
- return -EINVAL;
- if (tbl->min_count > tbl->max_count)
- return -EINVAL;
+ if (tbl->timer_period < 1) {
+ ret = -EINVAL;
+ goto out_put_table;
+ }
+ if (tbl->min_count > tbl->max_count) {
+ ret = -EINVAL;
+ goto out_put_table;
+ }
wdat->period = tbl->timer_period;
wdat->wdd.min_timeout = DIV_ROUND_UP(wdat->period * tbl->min_count, 1000);
@@ -355,15 +363,20 @@ static int wdat_wdt_probe(struct platform_device *pdev)
res = &pdev->resource[i];
if (resource_type(res) == IORESOURCE_MEM) {
reg = devm_ioremap_resource(dev, res);
- if (IS_ERR(reg))
- return PTR_ERR(reg);
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ goto out_put_table;
+ }
} else if (resource_type(res) == IORESOURCE_IO) {
reg = devm_ioport_map(dev, res->start, 1);
- if (!reg)
- return -ENOMEM;
+ if (!reg) {
+ ret = -ENOMEM;
+ goto out_put_table;
+ }
} else {
dev_err(dev, "Unsupported resource\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_put_table;
}
regs[i] = reg;
@@ -385,8 +398,10 @@ static int wdat_wdt_probe(struct platform_device *pdev)
}
instr = devm_kzalloc(dev, sizeof(*instr), GFP_KERNEL);
- if (!instr)
- return -ENOMEM;
+ if (!instr) {
+ ret = -ENOMEM;
+ goto out_put_table;
+ }
INIT_LIST_HEAD(&instr->node);
instr->entry = entries[i];
@@ -417,7 +432,8 @@ static int wdat_wdt_probe(struct platform_device *pdev)
if (!instr->reg) {
dev_err(dev, "I/O resource not found\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_put_table;
}
instructions = wdat->instructions[action];
@@ -425,8 +441,10 @@ static int wdat_wdt_probe(struct platform_device *pdev)
instructions = devm_kzalloc(dev,
sizeof(*instructions),
GFP_KERNEL);
- if (!instructions)
- return -ENOMEM;
+ if (!instructions) {
+ ret = -ENOMEM;
+ goto out_put_table;
+ }
INIT_LIST_HEAD(instructions);
wdat->instructions[action] = instructions;
@@ -443,7 +461,7 @@ static int wdat_wdt_probe(struct platform_device *pdev)
ret = wdat_wdt_enable_reboot(wdat);
if (ret)
- return ret;
+ goto out_put_table;
platform_set_drvdata(pdev, wdat);
@@ -460,12 +478,16 @@ static int wdat_wdt_probe(struct platform_device *pdev)
ret = wdat_wdt_set_timeout(&wdat->wdd, timeout);
if (ret)
- return ret;
+ goto out_put_table;
watchdog_set_nowayout(&wdat->wdd, nowayout);
watchdog_stop_on_reboot(&wdat->wdd);
watchdog_stop_on_unregister(&wdat->wdd);
- return devm_watchdog_register_device(dev, &wdat->wdd);
+ ret = devm_watchdog_register_device(dev, &wdat->wdd);
+
+out_put_table:
+ acpi_put_table((struct acpi_table_header *)tbl);
+ return ret;
}
static int wdat_wdt_suspend_noirq(struct device *dev)