mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
linux-watchdog 5.15-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAmE3t2UACgkQ+iyteGJfRspKrgCfVDZuMeFiDrRjPz9FMDUncIZ7 /24AoKWWPcOJLNXXNCwwD61kcW34LzVV =gMcs -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
4c00e1e2e5
@ -0,0 +1,44 @@
|
||||
# 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:
|
||||
"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,mt7986-wdt", "mediatek,mt6589-wdt": for MT7986
|
||||
"mediatek,mt8183-wdt": for MT8183
|
||||
"mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516
|
||||
"mediatek,mt8192-wdt": for MT8192
|
||||
|
@ -168,18 +168,6 @@ config SOFT_WATCHDOG_PRETIMEOUT
|
||||
watchdog. Be aware that governors might affect the watchdog because 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
|
||||
tristate "ROHM BD9576MUF and BD9573MUF PMIC Watchdog"
|
||||
depends on MFD_ROHM_BD957XMUF
|
||||
|
@ -204,7 +204,6 @@ obj-$(CONFIG_WATCHDOG_SUN4V) += sun4v_wdt.o
|
||||
obj-$(CONFIG_XEN_WDT) += xen_wdt.o
|
||||
|
||||
# Architecture Independent
|
||||
obj-$(CONFIG_BD70528_WATCHDOG) += bd70528_wdt.o
|
||||
obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o
|
||||
obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
|
||||
obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
|
||||
|
@ -205,9 +205,13 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (pm_power_off == NULL) {
|
||||
pm_power_off = bcm2835_power_off;
|
||||
bcm2835_power_off_wdt = wdt;
|
||||
if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) {
|
||||
if (!pm_power_off) {
|
||||
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");
|
||||
|
@ -1,291 +0,0 @@
|
||||
// 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, ®);
|
||||
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)
|
||||
* Otherwise, the BIOS generally reboots when the SMI triggers.
|
||||
*/
|
||||
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;
|
||||
|
||||
/* from the specs: */
|
||||
|
@ -317,6 +317,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_nowayout(wdog, nowayout);
|
||||
watchdog_set_restart_priority(wdog, 128);
|
||||
watchdog_init_timeout(wdog, timeout, dev);
|
||||
watchdog_stop_ping_on_suspend(wdog);
|
||||
|
||||
if (imx2_wdt_is_running(wdev)) {
|
||||
imx2_wdt_set_timeout(wdog, wdog->timeout);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#define DEFAULT_HEARTBEAT 60
|
||||
#define MAX_HEARTBEAT 60
|
||||
@ -99,8 +100,8 @@ static const struct max63xx_timeout max6373_table[] = {
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct max63xx_timeout *
|
||||
max63xx_select_timeout(struct max63xx_timeout *table, int value)
|
||||
static const struct max63xx_timeout *
|
||||
max63xx_select_timeout(const struct max63xx_timeout *table, int value)
|
||||
{
|
||||
while (table->twd) {
|
||||
if (value <= table->twd) {
|
||||
@ -202,14 +203,17 @@ static int max63xx_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct max63xx_wdt *wdt;
|
||||
struct max63xx_timeout *table;
|
||||
const struct max63xx_timeout *table;
|
||||
int err;
|
||||
|
||||
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
|
||||
if (!wdt)
|
||||
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)
|
||||
heartbeat = DEFAULT_HEARTBEAT;
|
||||
@ -255,11 +259,23 @@ static const struct platform_device_id 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 = {
|
||||
.probe = max63xx_wdt_probe,
|
||||
.id_table = max63xx_id_table,
|
||||
.driver = {
|
||||
.name = "max63xx_wdt",
|
||||
.of_match_table = max63xx_dt_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -118,7 +118,7 @@ static struct watchdog_info mpc8xxx_wdt_info = {
|
||||
.identity = "MPC8xxx",
|
||||
};
|
||||
|
||||
static struct watchdog_ops mpc8xxx_wdt_ops = {
|
||||
static const struct watchdog_ops mpc8xxx_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = mpc8xxx_wdt_start,
|
||||
.ping = mpc8xxx_wdt_ping,
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <dt-bindings/reset-controller/mt2712-resets.h>
|
||||
#include <dt-bindings/reset-controller/mt8183-resets.h>
|
||||
#include <dt-bindings/reset-controller/mt8192-resets.h>
|
||||
#include <dt-bindings/reset/mt8195-resets.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
@ -82,6 +83,10 @@ static const struct mtk_wdt_data mt8192_data = {
|
||||
.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,
|
||||
unsigned long id, bool assert)
|
||||
{
|
||||
@ -408,6 +413,7 @@ static const struct of_device_id mtk_wdt_dt_ids[] = {
|
||||
{ .compatible = "mediatek,mt6589-wdt" },
|
||||
{ .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data },
|
||||
{ .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data },
|
||||
{ .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);
|
||||
|
@ -108,7 +108,7 @@ static const struct watchdog_info sl28cpld_wdt_info = {
|
||||
.identity = "sl28cpld watchdog",
|
||||
};
|
||||
|
||||
static struct watchdog_ops sl28cpld_wdt_ops = {
|
||||
static const struct watchdog_ops sl28cpld_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = sl28cpld_wdt_start,
|
||||
.stop = sl28cpld_wdt_stop,
|
||||
|
@ -62,7 +62,7 @@ static const struct watchdog_info tqmx86_wdt_info = {
|
||||
.identity = "TQMx86 Watchdog",
|
||||
};
|
||||
|
||||
static struct watchdog_ops tqmx86_wdt_ops = {
|
||||
static const struct watchdog_ops tqmx86_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = tqmx86_wdt_start,
|
||||
.set_timeout = tqmx86_wdt_set_timeout,
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/idr.h> /* For ida_* macros */
|
||||
#include <linux/err.h> /* For IS_ERR macros */
|
||||
#include <linux/of.h> /* For of_get_timeout_sec */
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include "watchdog_core.h" /* For watchdog_dev_register/... */
|
||||
|
||||
@ -185,6 +186,33 @@ static int watchdog_restart_notifier(struct notifier_block *nb,
|
||||
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
|
||||
* @wdd: watchdog device
|
||||
@ -292,6 +320,15 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -401,7 +401,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd,
|
||||
if (watchdog_pretimeout_invalid(wdd, timeout))
|
||||
return -EINVAL;
|
||||
|
||||
if (wdd->ops->set_pretimeout)
|
||||
if (wdd->ops->set_pretimeout && (wdd->info->options & WDIOF_PRETIMEOUT))
|
||||
err = wdd->ops->set_pretimeout(wdd, timeout);
|
||||
else
|
||||
wdd->pretimeout = timeout;
|
||||
@ -1096,6 +1096,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
|
||||
watchdog_stop(wdd);
|
||||
}
|
||||
|
||||
watchdog_hrtimer_pretimeout_stop(wdd);
|
||||
|
||||
mutex_lock(&wd_data->lock);
|
||||
wd_data->wdd = NULL;
|
||||
wdd->wd_data = NULL;
|
||||
@ -1103,7 +1105,6 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
|
||||
|
||||
hrtimer_cancel(&wd_data->timer);
|
||||
kthread_cancel_work_sync(&wd_data->work);
|
||||
watchdog_hrtimer_pretimeout_stop(wdd);
|
||||
|
||||
put_device(&wd_data->dev);
|
||||
}
|
||||
@ -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));
|
||||
|
||||
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);
|
||||
|
||||
@ -1227,6 +1231,53 @@ void __exit watchdog_dev_exit(void)
|
||||
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_PARM_DESC(handle_boot_enabled,
|
||||
"Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default="
|
||||
|
29
include/dt-bindings/reset/mt8195-resets.h
Normal file
29
include/dt-bindings/reset/mt8195-resets.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* 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 {
|
||||
unsigned int max_hw_heartbeat_ms;
|
||||
struct notifier_block reboot_nb;
|
||||
struct notifier_block restart_nb;
|
||||
struct notifier_block pm_nb;
|
||||
void *driver_data;
|
||||
struct watchdog_core_data *wd_data;
|
||||
unsigned long status;
|
||||
@ -116,6 +117,7 @@ struct watchdog_device {
|
||||
#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */
|
||||
#define WDOG_HW_RUNNING 3 /* True if HW watchdog running */
|
||||
#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;
|
||||
};
|
||||
|
||||
@ -156,6 +158,12 @@ static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd)
|
||||
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 */
|
||||
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,
|
||||
unsigned int timeout_parm, struct device *dev);
|
||||
extern int watchdog_register_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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user