Commit b34f01f7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'linux-watchdog-5.6-rc1' of git://www.linux-watchdog.org/linux-watchdog

Pull watchdog updates from Wim Van Sebroeck:

 - add IT8786 chipset ID

 - addition of sam9x60 compatible watchdog

 - da9062 improvements

 - fix UAF in reboot notifier handling in watchdog core code

 - other fixes and small improvements

* tag 'linux-watchdog-5.6-rc1' of git://www.linux-watchdog.org/linux-watchdog:
  watchdog: da9062: make restart handler atomic safe
  watchdog: mtk_wdt: mt2712: Add reset controller
  watchdog: mtk_wdt: mt8183: Add reset controller
  dt-bindings: mediatek: mt2712: Add #reset-cells
  dt-bindings: mediatek: mt8183: Add #reset-cells
  dt-bindings: watchdog: da9062: add suspend disable option
  watchdog: it87_wdt: add IT8786 ID
  watchdog: dw_wdt: ping watchdog to reset countdown before start
  watchdog: fix UAF in reboot notifier handling in watchdog core code
  watchdog: cadence: Skip printing pointer value
  watchdog: qcom: Use platform_get_irq_optional() for bark irq
  watchdog: da9062: add power management ops
  watchdog: make DesignWare watchdog allow users to set bigger timeout value
  drivers: watchdog: stm32_iwdg: set WDOG_HW_RUNNING at probe
  watchdog: sama5d4_wdt: addition of sam9x60 compatible watchdog
