diff options
Diffstat (limited to 'drivers/watchdog')
| -rw-r--r-- | drivers/watchdog/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
| -rw-r--r-- | drivers/watchdog/aspeed_wdt.c | 30 | ||||
| -rw-r--r-- | drivers/watchdog/diag288_wdt.c | 9 | ||||
| -rw-r--r-- | drivers/watchdog/loongson1_wdt.c | 89 | ||||
| -rw-r--r-- | drivers/watchdog/renesas_wwdt.c | 163 | ||||
| -rw-r--r-- | drivers/watchdog/starfive-wdt.c | 4 | ||||
| -rw-r--r-- | drivers/watchdog/via_wdt.c | 1 | ||||
| -rw-r--r-- | drivers/watchdog/wdat_wdt.c | 64 |
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) |
