Commit 4c00e1e2 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull watchdog updates from Wim Van Sebroeck:

 - add Mediatek MT7986 & MT8195 wdt support

 - add Maxim MAX63xx

 - drop bd70528 support

 - rewrite ixp4xx to watchdog framework

 - constify static struct watchdog_ops for sl28cpld_wdt, mpc8xxx_wdt and
   tqmx86

 - introduce watchdog_dev_suspend/resume

 - several fixes and improvements

* tag 'linux-watchdog-5.15-rc1' of git://www.linux-watchdog.org/linux-watchdog:
  dt-bindings: watchdog: Add compatible for Mediatek MT7986
  watchdog: ixp4xx: Rewrite driver to use core
  watchdog: Start watchdog in watchdog_set_last_hw_keepalive only if appropriate
  watchdog: max63xx_wdt: Add device tree probing
  dt-bindings: watchdog: Add Maxim MAX63xx bindings
  watchdog: mediatek: mt8195: add wdt support
  dt-bindings: reset: mt8195: add toprgu reset-controller header file
  watchdog: tqmx86: Constify static struct watchdog_ops
  watchdog: mpc8xxx_wdt: Constify static struct watchdog_ops
  watchdog: sl28cpld_wdt: Constify static struct watchdog_ops
  watchdog: iTCO_wdt: Fix detection of SMI-off case
  watchdog: bcm2835_wdt: consider system-power-controller property
  watchdog: imx2_wdg: notify wdog core to stop ping worker on suspend
  watchdog: introduce watchdog_dev_suspend/resume
  watchdog: Fix NULL pointer dereference when releasing cdev
  watchdog: only run driver set_pretimeout op if device supports it
  watchdog: bd70528 drop bd70528 support
parents 192ad3c2 41e73feb
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/maxim,max63xx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim 63xx Watchdog Timers
allOf:
- $ref: "watchdog.yaml#"
maintainers:
- Marc Zyngier <maz@kernel.org>
- Linus Walleij <linus.walleij@linaro.org>
properties:
compatible:
oneOf:
- const: maxim,max6369
- const: maxim,max6370
- const: maxim,max6371
- const: maxim,max6372
- const: maxim,max6373
- const: maxim,max6374
reg:
description: This is a 1-byte memory-mapped address
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
wdt: watchdog@50000000 {
compatible = "maxim,max6369";
reg = <0x50000000 0x1>;
timeout-sec = <10>;
};
...
...@@ -13,6 +13,7 @@ Required properties: ...@@ -13,6 +13,7 @@ Required properties:
"mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622 "mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622
"mediatek,mt7623-wdt", "mediatek,mt6589-wdt": for MT7623 "mediatek,mt7623-wdt", "mediatek,mt6589-wdt": for MT7623
"mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629 "mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629
"mediatek,mt7986-wdt", "mediatek,mt6589-wdt": for MT7986
"mediatek,mt8183-wdt": for MT8183 "mediatek,mt8183-wdt": for MT8183
"mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516 "mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516
"mediatek,mt8192-wdt": for MT8192 "mediatek,mt8192-wdt": for MT8192
......
...@@ -168,18 +168,6 @@ config SOFT_WATCHDOG_PRETIMEOUT ...@@ -168,18 +168,6 @@ config SOFT_WATCHDOG_PRETIMEOUT
watchdog. Be aware that governors might affect the watchdog because it watchdog. Be aware that governors might affect the watchdog because it
is purely software, e.g. the panic governor will stall it! is purely software, e.g. the panic governor will stall it!
config BD70528_WATCHDOG
tristate "ROHM BD70528 PMIC Watchdog"
depends on MFD_ROHM_BD70528
select WATCHDOG_CORE
help
Support for the watchdog in the ROHM BD70528 PMIC. Watchdog trigger
cause system reset.
Say Y here to include support for the ROHM BD70528 watchdog.
Alternatively say M to compile the driver as a module,
which will be called bd70528_wdt.
config BD957XMUF_WATCHDOG config BD957XMUF_WATCHDOG
tristate "ROHM BD9576MUF and BD9573MUF PMIC Watchdog" tristate "ROHM BD9576MUF and BD9573MUF PMIC Watchdog"
depends on MFD_ROHM_BD957XMUF depends on MFD_ROHM_BD957XMUF
......
...@@ -204,7 +204,6 @@ obj-$(CONFIG_WATCHDOG_SUN4V) += sun4v_wdt.o ...@@ -204,7 +204,6 @@ obj-$(CONFIG_WATCHDOG_SUN4V) += sun4v_wdt.o
obj-$(CONFIG_XEN_WDT) += xen_wdt.o obj-$(CONFIG_XEN_WDT) += xen_wdt.o
# Architecture Independent # Architecture Independent
obj-$(CONFIG_BD70528_WATCHDOG) += bd70528_wdt.o
obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o
obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
......
...@@ -205,9 +205,13 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) ...@@ -205,9 +205,13 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
if (err) if (err)
return err; return err;
if (pm_power_off == NULL) { if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) {
pm_power_off = bcm2835_power_off; if (!pm_power_off) {
bcm2835_power_off_wdt = wdt; pm_power_off = bcm2835_power_off;
bcm2835_power_off_wdt = wdt;
} else {
dev_info(dev, "Poweroff handler already present!\n");
}
} }
dev_info(dev, "Broadcom BCM2835 watchdog timer"); dev_info(dev, "Broadcom BCM2835 watchdog timer");
......
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 ROHM Semiconductors
// ROHM BD70528MWV watchdog driver
#include <linux/bcd.h>
#include <linux/kernel.h>
#include <linux/mfd/rohm-bd70528.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/watchdog.h>
/*
* Max time we can set is 1 hour, 59 minutes and 59 seconds
* and Minimum time is 1 second
*/
#define WDT_MAX_MS ((2 * 60 * 60 - 1) * 1000)
#define WDT_MIN_MS 1000
#define DEFAULT_TIMEOUT 60
#define WD_CTRL_MAGIC1 0x55
#define WD_CTRL_MAGIC2 0xAA
struct wdtbd70528 {
struct device *dev;
struct regmap *regmap;
struct rohm_regmap_dev *mfd;
struct watchdog_device wdt;
};
/**
* bd70528_wdt_set - arm or disarm watchdog timer
*
* @data: device data for the PMIC instance we want to operate on
* @enable: new state of WDT. zero to disable, non zero to enable
* @old_state: previous state of WDT will be filled here
*
* Arm or disarm WDT on BD70528 PMIC. Expected to be called only by
* BD70528 RTC and BD70528 WDT drivers. The rtc_timer_lock must be taken
* by calling bd70528_wdt_lock before calling bd70528_wdt_set.
*/
int bd70528_wdt_set(struct rohm_regmap_dev *data, int enable, int *old_state)
{
int ret, i;
unsigned int tmp;
struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
chip);
u8 wd_ctrl_arr[3] = { WD_CTRL_MAGIC1, WD_CTRL_MAGIC2, 0 };
u8 *wd_ctrl = &wd_ctrl_arr[2];
ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp);
if (ret)
return ret;
*wd_ctrl = (u8)tmp;
if (old_state) {
if (*wd_ctrl & BD70528_MASK_WDT_EN)
*old_state |= BD70528_WDT_STATE_BIT;
else
*old_state &= ~BD70528_WDT_STATE_BIT;
if ((!enable) == (!(*old_state & BD70528_WDT_STATE_BIT)))
return 0;
}
if (enable) {
if (*wd_ctrl & BD70528_MASK_WDT_EN)
return 0;
*wd_ctrl |= BD70528_MASK_WDT_EN;
} else {
if (*wd_ctrl & BD70528_MASK_WDT_EN)
*wd_ctrl &= ~BD70528_MASK_WDT_EN;
else
return 0;
}
for (i = 0; i < 3; i++) {
ret = regmap_write(bd70528->chip.regmap, BD70528_REG_WDT_CTRL,
wd_ctrl_arr[i]);
if (ret)
return ret;
}
ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp);
if ((tmp & BD70528_MASK_WDT_EN) != (*wd_ctrl & BD70528_MASK_WDT_EN)) {
dev_err(bd70528->chip.dev,
"Watchdog ctrl mismatch (hw) 0x%x (set) 0x%x\n",
tmp, *wd_ctrl);
ret = -EIO;
}
return ret;
}
EXPORT_SYMBOL(bd70528_wdt_set);
/**
* bd70528_wdt_lock - take WDT lock
*
* @data: device data for the PMIC instance we want to operate on
*
* Lock WDT for arming/disarming in order to avoid race condition caused
* by WDT state changes initiated by WDT and RTC drivers.
*/
void bd70528_wdt_lock(struct rohm_regmap_dev *data)
{
struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
chip);
mutex_lock(&bd70528->rtc_timer_lock);
}
EXPORT_SYMBOL(bd70528_wdt_lock);
/**
* bd70528_wdt_unlock - unlock WDT lock
*
* @data: device data for the PMIC instance we want to operate on
*
* Unlock WDT lock which has previously been taken by call to
* bd70528_wdt_lock.
*/
void bd70528_wdt_unlock(struct rohm_regmap_dev *data)
{
struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
chip);
mutex_unlock(&bd70528->rtc_timer_lock);
}
EXPORT_SYMBOL(bd70528_wdt_unlock);
static int bd70528_wdt_set_locked(struct wdtbd70528 *w, int enable)
{
return bd70528_wdt_set(w->mfd, enable, NULL);
}
static int bd70528_wdt_change(struct wdtbd70528 *w, int enable)
{
int ret;
bd70528_wdt_lock(w->mfd);
ret = bd70528_wdt_set_locked(w, enable);
bd70528_wdt_unlock(w->mfd);
return ret;
}
static int bd70528_wdt_start(struct watchdog_device *wdt)
{
struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
dev_dbg(w->dev, "WDT ping...\n");
return bd70528_wdt_change(w, 1);
}
static int bd70528_wdt_stop(struct watchdog_device *wdt)
{
struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
dev_dbg(w->dev, "WDT stopping...\n");
return bd70528_wdt_change(w, 0);
}
static int bd70528_wdt_set_timeout(struct watchdog_device *wdt,
unsigned int timeout)
{
unsigned int hours;
unsigned int minutes;
unsigned int seconds;
int ret;
struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
seconds = timeout;
hours = timeout / (60 * 60);
/* Maximum timeout is 1h 59m 59s => hours is 1 or 0 */
if (hours)
seconds -= (60 * 60);
minutes = seconds / 60;
seconds = seconds % 60;
bd70528_wdt_lock(w->mfd);
ret = bd70528_wdt_set_locked(w, 0);
if (ret)
goto out_unlock;
ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_HOUR,
BD70528_MASK_WDT_HOUR, hours);
if (ret) {
dev_err(w->dev, "Failed to set WDT hours\n");
goto out_en_unlock;
}
ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_MINUTE,
BD70528_MASK_WDT_MINUTE, bin2bcd(minutes));
if (ret) {
dev_err(w->dev, "Failed to set WDT minutes\n");
goto out_en_unlock;
}
ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_SEC,
BD70528_MASK_WDT_SEC, bin2bcd(seconds));
if (ret)
dev_err(w->dev, "Failed to set WDT seconds\n");
else
dev_dbg(w->dev, "WDT tmo set to %u\n", timeout);
out_en_unlock:
ret = bd70528_wdt_set_locked(w, 1);
out_unlock:
bd70528_wdt_unlock(w->mfd);
return ret;
}
static const struct watchdog_info bd70528_wdt_info = {
.identity = "bd70528-wdt",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
};
static const struct watchdog_ops bd70528_wdt_ops = {
.start = bd70528_wdt_start,
.stop = bd70528_wdt_stop,
.set_timeout = bd70528_wdt_set_timeout,
};
static int bd70528_wdt_probe(struct platform_device *pdev)
{
struct rohm_regmap_dev *bd70528;
struct wdtbd70528 *w;
int ret;
unsigned int reg;
bd70528 = dev_get_drvdata(pdev->dev.parent);
if (!bd70528) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
w = devm_kzalloc(&pdev->dev, sizeof(*w), GFP_KERNEL);
if (!w)
return -ENOMEM;
w->regmap = bd70528->regmap;
w->mfd = bd70528;
w->dev = &pdev->dev;
w->wdt.info = &bd70528_wdt_info;
w->wdt.ops = &bd70528_wdt_ops;
w->wdt.min_hw_heartbeat_ms = WDT_MIN_MS;
w->wdt.max_hw_heartbeat_ms = WDT_MAX_MS;
w->wdt.parent = pdev->dev.parent;
w->wdt.timeout = DEFAULT_TIMEOUT;
watchdog_set_drvdata(&w->wdt, w);
watchdog_init_timeout(&w->wdt, 0, pdev->dev.parent);
ret = bd70528_wdt_set_timeout(&w->wdt, w->wdt.timeout);
if (ret) {
dev_err(&pdev->dev, "Failed to set the watchdog timeout\n");
return ret;
}
bd70528_wdt_lock(w->mfd);
ret = regmap_read(w->regmap, BD70528_REG_WDT_CTRL, &reg);
bd70528_wdt_unlock(w->mfd);
if (ret) {
dev_err(&pdev->dev, "Failed to get the watchdog state\n");
return ret;
}
if (reg & BD70528_MASK_WDT_EN) {
dev_dbg(&pdev->dev, "watchdog was running during probe\n");
set_bit(WDOG_HW_RUNNING, &w->wdt.status);
}
ret = devm_watchdog_register_device(&pdev->dev, &w->wdt);
if (ret < 0)
dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret);
return ret;
}
static struct platform_driver bd70528_wdt = {
.driver = {
.name = "bd70528-wdt"
},
.probe = bd70528_wdt_probe,
};
module_platform_driver(bd70528_wdt);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("BD70528 watchdog driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bd70528-wdt");
...@@ -362,7 +362,7 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) ...@@ -362,7 +362,7 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
* Otherwise, the BIOS generally reboots when the SMI triggers. * Otherwise, the BIOS generally reboots when the SMI triggers.
*/ */
if (p->smi_res && if (p->smi_res &&
(SMI_EN(p) & (TCO_EN | GBL_SMI_EN)) != (TCO_EN | GBL_SMI_EN)) (inl(SMI_EN(p)) & (TCO_EN | GBL_SMI_EN)) != (TCO_EN | GBL_SMI_EN))
tmrval /= 2; tmrval /= 2;
/* from the specs: */ /* from the specs: */
......
...@@ -317,6 +317,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -317,6 +317,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(wdog, nowayout); watchdog_set_nowayout(wdog, nowayout);
watchdog_set_restart_priority(wdog, 128); watchdog_set_restart_priority(wdog, 128);
watchdog_init_timeout(wdog, timeout, dev); watchdog_init_timeout(wdog, timeout, dev);
watchdog_stop_ping_on_suspend(wdog);
if (imx2_wdt_is_running(wdev)) { if (imx2_wdt_is_running(wdev)) {
imx2_wdt_set_timeout(wdog, wdog->timeout); imx2_wdt_set_timeout(wdog, wdog->timeout);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/property.h>
#define DEFAULT_HEARTBEAT 60 #define DEFAULT_HEARTBEAT 60
#define MAX_HEARTBEAT 60 #define MAX_HEARTBEAT 60
...@@ -99,8 +100,8 @@ static const struct max63xx_timeout max6373_table[] = { ...@@ -99,8 +100,8 @@ static const struct max63xx_timeout max6373_table[] = {
{ }, { },
}; };
static struct max63xx_timeout * static const struct max63xx_timeout *
max63xx_select_timeout(struct max63xx_timeout *table, int value) max63xx_select_timeout(const struct max63xx_timeout *table, int value)
{ {
while (table->twd) { while (table->twd) {
if (value <= table->twd) { if (value <= table->twd) {
...@@ -202,14 +203,17 @@ static int max63xx_wdt_probe(struct platform_device *pdev) ...@@ -202,14 +203,17 @@ static int max63xx_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct max63xx_wdt *wdt; struct max63xx_wdt *wdt;
struct max63xx_timeout *table; const struct max63xx_timeout *table;
int err; int err;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) if (!wdt)
return -ENOMEM; return -ENOMEM;
table = (struct max63xx_timeout *)pdev->id_entry->driver_data; /* Attempt to use fwnode first */
table = device_get_match_data(dev);
if (!table)
table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
heartbeat = DEFAULT_HEARTBEAT; heartbeat = DEFAULT_HEARTBEAT;
...@@ -255,11 +259,23 @@ static const struct platform_device_id max63xx_id_table[] = { ...@@ -255,11 +259,23 @@ static const struct platform_device_id max63xx_id_table[] = {
}; };
MODULE_DEVICE_TABLE(platform, max63xx_id_table); MODULE_DEVICE_TABLE(platform, max63xx_id_table);
static const struct of_device_id max63xx_dt_id_table[] = {
{ .compatible = "maxim,max6369", .data = max6369_table, },
{ .compatible = "maxim,max6370", .data = max6369_table, },
{ .compatible = "maxim,max6371", .data = max6371_table, },
{ .compatible = "maxim,max6372", .data = max6371_table, },
{ .compatible = "maxim,max6373", .data = max6373_table, },
{ .compatible = "maxim,max6374", .data = max6373_table, },
{ }
};
MODULE_DEVICE_TABLE(of, max63xx_dt_id_table);
static struct platform_driver max63xx_wdt_driver = { static struct platform_driver max63xx_wdt_driver = {
.probe = max63xx_wdt_probe, .probe = max63xx_wdt_probe,
.id_table = max63xx_id_table, .id_table = max63xx_id_table,
.driver = { .driver = {
.name = "max63xx_wdt", .name = "max63xx_wdt",
.of_match_table = max63xx_dt_id_table,
}, },
}; };
......
...@@ -118,7 +118,7 @@ static struct watchdog_info mpc8xxx_wdt_info = { ...@@ -118,7 +118,7 @@ static struct watchdog_info mpc8xxx_wdt_info = {
.identity = "MPC8xxx", .identity = "MPC8xxx",
}; };
static struct watchdog_ops mpc8xxx_wdt_ops = { static const struct watchdog_ops mpc8xxx_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = mpc8xxx_wdt_start, .start = mpc8xxx_wdt_start,
.ping = mpc8xxx_wdt_ping, .ping = mpc8xxx_wdt_ping,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <dt-bindings/reset-controller/mt2712-resets.h> #include <dt-bindings/reset-controller/mt2712-resets.h>
#include <dt-bindings/reset-controller/mt8183-resets.h> #include <dt-bindings/reset-controller/mt8183-resets.h>
#include <dt-bindings/reset-controller/mt8192-resets.h> #include <dt-bindings/reset-controller/mt8192-resets.h>
#include <dt-bindings/reset/mt8195-resets.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -82,6 +83,10 @@ static const struct mtk_wdt_data mt8192_data = { ...@@ -82,6 +83,10 @@ static const struct mtk_wdt_data mt8192_data = {
.toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM, .toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM,
}; };
static const struct mtk_wdt_data mt8195_data = {
.toprgu_sw_rst_num = MT8195_TOPRGU_SW_RST_NUM,
};
static int toprgu_reset_update(struct reset_controller_dev *rcdev, static int toprgu_reset_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert) unsigned long id, bool assert)
{ {
...@@ -408,6 +413,7 @@ static const struct of_device_id mtk_wdt_dt_ids[] = { ...@@ -408,6 +413,7 @@ static const struct of_device_id mtk_wdt_dt_ids[] = {
{ .compatible = "mediatek,mt6589-wdt" }, { .compatible = "mediatek,mt6589-wdt" },
{ .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data }, { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data },
{ .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data }, { .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data },
{ .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);
......
...@@ -108,7 +108,7 @@ static const struct watchdog_info sl28cpld_wdt_info = { ...@@ -108,7 +108,7 @@ static const struct watchdog_info sl28cpld_wdt_info = {
.identity = "sl28cpld watchdog", .identity = "sl28cpld watchdog",
}; };
static struct watchdog_ops sl28cpld_wdt_ops = { static const struct watchdog_ops sl28cpld_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = sl28cpld_wdt_start, .start = sl28cpld_wdt_start,
.stop = sl28cpld_wdt_stop, .stop = sl28cpld_wdt_stop,
......
...@@ -62,7 +62,7 @@ static const struct watchdog_info tqmx86_wdt_info = { ...@@ -62,7 +62,7 @@ static const struct watchdog_info tqmx86_wdt_info = {
.identity = "TQMx86 Watchdog", .identity = "TQMx86 Watchdog",
}; };
static struct watchdog_ops tqmx86_wdt_ops = { static const struct watchdog_ops tqmx86_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = tqmx86_wdt_start, .start = tqmx86_wdt_start,
.set_timeout = tqmx86_wdt_set_timeout, .set_timeout = tqmx86_wdt_set_timeout,
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <linux/idr.h> /* For ida_* macros */ #include <linux/idr.h> /* For ida_* macros */
#include <linux/err.h> /* For IS_ERR macros */ #include <linux/err.h> /* For IS_ERR macros */
#include <linux/of.h> /* For of_get_timeout_sec */ #include <linux/of.h> /* For of_get_timeout_sec */
#include <linux/suspend.h>
#include "watchdog_core.h" /* For watchdog_dev_register/... */ #include "watchdog_core.h" /* For watchdog_dev_register/... */
...@@ -185,6 +186,33 @@ static int watchdog_restart_notifier(struct notifier_block *nb, ...@@ -185,6 +186,33 @@ static int watchdog_restart_notifier(struct notifier_block *nb,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static int watchdog_pm_notifier(struct notifier_block *nb, unsigned long mode,
void *data)
{
struct watchdog_device *wdd;
int ret = 0;
wdd = container_of(nb, struct watchdog_device, pm_nb);
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
ret = watchdog_dev_suspend(wdd);
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
ret = watchdog_dev_resume(wdd);
break;
}
if (ret)
return NOTIFY_BAD;
return NOTIFY_DONE;
}
/** /**
* watchdog_set_restart_priority - Change priority of restart handler * watchdog_set_restart_priority - Change priority of restart handler
* @wdd: watchdog device * @wdd: watchdog device
...@@ -292,6 +320,15 @@ static int __watchdog_register_device(struct watchdog_device *wdd) ...@@ -292,6 +320,15 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
wdd->id, ret); wdd->id, ret);
} }
if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) {
wdd->pm_nb.notifier_call = watchdog_pm_notifier;
ret = register_pm_notifier(&wdd->pm_nb);
if (ret)
pr_warn("watchdog%d: Cannot register pm handler (%d)\n",
wdd->id, ret);
}
return 0; return 0;
} }
......
...@@ -401,7 +401,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd, ...@@ -401,7 +401,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd,
if (watchdog_pretimeout_invalid(wdd, timeout)) if (watchdog_pretimeout_invalid(wdd, timeout))
return -EINVAL; return -EINVAL;
if (wdd->ops->set_pretimeout) if (wdd->ops->set_pretimeout && (wdd->info->options & WDIOF_PRETIMEOUT))
err = wdd->ops->set_pretimeout(wdd, timeout); err = wdd->ops->set_pretimeout(wdd, timeout);
else else
wdd->pretimeout = timeout; wdd->pretimeout = timeout;
...@@ -1096,6 +1096,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) ...@@ -1096,6 +1096,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
watchdog_stop(wdd); watchdog_stop(wdd);
} }
watchdog_hrtimer_pretimeout_stop(wdd);
mutex_lock(&wd_data->lock); mutex_lock(&wd_data->lock);
wd_data->wdd = NULL; wd_data->wdd = NULL;
wdd->wd_data = NULL; wdd->wd_data = NULL;
...@@ -1103,7 +1105,6 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) ...@@ -1103,7 +1105,6 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
hrtimer_cancel(&wd_data->timer); hrtimer_cancel(&wd_data->timer);
kthread_cancel_work_sync(&wd_data->work); kthread_cancel_work_sync(&wd_data->work);
watchdog_hrtimer_pretimeout_stop(wdd);
put_device(&wd_data->dev); put_device(&wd_data->dev);
} }
...@@ -1172,7 +1173,10 @@ int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd, ...@@ -1172,7 +1173,10 @@ int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd,
wd_data->last_hw_keepalive = ktime_sub(now, ms_to_ktime(last_ping_ms)); wd_data->last_hw_keepalive = ktime_sub(now, ms_to_ktime(last_ping_ms));
return __watchdog_ping(wdd); if (watchdog_hw_running(wdd) && handle_boot_enabled)
return __watchdog_ping(wdd);
return 0;
} }
EXPORT_SYMBOL_GPL(watchdog_set_last_hw_keepalive); EXPORT_SYMBOL_GPL(watchdog_set_last_hw_keepalive);
...@@ -1227,6 +1231,53 @@ void __exit watchdog_dev_exit(void) ...@@ -1227,6 +1231,53 @@ void __exit watchdog_dev_exit(void)
kthread_destroy_worker(watchdog_kworker); kthread_destroy_worker(watchdog_kworker);
} }
int watchdog_dev_suspend(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
int ret = 0;
if (!wdd->wd_data)
return -ENODEV;
/* ping for the last time before suspend */
mutex_lock(&wd_data->lock);
if (watchdog_worker_should_ping(wd_data))
ret = __watchdog_ping(wd_data->wdd);
mutex_unlock(&wd_data->lock);
if (ret)
return ret;
/*
* make sure that watchdog worker will not kick in when the wdog is
* suspended
*/
hrtimer_cancel(&wd_data->timer);
kthread_cancel_work_sync(&wd_data->work);
return 0;
}
int watchdog_dev_resume(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
int ret = 0;
if (!wdd->wd_data)
return -ENODEV;
/*
* __watchdog_ping will also retrigger hrtimer and therefore restore the
* ping worker if needed.
*/
mutex_lock(&wd_data->lock);
if (watchdog_worker_should_ping(wd_data))
ret = __watchdog_ping(wd_data->wdd);
mutex_unlock(&wd_data->lock);
return ret;
}
module_param(handle_boot_enabled, bool, 0444); module_param(handle_boot_enabled, bool, 0444);
MODULE_PARM_DESC(handle_boot_enabled, MODULE_PARM_DESC(handle_boot_enabled,
"Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default=" "Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default="
......
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)*/
/*
* Copyright (c) 2021 MediaTek Inc.
* Author: Christine Zhu <christine.zhu@mediatek.com>
*/
#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8195
#define _DT_BINDINGS_RESET_CONTROLLER_MT8195
#define MT8195_TOPRGU_CONN_MCU_SW_RST 0
#define MT8195_TOPRGU_INFRA_GRST_SW_RST 1
#define MT8195_TOPRGU_APU_SW_RST 2
#define MT8195_TOPRGU_INFRA_AO_GRST_SW_RST 6
#define MT8195_TOPRGU_MMSYS_SW_RST 7
#define MT8195_TOPRGU_MFG_SW_RST 8
#define MT8195_TOPRGU_VENC_SW_RST 9
#define MT8195_TOPRGU_VDEC_SW_RST 10
#define MT8195_TOPRGU_IMG_SW_RST 11
#define MT8195_TOPRGU_APMIXEDSYS_SW_RST 13
#define MT8195_TOPRGU_AUDIO_SW_RST 14
#define MT8195_TOPRGU_CAMSYS_SW_RST 15
#define MT8195_TOPRGU_EDPTX_SW_RST 16
#define MT8195_TOPRGU_ADSPSYS_SW_RST 21
#define MT8195_TOPRGU_DPTX_SW_RST 22
#define MT8195_TOPRGU_SPMI_MST_SW_RST 23
#define MT8195_TOPRGU_SW_RST_NUM 16
#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8195 */
...@@ -107,6 +107,7 @@ struct watchdog_device { ...@@ -107,6 +107,7 @@ struct watchdog_device {
unsigned int max_hw_heartbeat_ms; unsigned int max_hw_heartbeat_ms;
struct notifier_block reboot_nb; struct notifier_block reboot_nb;
struct notifier_block restart_nb; struct notifier_block restart_nb;
struct notifier_block pm_nb;
void *driver_data; void *driver_data;
struct watchdog_core_data *wd_data; struct watchdog_core_data *wd_data;
unsigned long status; unsigned long status;
...@@ -116,6 +117,7 @@ struct watchdog_device { ...@@ -116,6 +117,7 @@ struct watchdog_device {
#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */
#define WDOG_HW_RUNNING 3 /* True if HW watchdog running */ #define WDOG_HW_RUNNING 3 /* True if HW watchdog running */
#define WDOG_STOP_ON_UNREGISTER 4 /* Should be stopped on unregister */ #define WDOG_STOP_ON_UNREGISTER 4 /* Should be stopped on unregister */
#define WDOG_NO_PING_ON_SUSPEND 5 /* Ping worker should be stopped on suspend */
struct list_head deferred; struct list_head deferred;
}; };
...@@ -156,6 +158,12 @@ static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd) ...@@ -156,6 +158,12 @@ static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd)
set_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status); set_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status);
} }
/* Use the following function to stop the wdog ping worker when suspending */
static inline void watchdog_stop_ping_on_suspend(struct watchdog_device *wdd)
{
set_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status);
}
/* Use the following function to check if a timeout value is invalid */ /* Use the following function to check if a timeout value is invalid */
static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t) static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t)
{ {
...@@ -209,6 +217,8 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, ...@@ -209,6 +217,8 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd,
unsigned int timeout_parm, struct device *dev); unsigned int timeout_parm, struct device *dev);
extern int watchdog_register_device(struct watchdog_device *); extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *);
int watchdog_dev_suspend(struct watchdog_device *wdd);
int watchdog_dev_resume(struct watchdog_device *wdd);
int watchdog_set_last_hw_keepalive(struct watchdog_device *, unsigned int); int watchdog_set_last_hw_keepalive(struct watchdog_device *, unsigned int);
......
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