parents e0f121c5 057b52b4
......@@ -6,6 +6,11 @@ Required properties:
"dlg,da9061-watchdog", "dlg,da9062-watchdog"
"dlg,da9062-watchdog"
Optional properties:
- dlg,use-sw-pm: Add this property to disable the watchdog during suspend.
Only use this option if you can't use the watchdog automatic suspend
function during a suspend (see register CONTROL_B).
Example: DA9062
pmic0: da9062@58 {
......
......@@ -4,22 +4,27 @@ Required properties:
- compatible should contain:
"mediatek,mt2701-wdt", "mediatek,mt6589-wdt": for MT2701
"mediatek,mt2712-wdt", "mediatek,mt6589-wdt": for MT2712
"mediatek,mt6589-wdt": for MT6589
"mediatek,mt6797-wdt", "mediatek,mt6589-wdt": for MT6797
"mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622
"mediatek,mt7623-wdt", "mediatek,mt6589-wdt": for MT7623
"mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629
"mediatek,mt8183-wdt", "mediatek,mt6589-wdt": for MT8183
"mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516
- reg : Specifies base physical address and size of the registers.
Optional properties:
- timeout-sec: contains the watchdog timeout in seconds.
- #reset-cells: Should be 1.
Example:
wdt: watchdog@10000000 {
compatible = "mediatek,mt6589-wdt";
reg = <0x10000000 0x18>;
watchdog: watchdog@10007000 {
compatible = "mediatek,mt8183-wdt",
"mediatek,mt6589-wdt";
reg = <0 0x10007000 0 0x100>;
timeout-sec = <10>;
#reset-cells = <1>;
};
......@@ -24,7 +24,10 @@
#define AT91_WDT_MR 0x04 /* Watchdog Mode Register */
#define AT91_WDT_WDV (0xfffUL << 0) /* Counter Value */
#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV)
#define AT91_SAM9X60_PERIODRST BIT(4) /* Period Reset */
#define AT91_SAM9X60_RPTHRST BIT(5) /* Minimum Restart Period */
#define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */
#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable */
#define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */
#define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */
#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */
......@@ -37,4 +40,22 @@
#define AT91_WDT_WDUNF BIT(0) /* Watchdog Underflow */
#define AT91_WDT_WDERR BIT(1) /* Watchdog Error */
/* Watchdog Timer Value Register */
#define AT91_SAM9X60_VR 0x08
/* Watchdog Window Level Register */
#define AT91_SAM9X60_WLR 0x0c
/* Watchdog Period Value */
#define AT91_SAM9X60_COUNTER (0xfffUL << 0)
#define AT91_SAM9X60_SET_COUNTER(x) ((x) & AT91_SAM9X60_COUNTER)
/* Interrupt Enable Register */
#define AT91_SAM9X60_IER 0x14
/* Period Interrupt Enable */
#define AT91_SAM9X60_PERINT BIT(0)
/* Interrupt Disable Register */
#define AT91_SAM9X60_IDR 0x18
/* Interrupt Status Register */
#define AT91_SAM9X60_ISR 0x1c
#endif
......@@ -369,9 +369,8 @@ static int cdns_wdt_probe(struct platform_device *pdev)
return ret;
platform_set_drvdata(pdev, wdt);
dev_info(dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n",
wdt->regs, cdns_wdt_device->timeout,
nowayout ? ", nowayout" : "");
dev_info(dev, "Xilinx Watchdog Timer with timeout %ds%s\n",
cdns_wdt_device->timeout, nowayout ? ", nowayout" : "");
return 0;
}
......
......@@ -11,6 +11,7 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/mfd/da9062/registers.h>
......@@ -147,12 +148,13 @@ static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action,
void *data)
{
struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
struct i2c_client *client = to_i2c_client(wdt->hw->dev);
int ret;
ret = regmap_write(wdt->hw->regmap,
DA9062AA_CONTROL_F,
/* Don't use regmap because it is not atomic safe */
ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F,
DA9062AA_SHUTDOWN_MASK);
if (ret)
if (ret < 0)
dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n",
ret);
......@@ -212,6 +214,7 @@ static int da9062_wdt_probe(struct platform_device *pdev)
watchdog_set_restart_priority(&wdt->wdtdev, 128);
watchdog_set_drvdata(&wdt->wdtdev, wdt);
dev_set_drvdata(dev, &wdt->wdtdev);
ret = devm_watchdog_register_device(dev, &wdt->wdtdev);
if (ret < 0)
......@@ -220,10 +223,34 @@ static int da9062_wdt_probe(struct platform_device *pdev)
return da9062_wdt_ping(&wdt->wdtdev);
}
static int __maybe_unused da9062_wdt_suspend(struct device *dev)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
if (watchdog_active(wdd))
return da9062_wdt_stop(wdd);
return 0;
}
static int __maybe_unused da9062_wdt_resume(struct device *dev)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
if (watchdog_active(wdd))
return da9062_wdt_start(wdd);
return 0;
}
static SIMPLE_DEV_PM_OPS(da9062_wdt_pm_ops,
da9062_wdt_suspend, da9062_wdt_resume);
static struct platform_driver da9062_wdt_driver = {
.probe = da9062_wdt_probe,
.driver = {
.name = "da9062-watchdog",
.pm = &da9062_wdt_pm_ops,
.of_match_table = da9062_compatible_id_table,
},
};
......
......@@ -114,7 +114,15 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s)
writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT,
dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
/*
* In case users set bigger timeout value than HW can support,
* kernel(watchdog_dev.c) helps to feed watchdog before
* wdd->max_hw_heartbeat_ms
*/
if (top_s * 1000 <= wdd->max_hw_heartbeat_ms)
wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val);
else
wdd->timeout = top_s;
return 0;
}
......@@ -135,6 +143,7 @@ static int dw_wdt_start(struct watchdog_device *wdd)
struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
dw_wdt_set_timeout(wdd, wdd->timeout);
dw_wdt_ping(&dw_wdt->wdd);
dw_wdt_arm_system_reset(dw_wdt);
return 0;
......
......@@ -67,6 +67,7 @@
#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
#define IT8728_ID 0x8728
#define IT8783_ID 0x8783
#define IT8786_ID 0x8786
/* GPIO Configuration Registers LDN=0x07 */
#define WDTCTRL 0x71
......@@ -294,6 +295,7 @@ static int __init it87_wdt_init(void)
case IT8721_ID:
case IT8728_ID:
case IT8783_ID:
case IT8786_ID:
max_units = 65535;
break;
case IT8705_ID:
......
......@@ -9,6 +9,9 @@
* Based on sunxi_wdt.c
*/
#include <dt-bindings/reset-controller/mt2712-resets.h>
#include <dt-bindings/reset-controller/mt8183-resets.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
......@@ -16,10 +19,11 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/delay.h>
#define WDT_MAX_TIMEOUT 31
#define WDT_MIN_TIMEOUT 1
......@@ -44,6 +48,9 @@
#define WDT_SWRST 0x14
#define WDT_SWRST_KEY 0x1209
#define WDT_SWSYSRST 0x18U
#define WDT_SWSYS_RST_KEY 0x88000000
#define DRV_NAME "mtk-wdt"
#define DRV_VERSION "1.0"
......@@ -53,8 +60,94 @@ static unsigned int timeout;
struct mtk_wdt_dev {
struct watchdog_device wdt_dev;
void __iomem *wdt_base;
spinlock_t lock; /* protects WDT_SWSYSRST reg */
struct reset_controller_dev rcdev;
};
struct mtk_wdt_data {
int toprgu_sw_rst_num;
};
static const struct mtk_wdt_data mt2712_data = {
.toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM,
};
static const struct mtk_wdt_data mt8183_data = {
.toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM,
};
static int toprgu_reset_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
unsigned int tmp;
unsigned long flags;
struct mtk_wdt_dev *data =
container_of(rcdev, struct mtk_wdt_dev, rcdev);
spin_lock_irqsave(&data->lock, flags);
tmp = readl(data->wdt_base + WDT_SWSYSRST);
if (assert)
tmp |= BIT(id);
else
tmp &= ~BIT(id);
tmp |= WDT_SWSYS_RST_KEY;
writel(tmp, data->wdt_base + WDT_SWSYSRST);
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int toprgu_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return toprgu_reset_update(rcdev, id, true);
}
static int toprgu_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return toprgu_reset_update(rcdev, id, false);
}
static int toprgu_reset(struct reset_controller_dev *rcdev,
unsigned long id)
{
int ret;
ret = toprgu_reset_assert(rcdev, id);
if (ret)
return ret;
return toprgu_reset_deassert(rcdev, id);
}
static const struct reset_control_ops toprgu_reset_ops = {
.assert = toprgu_reset_assert,
.deassert = toprgu_reset_deassert,
.reset = toprgu_reset,
};
static int toprgu_register_reset_controller(struct platform_device *pdev,
int rst_num)
{
int ret;
struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);
spin_lock_init(&mtk_wdt->lock);
mtk_wdt->rcdev.owner = THIS_MODULE;
mtk_wdt->rcdev.nr_resets = rst_num;
mtk_wdt->rcdev.ops = &toprgu_reset_ops;
mtk_wdt->rcdev.of_node = pdev->dev.of_node;
ret = devm_reset_controller_register(&pdev->dev, &mtk_wdt->rcdev);
if (ret != 0)
dev_err(&pdev->dev,
"couldn't register wdt reset controller: %d\n", ret);
return ret;
}
static int mtk_wdt_restart(struct watchdog_device *wdt_dev,
unsigned long action, void *data)
{
......@@ -155,6 +248,7 @@ static int mtk_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_wdt_dev *mtk_wdt;
const struct mtk_wdt_data *wdt_data;
int err;
mtk_wdt = devm_kzalloc(dev, sizeof(*mtk_wdt), GFP_KERNEL);
......@@ -190,6 +284,13 @@ static int mtk_wdt_probe(struct platform_device *pdev)
dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n",
mtk_wdt->wdt_dev.timeout, nowayout);
wdt_data = of_device_get_match_data(dev);
if (wdt_data) {
err = toprgu_register_reset_controller(pdev,
wdt_data->toprgu_sw_rst_num);
if (err)
return err;
}
return 0;
}
......@@ -218,7 +319,9 @@ static int mtk_wdt_resume(struct device *dev)
#endif
static const struct of_device_id mtk_wdt_dt_ids[] = {
{ .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data },
{ .compatible = "mediatek,mt6589-wdt" },
{ .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);
......
......@@ -246,7 +246,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
}
/* check if there is pretimeout support */
irq = platform_get_irq(pdev, 0);
irq = platform_get_irq_optional(pdev, 0);
if (irq > 0) {
ret = devm_request_irq(dev, irq, qcom_wdt_isr,
IRQF_TRIGGER_RISING,
......
......@@ -2,7 +2,7 @@
/*
* Driver for Atmel SAMA5D4 Watchdog Timer
*
* Copyright (C) 2015 Atmel Corporation
* Copyright (C) 2015-2019 Microchip Technology Inc. and its subsidiaries
*/
#include <linux/delay.h>
......@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
......@@ -29,7 +30,10 @@ struct sama5d4_wdt {
struct watchdog_device wdd;
void __iomem *reg_base;
u32 mr;
u32 ir;
unsigned long last_ping;
bool need_irq;
bool sam9x60_support;
};
static int wdt_timeout;
......@@ -78,7 +82,12 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd)
{
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
if (wdt->sam9x60_support) {
writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER);
wdt->mr &= ~AT91_SAM9X60_WDDIS;
} else {
wdt->mr &= ~AT91_WDT_WDDIS;
}
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0;
......@@ -88,7 +97,12 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd)
{
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
if (wdt->sam9x60_support) {
writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR);
wdt->mr |= AT91_SAM9X60_WDDIS;
} else {
wdt->mr |= AT91_WDT_WDDIS;
}
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0;
......@@ -109,6 +123,14 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
u32 value = WDT_SEC2TICKS(timeout);
if (wdt->sam9x60_support) {
wdt_write(wdt, AT91_SAM9X60_WLR,
AT91_SAM9X60_SET_COUNTER(value));
wdd->timeout = timeout;
return 0;
}
wdt->mr &= ~AT91_WDT_WDV;
wdt->mr |= AT91_WDT_SET_WDV(value);
......@@ -143,8 +165,14 @@ static const struct watchdog_ops sama5d4_wdt_ops = {
static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id)
{
struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id);
u32 reg;
if (wdt->sam9x60_support)
reg = wdt_read(wdt, AT91_SAM9X60_ISR);
else
reg = wdt_read(wdt, AT91_WDT_SR);
if (wdt_read(wdt, AT91_WDT_SR)) {
if (reg) {
pr_crit("Atmel Watchdog Software Reset\n");
emergency_restart();
pr_crit("Reboot didn't succeed\n");
......@@ -157,13 +185,14 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
{
const char *tmp;
if (wdt->sam9x60_support)
wdt->mr = AT91_SAM9X60_WDDIS;
else
wdt->mr = AT91_WDT_WDDIS;
if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
!strcmp(tmp, "software"))
wdt->mr |= AT91_WDT_WDFIEN;
else
wdt->mr |= AT91_WDT_WDRSTEN;
wdt->need_irq = true;
if (of_property_read_bool(np, "atmel,idle-halt"))
wdt->mr |= AT91_WDT_WDIDLEHLT;
......@@ -176,21 +205,46 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
{
u32 reg;
u32 reg, val;
val = WDT_SEC2TICKS(WDT_DEFAULT_TIMEOUT);
/*
* When booting and resuming, the bootloader may have changed the
* watchdog configuration.
* If the watchdog is already running, we can safely update it.
* Else, we have to disable it properly.
*/
if (wdt_enabled) {
wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
} else {
if (!wdt_enabled) {
reg = wdt_read(wdt, AT91_WDT_MR);
if (!(reg & AT91_WDT_WDDIS))
if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS)))
wdt_write_nosleep(wdt, AT91_WDT_MR,
reg | AT91_SAM9X60_WDDIS);
else if (!wdt->sam9x60_support &&
(!(reg & AT91_WDT_WDDIS)))
wdt_write_nosleep(wdt, AT91_WDT_MR,
reg | AT91_WDT_WDDIS);
}
if (wdt->sam9x60_support) {
if (wdt->need_irq)
wdt->ir = AT91_SAM9X60_PERINT;
else
wdt->mr |= AT91_SAM9X60_PERIODRST;
wdt_write(wdt, AT91_SAM9X60_IER, wdt->ir);
wdt_write(wdt, AT91_SAM9X60_WLR, AT91_SAM9X60_SET_COUNTER(val));
} else {
wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT));
wdt->mr |= AT91_WDT_SET_WDV(val);
if (wdt->need_irq)
wdt->mr |= AT91_WDT_WDFIEN;
else
wdt->mr |= AT91_WDT_WDRSTEN;
}
wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
return 0;
}
......@@ -201,7 +255,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
struct sama5d4_wdt *wdt;
void __iomem *regs;
u32 irq = 0;
u32 timeout;
int ret;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
......@@ -215,6 +268,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
wdd->min_timeout = MIN_WDT_TIMEOUT;
wdd->max_timeout = MAX_WDT_TIMEOUT;
wdt->last_ping = jiffies;
wdt->sam9x60_support = of_device_is_compatible(dev->of_node,
"microchip,sam9x60-wdt");
watchdog_set_drvdata(wdd, wdt);
......@@ -224,15 +279,19 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
wdt->reg_base = regs;
irq = irq_of_parse_and_map(dev->of_node, 0);
if (!irq)
dev_warn(dev, "failed to get IRQ from DT\n");
ret = of_sama5d4_wdt_init(dev->of_node, wdt);
if (ret)
return ret;
if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
if (wdt->need_irq) {
irq = irq_of_parse_and_map(dev->of_node, 0);
if (!irq) {
dev_warn(dev, "failed to get IRQ from DT\n");
wdt->need_irq = false;
}
}
if (wdt->need_irq) {
ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler,
IRQF_SHARED | IRQF_IRQPOLL |
IRQF_NO_SUSPEND, pdev->name, pdev);
......@@ -244,11 +303,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(wdd, wdt_timeout, dev);
timeout = WDT_SEC2TICKS(wdd->timeout);
wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT));
wdt->mr |= AT91_WDT_SET_WDV(timeout);
ret = sama5d4_wdt_init(wdt);
if (ret)
return ret;
......@@ -269,7 +323,12 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
}
static const struct of_device_id sama5d4_wdt_of_match[] = {
{ .compatible = "atmel,sama5d4-wdt", },
{
.compatible = "atmel,sama5d4-wdt",
},
{
.compatible = "microchip,sam9x60-wdt",
},
{ }
};
MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
......
......@@ -262,6 +262,24 @@ static int stm32_iwdg_probe(struct platform_device *pdev)
watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
watchdog_init_timeout(wdd, 0, dev);
/*
* In case of CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED is set
* (Means U-Boot/bootloaders leaves the watchdog running)
* When we get here we should make a decision to prevent
* any side effects before user space daemon will take care of it.
* The best option, taking into consideration that there is no
* way to read values back from hardware, is to enforce watchdog
* being run with deterministic values.
*/
if (IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED)) {
ret = stm32_iwdg_start(wdd);
if (ret)
return ret;
/* Make sure the watchdog is serviced */
set_bit(WDOG_HW_RUNNING, &wdd->status);
}
ret = devm_watchdog_register_device(dev, wdd);
if (ret)
return ret;
......
......@@ -147,6 +147,25 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
}
EXPORT_SYMBOL_GPL(watchdog_init_timeout);
static int watchdog_reboot_notifier(struct notifier_block *nb,
unsigned long code, void *data)
{
struct watchdog_device *wdd;
wdd = container_of(nb, struct watchdog_device, reboot_nb);
if (code == SYS_DOWN || code == SYS_HALT) {
if (watchdog_active(wdd)) {
int ret;
ret = wdd->ops->stop(wdd);
if (ret)
return NOTIFY_BAD;
}
}
return NOTIFY_DONE;
}
static int watchdog_restart_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
......@@ -235,6 +254,19 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
}
}
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) {
wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;
ret = register_reboot_notifier(&wdd->reboot_nb);
if (ret) {
pr_err("watchdog%d: Cannot register reboot notifier (%d)\n",
wdd->id, ret);
watchdog_dev_unregister(wdd);
ida_simple_remove(&watchdog_ida, id);
return ret;
}
}
if (wdd->ops->restart) {
wdd->restart_nb.notifier_call = watchdog_restart_notifier;
......@@ -289,6 +321,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd)
if (wdd->ops->restart)
unregister_restart_handler(&wdd->restart_nb);
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status))
unregister_reboot_notifier(&wdd->reboot_nb);
watchdog_dev_unregister(wdd);
ida_simple_remove(&watchdog_ida, wdd->id);
}
......
......@@ -38,7 +38,6 @@
#include <linux/miscdevice.h> /* For handling misc devices */
#include <linux/module.h> /* For module stuff/... */
#include <linux/mutex.h> /* For mutexes */
#include <linux/reboot.h> /* For reboot notifier */
#include <linux/slab.h> /* For memory functions */
#include <linux/types.h> /* For standard types (like size_t) */
#include <linux/watchdog.h> /* For watchdog specific items */
......@@ -1097,25 +1096,6 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
put_device(&wd_data->dev);
}
static int watchdog_reboot_notifier(struct notifier_block *nb,
unsigned long code, void *data)
{
struct watchdog_device *wdd;
wdd = container_of(nb, struct watchdog_device, reboot_nb);
if (code == SYS_DOWN || code == SYS_HALT) {
if (watchdog_active(wdd)) {
int ret;
ret = wdd->ops->stop(wdd);
if (ret)
return NOTIFY_BAD;
}
}
return NOTIFY_DONE;
}
/*
* watchdog_dev_register: register a watchdog device
* @wdd: watchdog device
......@@ -1134,22 +1114,8 @@ int watchdog_dev_register(struct watchdog_device *wdd)
return ret;
ret = watchdog_register_pretimeout(wdd);
if (ret) {
if (ret)
watchdog_cdev_unregister(wdd);
return ret;
}
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) {
wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;
ret = devm_register_reboot_notifier(&wdd->wd_data->dev,
&wdd->reboot_nb);
if (ret) {
pr_err("watchdog%d: Cannot register reboot notifier (%d)\n",
wdd->id, ret);
watchdog_dev_unregister(wdd);
}
}
return ret;
}
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Yong Liang <yong.liang@mediatek.com>
*/
#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT2712
#define _DT_BINDINGS_RESET_CONTROLLER_MT2712
#define MT2712_TOPRGU_INFRA_SW_RST 0
#define MT2712_TOPRGU_MM_SW_RST 1
#define MT2712_TOPRGU_MFG_SW_RST 2
#define MT2712_TOPRGU_VENC_SW_RST 3
#define MT2712_TOPRGU_VDEC_SW_RST 4
#define MT2712_TOPRGU_IMG_SW_RST 5
#define MT2712_TOPRGU_INFRA_AO_SW_RST 8
#define MT2712_TOPRGU_USB_SW_RST 9
#define MT2712_TOPRGU_APMIXED_SW_RST 10
#define MT2712_TOPRGU_SW_RST_NUM 11
#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT2712 */
......@@ -78,4 +78,21 @@
#define MT8183_INFRACFG_AO_I2C7_SW_RST 126
#define MT8183_INFRACFG_AO_I2C8_SW_RST 127
#define MT8183_INFRACFG_SW_RST_NUM 128
#define MT8183_TOPRGU_MM_SW_RST 1
#define MT8183_TOPRGU_MFG_SW_RST 2
#define MT8183_TOPRGU_VENC_SW_RST 3
#define MT8183_TOPRGU_VDEC_SW_RST 4
#define MT8183_TOPRGU_IMG_SW_RST 5
#define MT8183_TOPRGU_MD_SW_RST 7
#define MT8183_TOPRGU_CONN_SW_RST 9
#define MT8183_TOPRGU_CONN_MCU_SW_RST 12
#define MT8183_TOPRGU_IPU0_SW_RST 14
#define MT8183_TOPRGU_IPU1_SW_RST 15
#define MT8183_TOPRGU_AUDIO_SW_RST 17
#define MT8183_TOPRGU_CAMSYS_SW_RST 18
#define MT8183_TOPRGU_SW_RST_NUM 19
#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8183 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment