mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 00:33:16 +00:00
linux-watchdog 6.13-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAmdO8FgACgkQ+iyteGJfRsqdmgCgqPArbZweV/LTPcZXx89MaIEd hG8AoLrh8n5L08d6r5v1sII5To4KoZI7 =IX0/ -----END PGP SIGNATURE----- Merge tag 'linux-watchdog-6.13-rc1' of git://www.linux-watchdog.org/linux-watchdog Pull watchdog updates from Wim Van Sebroeck: - Add support for exynosautov920 SoC - Add support for Airoha EN7851 watchdog - Add support for MT6735 TOPRGU/WDT - Delete the cpu5wdt driver - Always print when registering watchdog fails - Several other small fixes and improvements * tag 'linux-watchdog-6.13-rc1' of git://www.linux-watchdog.org/linux-watchdog: (36 commits) watchdog: rti: of: honor timeout-sec property watchdog: s3c2410_wdt: add support for exynosautov920 SoC dt-bindings: watchdog: Document ExynosAutoV920 watchdog bindings watchdog: mediatek: Add support for MT6735 TOPRGU/WDT watchdog: mediatek: Make sure system reset gets asserted in mtk_wdt_restart() dt-bindings: watchdog: fsl-imx-wdt: Add missing 'big-endian' property dt-bindings: watchdog: Document Qualcomm QCS8300 docs: ABI: Fix spelling mistake in pretimeout_avaialable_governors Revert "watchdog: s3c2410_wdt: use exynos_get_pmu_regmap_by_phandle() for PMU regs" watchdog: rzg2l_wdt: Power on the watchdog domain in the restart handler watchdog: Switch back to struct platform_driver::remove() watchdog: it87_wdt: add PWRGD enable quirk for Qotom QCML04 watchdog: da9063: Remove __maybe_unused notations watchdog: da9063: Do not use a global variable watchdog: Delete the cpu5wdt driver watchdog: Add support for Airoha EN7851 watchdog dt-bindings: watchdog: airoha: document watchdog for Airoha EN7581 watchdog: sl28cpld_wdt: don't print out if registering watchdog fails watchdog: rza_wdt: don't print out if registering watchdog fails watchdog: rti_wdt: don't print out if registering watchdog fails ...
This commit is contained in:
commit
42d52acfb1
@ -76,7 +76,7 @@ Description:
|
||||
timeout when the pretimeout interrupt is delivered. Pretimeout
|
||||
is an optional feature.
|
||||
|
||||
What: /sys/class/watchdog/watchdogn/pretimeout_avaialable_governors
|
||||
What: /sys/class/watchdog/watchdogn/pretimeout_available_governors
|
||||
Date: February 2017
|
||||
Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||
Description:
|
||||
|
@ -0,0 +1,47 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/airoha,en7581-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Airoha EN7581 Watchdog Timer
|
||||
|
||||
maintainers:
|
||||
- Christian Marangi <ansuelsmth@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: airoha,en7581-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description: BUS clock (timer ticks at half the BUS clock)
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: bus
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/en7523-clk.h>
|
||||
|
||||
watchdog@1fbf0100 {
|
||||
compatible = "airoha,en7581-wdt";
|
||||
reg = <0x1fbf0100 0x3c>;
|
||||
|
||||
clocks = <&scuclk EN7523_CLK_BUS>;
|
||||
clock-names = "bus";
|
||||
};
|
@ -48,6 +48,8 @@ properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
big-endian: true
|
||||
|
||||
fsl,ext-reset-output:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: |
|
||||
@ -93,6 +95,18 @@ allOf:
|
||||
properties:
|
||||
fsl,suspend-in-wait: false
|
||||
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,ls1012a-wdt
|
||||
- fsl,ls1043a-wdt
|
||||
then:
|
||||
properties:
|
||||
big-endian: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -26,6 +26,8 @@ properties:
|
||||
- qcom,apss-wdt-msm8994
|
||||
- qcom,apss-wdt-qcm2290
|
||||
- qcom,apss-wdt-qcs404
|
||||
- qcom,apss-wdt-qcs615
|
||||
- qcom,apss-wdt-qcs8300
|
||||
- qcom,apss-wdt-sa8255p
|
||||
- qcom,apss-wdt-sa8775p
|
||||
- qcom,apss-wdt-sc7180
|
||||
|
@ -26,6 +26,7 @@ properties:
|
||||
- samsung,exynos7-wdt # for Exynos7
|
||||
- samsung,exynos850-wdt # for Exynos850
|
||||
- samsung,exynosautov9-wdt # for Exynosautov9
|
||||
- samsung,exynosautov920-wdt # for Exynosautov920
|
||||
- items:
|
||||
- enum:
|
||||
- tesla,fsd-wdt
|
||||
@ -77,6 +78,7 @@ allOf:
|
||||
- samsung,exynos7-wdt
|
||||
- samsung,exynos850-wdt
|
||||
- samsung,exynosautov9-wdt
|
||||
- samsung,exynosautov920-wdt
|
||||
then:
|
||||
required:
|
||||
- samsung,syscon-phandle
|
||||
@ -88,6 +90,7 @@ allOf:
|
||||
- google,gs101-wdt
|
||||
- samsung,exynos850-wdt
|
||||
- samsung,exynosautov9-wdt
|
||||
- samsung,exynosautov920-wdt
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
@ -120,16 +120,6 @@ coh901327_wdt:
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
cpu5wdt:
|
||||
port:
|
||||
base address of watchdog card, default is 0x91
|
||||
verbose:
|
||||
be verbose, default is 0 (no)
|
||||
ticks:
|
||||
count down ticks, default is 10000
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
cpwd:
|
||||
wd0_timeout:
|
||||
Default watchdog0 timeout in 1/10secs
|
||||
|
@ -22407,7 +22407,7 @@ F: drivers/char/hw_random/jh7110-trng.c
|
||||
|
||||
STARFIVE WATCHDOG DRIVER
|
||||
M: Xingyu Wu <xingyu.wu@starfivetech.com>
|
||||
M: Samin Guo <samin.guo@starfivetech.com>
|
||||
M: Ziv Xu <ziv.xu@starfivetech.com>
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/watchdog/starfive*
|
||||
F: drivers/watchdog/starfive-wdt.c
|
||||
|
@ -408,6 +408,14 @@ config SL28CPLD_WATCHDOG
|
||||
|
||||
# ARM Architecture
|
||||
|
||||
config AIROHA_WATCHDOG
|
||||
tristate "Airoha EN7581 Watchdog"
|
||||
depends on ARCH_AIROHA || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Watchdog timer embedded into Airoha SoC. This will reboot your
|
||||
system when the timeout is reached.
|
||||
|
||||
config ARM_SP805_WATCHDOG
|
||||
tristate "ARM SP805 Watchdog"
|
||||
depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA
|
||||
@ -549,6 +557,7 @@ config S3C2410_WATCHDOG
|
||||
tristate "S3C6410/S5Pv210/Exynos Watchdog"
|
||||
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
select MFD_SYSCON if ARCH_EXYNOS
|
||||
help
|
||||
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
|
||||
SoCs. This will reboot the system when the timer expires with
|
||||
@ -1543,14 +1552,6 @@ config SBC7240_WDT
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sbc7240_wdt.
|
||||
|
||||
config CPU5_WDT
|
||||
tristate "SMA CPU5 Watchdog"
|
||||
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
|
||||
help
|
||||
TBD.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cpu5wdt.
|
||||
|
||||
config SMSC_SCH311X_WDT
|
||||
tristate "SMSC SCH311X Watchdog Timer"
|
||||
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
|
||||
|
@ -40,6 +40,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
|
||||
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
|
||||
obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
|
||||
obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o
|
||||
obj-$(CONFIG_AIROHA_WATCHDOG) += airoha_wdt.o
|
||||
obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
|
||||
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
|
||||
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
|
||||
@ -138,7 +139,6 @@ obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o
|
||||
obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
|
||||
obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
|
||||
obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
|
||||
obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
|
||||
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
|
||||
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
|
||||
obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o
|
||||
|
216
drivers/watchdog/airoha_wdt.c
Normal file
216
drivers/watchdog/airoha_wdt.c
Normal file
@ -0,0 +1,216 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Airoha Watchdog Driver
|
||||
*
|
||||
* Copyright (c) 2024, AIROHA All rights reserved.
|
||||
*
|
||||
* Mayur Kumar <mayur.kumar@airoha.com>
|
||||
* Christian Marangi <ansuelsmth@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
/* Base address of timer and watchdog registers */
|
||||
#define TIMER_CTRL 0x0
|
||||
#define WDT_ENABLE BIT(25)
|
||||
#define WDT_TIMER_INTERRUPT BIT(21)
|
||||
/* Timer3 is used as Watchdog Timer */
|
||||
#define WDT_TIMER_ENABLE BIT(5)
|
||||
#define WDT_TIMER_LOAD_VALUE 0x2c
|
||||
#define WDT_TIMER_CUR_VALUE 0x30
|
||||
#define WDT_TIMER_VAL GENMASK(31, 0)
|
||||
#define WDT_RELOAD 0x38
|
||||
#define WDT_RLD BIT(0)
|
||||
|
||||
/* Airoha watchdog structure description */
|
||||
struct airoha_wdt_desc {
|
||||
struct watchdog_device wdog_dev;
|
||||
unsigned int wdt_freq;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define WDT_HEARTBEAT 24
|
||||
static int heartbeat = WDT_HEARTBEAT;
|
||||
module_param(heartbeat, int, 0);
|
||||
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default="
|
||||
__MODULE_STRING(WDT_HEARTBEAT) ")");
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
static int airoha_wdt_start(struct watchdog_device *wdog_dev)
|
||||
{
|
||||
struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
|
||||
u32 val;
|
||||
|
||||
val = readl(airoha_wdt->base + TIMER_CTRL);
|
||||
val |= (WDT_TIMER_ENABLE | WDT_ENABLE | WDT_TIMER_INTERRUPT);
|
||||
writel(val, airoha_wdt->base + TIMER_CTRL);
|
||||
val = wdog_dev->timeout * airoha_wdt->wdt_freq;
|
||||
writel(val, airoha_wdt->base + WDT_TIMER_LOAD_VALUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_wdt_stop(struct watchdog_device *wdog_dev)
|
||||
{
|
||||
struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
|
||||
u32 val;
|
||||
|
||||
val = readl(airoha_wdt->base + TIMER_CTRL);
|
||||
val &= (~WDT_ENABLE & ~WDT_TIMER_ENABLE);
|
||||
writel(val, airoha_wdt->base + TIMER_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_wdt_ping(struct watchdog_device *wdog_dev)
|
||||
{
|
||||
struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
|
||||
u32 val;
|
||||
|
||||
val = readl(airoha_wdt->base + WDT_RELOAD);
|
||||
val |= WDT_RLD;
|
||||
writel(val, airoha_wdt->base + WDT_RELOAD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_wdt_set_timeout(struct watchdog_device *wdog_dev, unsigned int timeout)
|
||||
{
|
||||
wdog_dev->timeout = timeout;
|
||||
|
||||
if (watchdog_active(wdog_dev)) {
|
||||
airoha_wdt_stop(wdog_dev);
|
||||
return airoha_wdt_start(wdog_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int airoha_wdt_get_timeleft(struct watchdog_device *wdog_dev)
|
||||
{
|
||||
struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
|
||||
u32 val;
|
||||
|
||||
val = readl(airoha_wdt->base + WDT_TIMER_CUR_VALUE);
|
||||
return DIV_ROUND_UP(val, airoha_wdt->wdt_freq);
|
||||
}
|
||||
|
||||
static const struct watchdog_info airoha_wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||
.identity = "Airoha Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops airoha_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = airoha_wdt_start,
|
||||
.stop = airoha_wdt_stop,
|
||||
.ping = airoha_wdt_ping,
|
||||
.set_timeout = airoha_wdt_set_timeout,
|
||||
.get_timeleft = airoha_wdt_get_timeleft,
|
||||
};
|
||||
|
||||
static int airoha_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct airoha_wdt_desc *airoha_wdt;
|
||||
struct watchdog_device *wdog_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *bus_clk;
|
||||
int ret;
|
||||
|
||||
airoha_wdt = devm_kzalloc(dev, sizeof(*airoha_wdt), GFP_KERNEL);
|
||||
if (!airoha_wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
airoha_wdt->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(airoha_wdt->base))
|
||||
return PTR_ERR(airoha_wdt->base);
|
||||
|
||||
bus_clk = devm_clk_get_enabled(dev, "bus");
|
||||
if (IS_ERR(bus_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(bus_clk),
|
||||
"failed to enable bus clock\n");
|
||||
|
||||
/* Watchdog ticks at half the bus rate */
|
||||
airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2;
|
||||
|
||||
/* Initialize struct watchdog device */
|
||||
wdog_dev = &airoha_wdt->wdog_dev;
|
||||
wdog_dev->timeout = heartbeat;
|
||||
wdog_dev->info = &airoha_wdt_info;
|
||||
wdog_dev->ops = &airoha_wdt_ops;
|
||||
/* Bus 300MHz, watchdog 150MHz, 28 seconds */
|
||||
wdog_dev->max_timeout = FIELD_MAX(WDT_TIMER_VAL) / airoha_wdt->wdt_freq;
|
||||
wdog_dev->parent = dev;
|
||||
|
||||
watchdog_set_drvdata(wdog_dev, airoha_wdt);
|
||||
watchdog_set_nowayout(wdog_dev, nowayout);
|
||||
watchdog_stop_on_unregister(wdog_dev);
|
||||
|
||||
ret = devm_watchdog_register_device(dev, wdog_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, airoha_wdt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_wdt_suspend(struct device *dev)
|
||||
{
|
||||
struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&airoha_wdt->wdog_dev))
|
||||
airoha_wdt_stop(&airoha_wdt->wdog_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_wdt_resume(struct device *dev)
|
||||
{
|
||||
struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&airoha_wdt->wdog_dev)) {
|
||||
airoha_wdt_start(&airoha_wdt->wdog_dev);
|
||||
airoha_wdt_ping(&airoha_wdt->wdog_dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id airoha_wdt_of_match[] = {
|
||||
{ .compatible = "airoha,en7581-wdt", },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, airoha_wdt_of_match);
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(airoha_wdt_pm_ops, airoha_wdt_suspend, airoha_wdt_resume);
|
||||
|
||||
static struct platform_driver airoha_wdt_driver = {
|
||||
.probe = airoha_wdt_probe,
|
||||
.driver = {
|
||||
.name = "airoha-wdt",
|
||||
.pm = pm_sleep_ptr(&airoha_wdt_pm_ops),
|
||||
.of_match_table = airoha_wdt_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(airoha_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Mayur Kumar <mayur.kumar@airoha.com>");
|
||||
MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||
MODULE_DESCRIPTION("Airoha EN7581 Watchdog Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -127,11 +127,11 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode,
|
||||
/*
|
||||
* Flush writes and then wait for the SoC to reset. Even though the
|
||||
* reset is queued almost immediately experiments have shown that it
|
||||
* can take up to ~20-25ms until the SoC is actually reset. Just wait
|
||||
* 50ms here to be safe.
|
||||
* can take up to ~120-125ms until the SoC is actually reset. Just
|
||||
* wait 150ms here to be safe.
|
||||
*/
|
||||
(void)readl_relaxed(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||
mdelay(50);
|
||||
(void)readl(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||
mdelay(150);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -248,7 +248,6 @@ static const struct watchdog_ops armada_37xx_wdt_ops = {
|
||||
static int armada_37xx_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct armada_37xx_watchdog *dev;
|
||||
struct resource *res;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
@ -266,12 +265,9 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(regmap);
|
||||
dev->cpu_misc = regmap;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!dev->reg)
|
||||
return -ENOMEM;
|
||||
dev->reg = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(dev->reg))
|
||||
return PTR_ERR(dev->reg);
|
||||
|
||||
/* init clock */
|
||||
dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
|
@ -1,284 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* sma cpu5 watchdog driver
|
||||
*
|
||||
* Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
/* adjustable parameters */
|
||||
|
||||
static int verbose;
|
||||
static int port = 0x91;
|
||||
static int ticks = 10000;
|
||||
static DEFINE_SPINLOCK(cpu5wdt_lock);
|
||||
|
||||
#define PFX "cpu5wdt: "
|
||||
|
||||
#define CPU5WDT_EXTENT 0x0A
|
||||
|
||||
#define CPU5WDT_STATUS_REG 0x00
|
||||
#define CPU5WDT_TIME_A_REG 0x02
|
||||
#define CPU5WDT_TIME_B_REG 0x03
|
||||
#define CPU5WDT_MODE_REG 0x04
|
||||
#define CPU5WDT_TRIGGER_REG 0x07
|
||||
#define CPU5WDT_ENABLE_REG 0x08
|
||||
#define CPU5WDT_RESET_REG 0x09
|
||||
|
||||
#define CPU5WDT_INTERVAL (HZ/10+1)
|
||||
|
||||
/* some device data */
|
||||
|
||||
static struct {
|
||||
struct completion stop;
|
||||
int running;
|
||||
struct timer_list timer;
|
||||
int queue;
|
||||
int default_ticks;
|
||||
unsigned long inuse;
|
||||
} cpu5wdt_device;
|
||||
|
||||
/* generic helper functions */
|
||||
|
||||
static void cpu5wdt_trigger(struct timer_list *unused)
|
||||
{
|
||||
if (verbose > 2)
|
||||
pr_debug("trigger at %i ticks\n", ticks);
|
||||
|
||||
if (cpu5wdt_device.running)
|
||||
ticks--;
|
||||
|
||||
spin_lock(&cpu5wdt_lock);
|
||||
/* keep watchdog alive */
|
||||
outb(1, port + CPU5WDT_TRIGGER_REG);
|
||||
|
||||
/* requeue?? */
|
||||
if (cpu5wdt_device.queue && ticks)
|
||||
mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
|
||||
else {
|
||||
/* ticks doesn't matter anyway */
|
||||
complete(&cpu5wdt_device.stop);
|
||||
}
|
||||
spin_unlock(&cpu5wdt_lock);
|
||||
|
||||
}
|
||||
|
||||
static void cpu5wdt_reset(void)
|
||||
{
|
||||
ticks = cpu5wdt_device.default_ticks;
|
||||
|
||||
if (verbose)
|
||||
pr_debug("reset (%i ticks)\n", (int) ticks);
|
||||
|
||||
}
|
||||
|
||||
static void cpu5wdt_start(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&cpu5wdt_lock, flags);
|
||||
if (!cpu5wdt_device.queue) {
|
||||
cpu5wdt_device.queue = 1;
|
||||
outb(0, port + CPU5WDT_TIME_A_REG);
|
||||
outb(0, port + CPU5WDT_TIME_B_REG);
|
||||
outb(1, port + CPU5WDT_MODE_REG);
|
||||
outb(0, port + CPU5WDT_RESET_REG);
|
||||
outb(0, port + CPU5WDT_ENABLE_REG);
|
||||
mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
|
||||
}
|
||||
/* if process dies, counter is not decremented */
|
||||
cpu5wdt_device.running++;
|
||||
spin_unlock_irqrestore(&cpu5wdt_lock, flags);
|
||||
}
|
||||
|
||||
static int cpu5wdt_stop(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&cpu5wdt_lock, flags);
|
||||
if (cpu5wdt_device.running)
|
||||
cpu5wdt_device.running = 0;
|
||||
ticks = cpu5wdt_device.default_ticks;
|
||||
spin_unlock_irqrestore(&cpu5wdt_lock, flags);
|
||||
if (verbose)
|
||||
pr_crit("stop not possible\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* filesystem operations */
|
||||
|
||||
static int cpu5wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(0, &cpu5wdt_device.inuse))
|
||||
return -EBUSY;
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
static int cpu5wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
clear_bit(0, &cpu5wdt_device.inuse);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
unsigned int value;
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_CARDRESET,
|
||||
.identity = "CPU5 WDT",
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
if (copy_to_user(argp, &ident, sizeof(ident)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case WDIOC_GETSTATUS:
|
||||
value = inb(port + CPU5WDT_STATUS_REG);
|
||||
value = (value >> 2) & 1;
|
||||
return put_user(value, p);
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(value, p))
|
||||
return -EFAULT;
|
||||
if (value & WDIOS_ENABLECARD)
|
||||
cpu5wdt_start();
|
||||
if (value & WDIOS_DISABLECARD)
|
||||
cpu5wdt_stop();
|
||||
break;
|
||||
case WDIOC_KEEPALIVE:
|
||||
cpu5wdt_reset();
|
||||
break;
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
if (!count)
|
||||
return -EIO;
|
||||
cpu5wdt_reset();
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations cpu5wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = cpu5wdt_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.open = cpu5wdt_open,
|
||||
.write = cpu5wdt_write,
|
||||
.release = cpu5wdt_release,
|
||||
};
|
||||
|
||||
static struct miscdevice cpu5wdt_misc = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &cpu5wdt_fops,
|
||||
};
|
||||
|
||||
/* init/exit function */
|
||||
|
||||
static int cpu5wdt_init(void)
|
||||
{
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
if (verbose)
|
||||
pr_debug("port=0x%x, verbose=%i\n", port, verbose);
|
||||
|
||||
init_completion(&cpu5wdt_device.stop);
|
||||
cpu5wdt_device.queue = 0;
|
||||
timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
|
||||
cpu5wdt_device.default_ticks = ticks;
|
||||
|
||||
if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
|
||||
pr_err("request_region failed\n");
|
||||
err = -EBUSY;
|
||||
goto no_port;
|
||||
}
|
||||
|
||||
/* watchdog reboot? */
|
||||
val = inb(port + CPU5WDT_STATUS_REG);
|
||||
val = (val >> 2) & 1;
|
||||
if (!val)
|
||||
pr_info("sorry, was my fault\n");
|
||||
|
||||
err = misc_register(&cpu5wdt_misc);
|
||||
if (err < 0) {
|
||||
pr_err("misc_register failed\n");
|
||||
goto no_misc;
|
||||
}
|
||||
|
||||
|
||||
pr_info("init success\n");
|
||||
return 0;
|
||||
|
||||
no_misc:
|
||||
release_region(port, CPU5WDT_EXTENT);
|
||||
no_port:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cpu5wdt_init_module(void)
|
||||
{
|
||||
return cpu5wdt_init();
|
||||
}
|
||||
|
||||
static void cpu5wdt_exit(void)
|
||||
{
|
||||
if (cpu5wdt_device.queue) {
|
||||
cpu5wdt_device.queue = 0;
|
||||
wait_for_completion(&cpu5wdt_device.stop);
|
||||
timer_shutdown_sync(&cpu5wdt_device.timer);
|
||||
}
|
||||
|
||||
misc_deregister(&cpu5wdt_misc);
|
||||
|
||||
release_region(port, CPU5WDT_EXTENT);
|
||||
|
||||
}
|
||||
|
||||
static void cpu5wdt_exit_module(void)
|
||||
{
|
||||
cpu5wdt_exit();
|
||||
}
|
||||
|
||||
/* module entry points */
|
||||
|
||||
module_init(cpu5wdt_init_module);
|
||||
module_exit(cpu5wdt_exit_module);
|
||||
|
||||
MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
|
||||
MODULE_DESCRIPTION("sma cpu5 watchdog driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param_hw(port, int, ioport, 0);
|
||||
MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
|
||||
|
||||
module_param(verbose, int, 0);
|
||||
MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
|
||||
|
||||
module_param(ticks, int, 0);
|
||||
MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");
|
@ -146,12 +146,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_watchdog_register_device(dev, &driver_data->wdt);
|
||||
if (ret != 0)
|
||||
dev_err(da9055->dev, "watchdog_register_device() failed: %d\n",
|
||||
ret);
|
||||
|
||||
return ret;
|
||||
return devm_watchdog_register_device(dev, &driver_data->wdt);
|
||||
}
|
||||
|
||||
static struct platform_driver da9055_wdt_driver = {
|
||||
|
@ -27,7 +27,6 @@
|
||||
* others: timeout = 2048 ms * 2^(TWDSCALE-1).
|
||||
*/
|
||||
static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
|
||||
static bool use_sw_pm;
|
||||
|
||||
#define DA9063_TWDSCALE_DISABLE 0
|
||||
#define DA9063_TWDSCALE_MIN 1
|
||||
@ -230,7 +229,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
|
||||
if (!wdd)
|
||||
return -ENOMEM;
|
||||
|
||||
use_sw_pm = device_property_present(dev, "dlg,use-sw-pm");
|
||||
da9063->use_sw_pm = device_property_present(dev, "dlg,use-sw-pm");
|
||||
|
||||
wdd->info = &da9063_watchdog_info;
|
||||
wdd->ops = &da9063_watchdog_ops;
|
||||
@ -264,11 +263,12 @@ static int da9063_wdt_probe(struct platform_device *pdev)
|
||||
return devm_watchdog_register_device(dev, wdd);
|
||||
}
|
||||
|
||||
static int __maybe_unused da9063_wdt_suspend(struct device *dev)
|
||||
static int da9063_wdt_suspend(struct device *dev)
|
||||
{
|
||||
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||
struct da9063 *da9063 = watchdog_get_drvdata(wdd);
|
||||
|
||||
if (!use_sw_pm)
|
||||
if (!da9063->use_sw_pm)
|
||||
return 0;
|
||||
|
||||
if (watchdog_active(wdd))
|
||||
@ -277,11 +277,12 @@ static int __maybe_unused da9063_wdt_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused da9063_wdt_resume(struct device *dev)
|
||||
static int da9063_wdt_resume(struct device *dev)
|
||||
{
|
||||
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||
struct da9063 *da9063 = watchdog_get_drvdata(wdd);
|
||||
|
||||
if (!use_sw_pm)
|
||||
if (!da9063->use_sw_pm)
|
||||
return 0;
|
||||
|
||||
if (watchdog_active(wdd))
|
||||
@ -290,14 +291,14 @@ static int __maybe_unused da9063_wdt_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops,
|
||||
da9063_wdt_suspend, da9063_wdt_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops, da9063_wdt_suspend,
|
||||
da9063_wdt_resume);
|
||||
|
||||
static struct platform_driver da9063_wdt_driver = {
|
||||
.probe = da9063_wdt_probe,
|
||||
.driver = {
|
||||
.name = DA9063_DRVNAME_WATCHDOG,
|
||||
.pm = &da9063_wdt_pm_ops,
|
||||
.pm = pm_sleep_ptr(&da9063_wdt_pm_ops),
|
||||
},
|
||||
};
|
||||
module_platform_driver(da9063_wdt_driver);
|
||||
|
@ -151,10 +151,8 @@ static int gxp_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
watchdog_stop_on_reboot(&drvdata->wdd);
|
||||
err = devm_watchdog_register_device(dev, &drvdata->wdd);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to register watchdog device");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_info(dev, "HPE GXP watchdog timer");
|
||||
|
||||
|
@ -82,6 +82,13 @@
|
||||
#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */
|
||||
#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/
|
||||
|
||||
/*
|
||||
* NMI_NOW is bit 8 of TCO1_CNT register
|
||||
* Read/Write
|
||||
* This bit is implemented as RW but has no effect on HW.
|
||||
*/
|
||||
#define NMI_NOW BIT(8)
|
||||
|
||||
/* internal variables */
|
||||
struct iTCO_wdt_private {
|
||||
struct watchdog_device wddev;
|
||||
@ -219,13 +226,23 @@ static int update_no_reboot_bit_cnt(void *priv, bool set)
|
||||
struct iTCO_wdt_private *p = priv;
|
||||
u16 val, newval;
|
||||
|
||||
val = inw(TCO1_CNT(p));
|
||||
/*
|
||||
* writing back 1b1 to NMI_NOW of TCO1_CNT register
|
||||
* causes NMI_NOW bit inversion what consequently does
|
||||
* not allow to perform the register's value comparison
|
||||
* properly.
|
||||
*
|
||||
* NMI_NOW bit masking for TCO1_CNT register values
|
||||
* helps to avoid possible NMI_NOW bit inversions on
|
||||
* following write operation.
|
||||
*/
|
||||
val = inw(TCO1_CNT(p)) & ~NMI_NOW;
|
||||
if (set)
|
||||
val |= BIT(0);
|
||||
else
|
||||
val &= ~BIT(0);
|
||||
outw(val, TCO1_CNT(p));
|
||||
newval = inw(TCO1_CNT(p));
|
||||
newval = inw(TCO1_CNT(p)) & ~NMI_NOW;
|
||||
|
||||
/* make sure the update is successful */
|
||||
return val != newval ? -EIO : 0;
|
||||
@ -592,10 +609,8 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_stop_on_reboot(&p->wddev);
|
||||
watchdog_stop_on_unregister(&p->wddev);
|
||||
ret = devm_watchdog_register_device(dev, &p->wddev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "cannot register watchdog device (err=%d)\n", ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n",
|
||||
heartbeat, nowayout);
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -40,6 +42,7 @@
|
||||
#define VAL 0x2f
|
||||
|
||||
/* Logical device Numbers LDN */
|
||||
#define EC 0x04
|
||||
#define GPIO 0x07
|
||||
|
||||
/* Configuration Registers and Functions */
|
||||
@ -73,6 +76,12 @@
|
||||
#define IT8784_ID 0x8784
|
||||
#define IT8786_ID 0x8786
|
||||
|
||||
/* Environment Controller Configuration Registers LDN=0x04 */
|
||||
#define SCR1 0xfa
|
||||
|
||||
/* Environment Controller Bits SCR1 */
|
||||
#define WDT_PWRGD 0x20
|
||||
|
||||
/* GPIO Configuration Registers LDN=0x07 */
|
||||
#define WDTCTRL 0x71
|
||||
#define WDTCFG 0x72
|
||||
@ -240,6 +249,21 @@ static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum {
|
||||
IT87_WDT_OUTPUT_THROUGH_PWRGD = BIT(0),
|
||||
};
|
||||
|
||||
static const struct dmi_system_id it87_quirks[] = {
|
||||
{
|
||||
/* Qotom Q30900P (IT8786) */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "QCML04"),
|
||||
},
|
||||
.driver_data = (void *)IT87_WDT_OUTPUT_THROUGH_PWRGD,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||
.firmware_version = 1,
|
||||
@ -261,8 +285,10 @@ static struct watchdog_device wdt_dev = {
|
||||
|
||||
static int __init it87_wdt_init(void)
|
||||
{
|
||||
const struct dmi_system_id *dmi_id;
|
||||
u8 chip_rev;
|
||||
u8 ctrl;
|
||||
int quirks = 0;
|
||||
int rc;
|
||||
|
||||
rc = superio_enter();
|
||||
@ -273,6 +299,10 @@ static int __init it87_wdt_init(void)
|
||||
chip_rev = superio_inb(CHIPREV) & 0x0f;
|
||||
superio_exit();
|
||||
|
||||
dmi_id = dmi_first_match(it87_quirks);
|
||||
if (dmi_id)
|
||||
quirks = (long)dmi_id->driver_data;
|
||||
|
||||
switch (chip_type) {
|
||||
case IT8702_ID:
|
||||
max_units = 255;
|
||||
@ -333,6 +363,15 @@ static int __init it87_wdt_init(void)
|
||||
superio_outb(0x00, WDTCTRL);
|
||||
}
|
||||
|
||||
if (quirks & IT87_WDT_OUTPUT_THROUGH_PWRGD) {
|
||||
superio_select(EC);
|
||||
ctrl = superio_inb(SCR1);
|
||||
if (!(ctrl & WDT_PWRGD)) {
|
||||
ctrl |= WDT_PWRGD;
|
||||
superio_outb(ctrl, SCR1);
|
||||
}
|
||||
}
|
||||
|
||||
superio_exit();
|
||||
|
||||
if (timeout < 1 || timeout > max_units * 60) {
|
||||
@ -349,10 +388,8 @@ static int __init it87_wdt_init(void)
|
||||
|
||||
watchdog_stop_on_reboot(&wdt_dev);
|
||||
rc = watchdog_register_device(&wdt_dev);
|
||||
if (rc) {
|
||||
pr_err("Cannot register watchdog device (err=%d)\n", rc);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
|
||||
chip_type, chip_rev, timeout, nowayout, testmode);
|
||||
|
@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <dt-bindings/reset/mt2712-resets.h>
|
||||
#include <dt-bindings/reset/mediatek,mt6735-wdt.h>
|
||||
#include <dt-bindings/reset/mediatek,mt6795-resets.h>
|
||||
#include <dt-bindings/reset/mt7986-resets.h>
|
||||
#include <dt-bindings/reset/mt8183-resets.h>
|
||||
@ -87,6 +88,10 @@ static const struct mtk_wdt_data mt2712_data = {
|
||||
.toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM,
|
||||
};
|
||||
|
||||
static const struct mtk_wdt_data mt6735_data = {
|
||||
.toprgu_sw_rst_num = MT6735_TOPRGU_RST_NUM,
|
||||
};
|
||||
|
||||
static const struct mtk_wdt_data mt6795_data = {
|
||||
.toprgu_sw_rst_num = MT6795_TOPRGU_SW_RST_NUM,
|
||||
};
|
||||
@ -225,9 +230,15 @@ static int mtk_wdt_restart(struct watchdog_device *wdt_dev,
|
||||
{
|
||||
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
void __iomem *wdt_base;
|
||||
u32 reg;
|
||||
|
||||
wdt_base = mtk_wdt->wdt_base;
|
||||
|
||||
/* Enable reset in order to issue a system reset instead of an IRQ */
|
||||
reg = readl(wdt_base + WDT_MODE);
|
||||
reg &= ~WDT_MODE_IRQ_EN;
|
||||
writel(reg | WDT_MODE_KEY, wdt_base + WDT_MODE);
|
||||
|
||||
while (1) {
|
||||
writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);
|
||||
mdelay(5);
|
||||
@ -483,6 +494,7 @@ static int mtk_wdt_resume(struct device *dev)
|
||||
static const struct of_device_id mtk_wdt_dt_ids[] = {
|
||||
{ .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data },
|
||||
{ .compatible = "mediatek,mt6589-wdt" },
|
||||
{ .compatible = "mediatek,mt6735-wdt", .data = &mt6735_data },
|
||||
{ .compatible = "mediatek,mt6795-wdt", .data = &mt6795_data },
|
||||
{ .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data },
|
||||
{ .compatible = "mediatek,mt7988-wdt", .data = &mt7988_data },
|
||||
|
@ -559,10 +559,8 @@ static int __init octeon_wdt_init(void)
|
||||
watchdog_set_nowayout(&octeon_wdt, nowayout);
|
||||
|
||||
ret = watchdog_register_device(&octeon_wdt);
|
||||
if (ret) {
|
||||
pr_err("watchdog_register_device() failed: %d\n", ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (disable) {
|
||||
pr_notice("disabled\n");
|
||||
|
@ -833,7 +833,7 @@ static int pcwd_isa_match(struct device *dev, unsigned int id)
|
||||
port0 = inb_p(base_addr);
|
||||
port1 = inb_p(base_addr + 1);
|
||||
|
||||
/* Has either hearbeat bit changed? */
|
||||
/* Has either heartbeat bit changed? */
|
||||
if ((port0 ^ last_port0) & WD_HRTBT ||
|
||||
(port1 ^ last_port1) & WD_REVC_HRBT) {
|
||||
retval = 1;
|
||||
|
@ -61,7 +61,7 @@
|
||||
|
||||
#define MAX_HW_ERROR 250
|
||||
|
||||
static int heartbeat = DEFAULT_HEARTBEAT;
|
||||
static int heartbeat;
|
||||
|
||||
/*
|
||||
* struct to hold data for each WDT device
|
||||
@ -252,6 +252,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
wdd->min_timeout = 1;
|
||||
wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /
|
||||
wdt->freq * 1000;
|
||||
wdd->timeout = DEFAULT_HEARTBEAT;
|
||||
wdd->parent = dev;
|
||||
|
||||
watchdog_set_drvdata(wdd, wdt);
|
||||
@ -336,10 +337,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_init_timeout(wdd, heartbeat, dev);
|
||||
|
||||
ret = watchdog_register_device(wdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot register watchdog device\n");
|
||||
if (ret)
|
||||
goto err_iomap;
|
||||
}
|
||||
|
||||
if (last_ping)
|
||||
watchdog_set_last_hw_keepalive(wdd, last_ping);
|
||||
|
@ -169,7 +169,6 @@ static int rza_wdt_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rza_wdt *priv;
|
||||
unsigned long rate;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@ -218,11 +217,7 @@ static int rza_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_init_timeout(&priv->wdev, 0, dev);
|
||||
watchdog_set_drvdata(&priv->wdev, priv);
|
||||
|
||||
ret = devm_watchdog_register_device(dev, &priv->wdev);
|
||||
if (ret)
|
||||
dev_err(dev, "Cannot register watchdog device\n");
|
||||
|
||||
return ret;
|
||||
return devm_watchdog_register_device(dev, &priv->wdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id rza_wdt_of_match[] = {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/units.h>
|
||||
@ -166,8 +167,22 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev,
|
||||
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(priv->pclk);
|
||||
clk_prepare_enable(priv->osc_clk);
|
||||
/*
|
||||
* In case of RZ/G3S the watchdog device may be part of an IRQ safe power
|
||||
* domain that is currently powered off. In this case we need to power
|
||||
* it on before accessing registers. Along with this the clocks will be
|
||||
* enabled. We don't undo the pm_runtime_resume_and_get() as the device
|
||||
* need to be on for the reboot to happen.
|
||||
*
|
||||
* For the rest of SoCs not registering a watchdog IRQ safe power
|
||||
* domain it is safe to call pm_runtime_resume_and_get() as the
|
||||
* irq_safe_dev_in_sleep_domain() call in genpd_runtime_resume()
|
||||
* returns non zero value and the genpd_lock() is avoided, thus, there
|
||||
* will be no invalid wait context reported by lockdep.
|
||||
*/
|
||||
ret = pm_runtime_resume_and_get(wdev->parent);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->devtype == WDT_RZG2L) {
|
||||
ret = reset_control_deassert(priv->rstc);
|
||||
@ -275,6 +290,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
priv->devtype = (uintptr_t)of_device_get_match_data(dev);
|
||||
|
||||
pm_runtime_irq_safe(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
priv->wdev.info = &rzg2l_wdt_ident;
|
||||
|
@ -52,7 +52,7 @@ static int rzn1_wdt_ping(struct watchdog_device *w)
|
||||
{
|
||||
struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
|
||||
|
||||
/* Any value retrigggers the watchdog */
|
||||
/* Any value retriggers the watchdog */
|
||||
writel(0, wdt->base + RZN1_WDT_RETRIGGER);
|
||||
|
||||
return 0;
|
||||
|
@ -24,9 +24,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/soc/samsung/exynos-pmu.h>
|
||||
|
||||
#define S3C2410_WTCON 0x00
|
||||
#define S3C2410_WTDAT 0x04
|
||||
@ -63,6 +63,10 @@
|
||||
#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
|
||||
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520
|
||||
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN 0x1544
|
||||
#define EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT 0x1420
|
||||
#define EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN 0x1444
|
||||
#define EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT 0x1720
|
||||
#define EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN 0x1744
|
||||
|
||||
#define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
|
||||
#define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
|
||||
@ -303,6 +307,32 @@ static const struct s3c2410_wdt_variant drv_data_gs101_cl1 = {
|
||||
QUIRK_HAS_DBGACK_BIT,
|
||||
};
|
||||
|
||||
static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = {
|
||||
.mask_reset_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN,
|
||||
.mask_bit = 2,
|
||||
.mask_reset_inv = true,
|
||||
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||
.rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT,
|
||||
.cnt_en_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT,
|
||||
.cnt_en_bit = 7,
|
||||
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
|
||||
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
|
||||
QUIRK_HAS_DBGACK_BIT,
|
||||
};
|
||||
|
||||
static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = {
|
||||
.mask_reset_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN,
|
||||
.mask_bit = 2,
|
||||
.mask_reset_inv = true,
|
||||
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||
.rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT,
|
||||
.cnt_en_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT,
|
||||
.cnt_en_bit = 7,
|
||||
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
|
||||
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
|
||||
QUIRK_HAS_DBGACK_BIT,
|
||||
};
|
||||
|
||||
static const struct of_device_id s3c2410_wdt_match[] = {
|
||||
{ .compatible = "google,gs101-wdt",
|
||||
.data = &drv_data_gs101_cl0 },
|
||||
@ -320,6 +350,8 @@ static const struct of_device_id s3c2410_wdt_match[] = {
|
||||
.data = &drv_data_exynos850_cl0 },
|
||||
{ .compatible = "samsung,exynosautov9-wdt",
|
||||
.data = &drv_data_exynosautov9_cl0 },
|
||||
{ .compatible = "samsung,exynosautov920-wdt",
|
||||
.data = &drv_data_exynosautov920_cl0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
|
||||
@ -643,7 +675,8 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
|
||||
/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
|
||||
if (variant == &drv_data_exynos850_cl0 ||
|
||||
variant == &drv_data_exynosautov9_cl0 ||
|
||||
variant == &drv_data_gs101_cl0) {
|
||||
variant == &drv_data_gs101_cl0 ||
|
||||
variant == &drv_data_exynosautov920_cl0) {
|
||||
u32 index;
|
||||
int err;
|
||||
|
||||
@ -662,6 +695,8 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
|
||||
variant = &drv_data_exynosautov9_cl1;
|
||||
else if (variant == &drv_data_gs101_cl0)
|
||||
variant = &drv_data_gs101_cl1;
|
||||
else if (variant == &drv_data_exynosautov920_cl0)
|
||||
variant = &drv_data_exynosautov920_cl1;
|
||||
break;
|
||||
default:
|
||||
return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index);
|
||||
@ -699,11 +734,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
|
||||
wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
|
||||
"samsung,syscon-phandle");
|
||||
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"samsung,syscon-phandle");
|
||||
if (IS_ERR(wdt->pmureg))
|
||||
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
|
||||
"PMU regmap lookup failed.\n");
|
||||
"syscon regmap lookup failed.\n");
|
||||
}
|
||||
|
||||
wdt_irq = platform_get_irq(pdev, 0);
|
||||
|
@ -236,8 +236,8 @@ static void sa1100dog_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver sa1100dog_driver = {
|
||||
.driver.name = "sa1100_wdt",
|
||||
.probe = sa1100dog_probe,
|
||||
.remove = sa1100dog_remove,
|
||||
.probe = sa1100dog_probe,
|
||||
.remove = sa1100dog_remove,
|
||||
};
|
||||
module_platform_driver(sa1100dog_driver);
|
||||
|
||||
|
@ -198,10 +198,8 @@ static int sl28cpld_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = devm_watchdog_register_device(&pdev->dev, wdd);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register watchdog device\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "initial timeout %d sec%s\n",
|
||||
wdd->timeout, nowayout ? ", nowayout" : "");
|
||||
|
@ -485,7 +485,7 @@ static long wb_smsc_wdt_ioctl(struct file *file,
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Notifier funtions -----------------------------------------*/
|
||||
/* -- Notifier functions -----------------------------------------*/
|
||||
|
||||
static int wb_smsc_wdt_notify_sys(struct notifier_block *this,
|
||||
unsigned long code, void *unused)
|
||||
|
@ -80,7 +80,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
struct starfive_wdt_variant {
|
||||
unsigned int control; /* Watchdog Control Resgister for reset enable */
|
||||
unsigned int control; /* Watchdog Control Register for reset enable */
|
||||
unsigned int load; /* Watchdog Load register */
|
||||
unsigned int reload; /* Watchdog Reload Control register */
|
||||
unsigned int enable; /* Watchdog Enable Register */
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define DEFAULT_TIMEOUT 10
|
||||
@ -28,6 +29,7 @@
|
||||
#define IWDG_RLR 0x08 /* ReLoad Register */
|
||||
#define IWDG_SR 0x0C /* Status Register */
|
||||
#define IWDG_WINR 0x10 /* Windows Register */
|
||||
#define IWDG_EWCR 0x14 /* Early Wake-up Register */
|
||||
|
||||
/* IWDG_KR register bit mask */
|
||||
#define KR_KEY_RELOAD 0xAAAA /* reload counter enable */
|
||||
@ -47,22 +49,29 @@
|
||||
#define SR_PVU BIT(0) /* Watchdog prescaler value update */
|
||||
#define SR_RVU BIT(1) /* Watchdog counter reload value update */
|
||||
|
||||
#define EWCR_EWIT GENMASK(11, 0) /* Watchdog counter window value */
|
||||
#define EWCR_EWIC BIT(14) /* Watchdog early interrupt acknowledge */
|
||||
#define EWCR_EWIE BIT(15) /* Watchdog early interrupt enable */
|
||||
|
||||
/* set timeout to 100000 us */
|
||||
#define TIMEOUT_US 100000
|
||||
#define SLEEP_US 1000
|
||||
|
||||
struct stm32_iwdg_data {
|
||||
bool has_pclk;
|
||||
bool has_early_wakeup;
|
||||
u32 max_prescaler;
|
||||
};
|
||||
|
||||
static const struct stm32_iwdg_data stm32_iwdg_data = {
|
||||
.has_pclk = false,
|
||||
.has_early_wakeup = false,
|
||||
.max_prescaler = 256,
|
||||
};
|
||||
|
||||
static const struct stm32_iwdg_data stm32mp1_iwdg_data = {
|
||||
.has_pclk = true,
|
||||
.has_early_wakeup = true,
|
||||
.max_prescaler = 1024,
|
||||
};
|
||||
|
||||
@ -88,13 +97,18 @@ static inline void reg_write(void __iomem *base, u32 reg, u32 val)
|
||||
static int stm32_iwdg_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
|
||||
u32 tout, presc, iwdg_rlr, iwdg_pr, iwdg_sr;
|
||||
u32 tout, ptot, presc, iwdg_rlr, iwdg_ewcr, iwdg_pr, iwdg_sr;
|
||||
int ret;
|
||||
|
||||
dev_dbg(wdd->parent, "%s\n", __func__);
|
||||
|
||||
if (!wdd->pretimeout)
|
||||
wdd->pretimeout = 3 * wdd->timeout / 4;
|
||||
|
||||
tout = clamp_t(unsigned int, wdd->timeout,
|
||||
wdd->min_timeout, wdd->max_hw_heartbeat_ms / 1000);
|
||||
ptot = clamp_t(unsigned int, tout - wdd->pretimeout,
|
||||
wdd->min_timeout, tout);
|
||||
|
||||
presc = DIV_ROUND_UP(tout * wdt->rate, RLR_MAX + 1);
|
||||
|
||||
@ -102,6 +116,7 @@ static int stm32_iwdg_start(struct watchdog_device *wdd)
|
||||
presc = roundup_pow_of_two(presc);
|
||||
iwdg_pr = presc <= 1 << PR_SHIFT ? 0 : ilog2(presc) - PR_SHIFT;
|
||||
iwdg_rlr = ((tout * wdt->rate) / presc) - 1;
|
||||
iwdg_ewcr = ((ptot * wdt->rate) / presc) - 1;
|
||||
|
||||
/* enable write access */
|
||||
reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA);
|
||||
@ -109,6 +124,8 @@ static int stm32_iwdg_start(struct watchdog_device *wdd)
|
||||
/* set prescaler & reload registers */
|
||||
reg_write(wdt->regs, IWDG_PR, iwdg_pr);
|
||||
reg_write(wdt->regs, IWDG_RLR, iwdg_rlr);
|
||||
if (wdt->data->has_early_wakeup)
|
||||
reg_write(wdt->regs, IWDG_EWCR, iwdg_ewcr | EWCR_EWIE);
|
||||
reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE);
|
||||
|
||||
/* wait for the registers to be updated (max 100ms) */
|
||||
@ -151,6 +168,34 @@ static int stm32_iwdg_set_timeout(struct watchdog_device *wdd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_iwdg_set_pretimeout(struct watchdog_device *wdd,
|
||||
unsigned int pretimeout)
|
||||
{
|
||||
dev_dbg(wdd->parent, "%s pretimeout: %d sec\n", __func__, pretimeout);
|
||||
|
||||
wdd->pretimeout = pretimeout;
|
||||
|
||||
if (watchdog_active(wdd))
|
||||
return stm32_iwdg_start(wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t stm32_iwdg_isr(int irq, void *wdog_arg)
|
||||
{
|
||||
struct watchdog_device *wdd = wdog_arg;
|
||||
struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
|
||||
u32 reg;
|
||||
|
||||
reg = reg_read(wdt->regs, IWDG_EWCR);
|
||||
reg |= EWCR_EWIC;
|
||||
reg_write(wdt->regs, IWDG_EWCR, reg);
|
||||
|
||||
watchdog_notify_pretimeout(wdd);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void stm32_clk_disable_unprepare(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
@ -207,11 +252,20 @@ static const struct watchdog_info stm32_iwdg_info = {
|
||||
.identity = "STM32 Independent Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_info stm32_iwdg_preinfo = {
|
||||
.options = WDIOF_SETTIMEOUT |
|
||||
WDIOF_MAGICCLOSE |
|
||||
WDIOF_KEEPALIVEPING |
|
||||
WDIOF_PRETIMEOUT,
|
||||
.identity = "STM32 Independent Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops stm32_iwdg_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = stm32_iwdg_start,
|
||||
.ping = stm32_iwdg_ping,
|
||||
.set_timeout = stm32_iwdg_set_timeout,
|
||||
.set_pretimeout = stm32_iwdg_set_pretimeout,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32_iwdg_of_match[] = {
|
||||
@ -221,6 +275,40 @@ static const struct of_device_id stm32_iwdg_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
|
||||
|
||||
static int stm32_iwdg_irq_init(struct platform_device *pdev,
|
||||
struct stm32_iwdg *wdt)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct watchdog_device *wdd = &wdt->wdd;
|
||||
struct device *dev = &pdev->dev;
|
||||
int irq, ret;
|
||||
|
||||
if (!wdt->data->has_early_wakeup)
|
||||
return 0;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0)
|
||||
return 0;
|
||||
|
||||
if (of_property_read_bool(np, "wakeup-source")) {
|
||||
ret = device_init_wakeup(dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dev_pm_set_wake_irq(dev, irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, irq, stm32_iwdg_isr, 0,
|
||||
dev_name(dev), wdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wdd->info = &stm32_iwdg_preinfo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_iwdg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -255,6 +343,11 @@ static int stm32_iwdg_probe(struct platform_device *pdev)
|
||||
wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * wdt->data->max_prescaler *
|
||||
1000) / wdt->rate;
|
||||
|
||||
/* Initialize IRQ, this might override wdd->info, hence it is here. */
|
||||
ret = stm32_iwdg_irq_init(pdev, wdt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
watchdog_set_drvdata(wdd, wdt);
|
||||
watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
|
||||
watchdog_init_timeout(wdd, 0, dev);
|
||||
|
@ -237,7 +237,7 @@ void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(watchdog_set_restart_priority);
|
||||
|
||||
static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||
static int ___watchdog_register_device(struct watchdog_device *wdd)
|
||||
{
|
||||
int ret, id = -1;
|
||||
|
||||
@ -337,6 +337,22 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||
{
|
||||
const char *dev_str;
|
||||
int ret;
|
||||
|
||||
ret = ___watchdog_register_device(wdd);
|
||||
if (ret) {
|
||||
dev_str = wdd->parent ? dev_name(wdd->parent) :
|
||||
(const char *)wdd->info->identity;
|
||||
pr_err("%s: failed to register watchdog device (err = %d)\n",
|
||||
dev_str, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* watchdog_register_device() - register a watchdog device
|
||||
* @wdd: watchdog device
|
||||
@ -350,7 +366,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||
|
||||
int watchdog_register_device(struct watchdog_device *wdd)
|
||||
{
|
||||
const char *dev_str;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&wtd_deferred_reg_mutex);
|
||||
@ -360,13 +375,6 @@ int watchdog_register_device(struct watchdog_device *wdd)
|
||||
watchdog_deferred_registration_add(wdd);
|
||||
mutex_unlock(&wtd_deferred_reg_mutex);
|
||||
|
||||
if (ret) {
|
||||
dev_str = wdd->parent ? dev_name(wdd->parent) :
|
||||
(const char *)wdd->info->identity;
|
||||
pr_err("%s: failed to register watchdog device (err = %d)\n",
|
||||
dev_str, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(watchdog_register_device);
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Window watchdog device driver for Xilinx Versal WWDT
|
||||
*
|
||||
* Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
@ -36,6 +36,12 @@
|
||||
|
||||
#define XWWDT_CLOSE_WINDOW_PERCENT 50
|
||||
|
||||
/* Maximum count value of each 32 bit window */
|
||||
#define XWWDT_MAX_COUNT_WINDOW GENMASK(31, 0)
|
||||
|
||||
/* Maximum count value of closed and open window combined */
|
||||
#define XWWDT_MAX_COUNT_WINDOW_COMBINED GENMASK_ULL(32, 1)
|
||||
|
||||
static int wwdt_timeout;
|
||||
static int closed_window_percent;
|
||||
|
||||
@ -54,6 +60,8 @@ MODULE_PARM_DESC(closed_window_percent,
|
||||
* @xilinx_wwdt_wdd: watchdog device structure
|
||||
* @freq: source clock frequency of WWDT
|
||||
* @close_percent: Closed window percent
|
||||
* @closed_timeout: Closed window timeout in ticks
|
||||
* @open_timeout: Open window timeout in ticks
|
||||
*/
|
||||
struct xwwdt_device {
|
||||
void __iomem *base;
|
||||
@ -61,27 +69,22 @@ struct xwwdt_device {
|
||||
struct watchdog_device xilinx_wwdt_wdd;
|
||||
unsigned long freq;
|
||||
u32 close_percent;
|
||||
u64 closed_timeout;
|
||||
u64 open_timeout;
|
||||
};
|
||||
|
||||
static int xilinx_wwdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
|
||||
struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
|
||||
u64 time_out, closed_timeout, open_timeout;
|
||||
u32 control_status_reg;
|
||||
|
||||
/* Calculate timeout count */
|
||||
time_out = xdev->freq * wdd->timeout;
|
||||
closed_timeout = div_u64(time_out * xdev->close_percent, 100);
|
||||
open_timeout = time_out - closed_timeout;
|
||||
wdd->min_hw_heartbeat_ms = xdev->close_percent * 10 * wdd->timeout;
|
||||
|
||||
spin_lock(&xdev->spinlock);
|
||||
|
||||
iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
|
||||
iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET);
|
||||
iowrite32((u32)closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
|
||||
iowrite32((u32)open_timeout, xdev->base + XWWDT_SWR_OFFSET);
|
||||
iowrite32((u32)xdev->closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
|
||||
iowrite32((u32)xdev->open_timeout, xdev->base + XWWDT_SWR_OFFSET);
|
||||
|
||||
/* Enable the window watchdog timer */
|
||||
control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
|
||||
@ -133,7 +136,12 @@ static int xwwdt_probe(struct platform_device *pdev)
|
||||
struct watchdog_device *xilinx_wwdt_wdd;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct xwwdt_device *xdev;
|
||||
u64 max_per_window_ms;
|
||||
u64 min_per_window_ms;
|
||||
u64 timeout_count;
|
||||
struct clk *clk;
|
||||
u32 timeout_ms;
|
||||
u64 ms_count;
|
||||
int ret;
|
||||
|
||||
xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
|
||||
@ -154,12 +162,13 @@ static int xwwdt_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(clk);
|
||||
|
||||
xdev->freq = clk_get_rate(clk);
|
||||
if (!xdev->freq)
|
||||
if (xdev->freq < 1000000)
|
||||
return -EINVAL;
|
||||
|
||||
xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT;
|
||||
xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT;
|
||||
xilinx_wwdt_wdd->max_hw_heartbeat_ms = 1000 * xilinx_wwdt_wdd->timeout;
|
||||
xilinx_wwdt_wdd->max_hw_heartbeat_ms =
|
||||
div64_u64(XWWDT_MAX_COUNT_WINDOW_COMBINED, xdev->freq) * 1000;
|
||||
|
||||
if (closed_window_percent == 0 || closed_window_percent >= 100)
|
||||
xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT;
|
||||
@ -167,6 +176,48 @@ static int xwwdt_probe(struct platform_device *pdev)
|
||||
xdev->close_percent = closed_window_percent;
|
||||
|
||||
watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev);
|
||||
|
||||
/* Calculate ticks for 1 milli-second */
|
||||
ms_count = div_u64(xdev->freq, 1000);
|
||||
timeout_ms = xilinx_wwdt_wdd->timeout * 1000;
|
||||
timeout_count = timeout_ms * ms_count;
|
||||
|
||||
if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms) {
|
||||
/*
|
||||
* To avoid ping restrictions until the minimum hardware heartbeat,
|
||||
* we will solely rely on the open window and
|
||||
* adjust the minimum hardware heartbeat to 0.
|
||||
*/
|
||||
xdev->closed_timeout = 0;
|
||||
xdev->open_timeout = XWWDT_MAX_COUNT_WINDOW;
|
||||
xilinx_wwdt_wdd->min_hw_heartbeat_ms = 0;
|
||||
xilinx_wwdt_wdd->max_hw_heartbeat_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
|
||||
} else {
|
||||
xdev->closed_timeout = div64_u64(timeout_count * xdev->close_percent, 100);
|
||||
xilinx_wwdt_wdd->min_hw_heartbeat_ms =
|
||||
div64_u64(timeout_ms * xdev->close_percent, 100);
|
||||
|
||||
if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2) {
|
||||
max_per_window_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
|
||||
min_per_window_ms = timeout_ms - max_per_window_ms;
|
||||
|
||||
if (xilinx_wwdt_wdd->min_hw_heartbeat_ms > max_per_window_ms) {
|
||||
dev_info(xilinx_wwdt_wdd->parent,
|
||||
"Closed window cannot be set to %d%%. Using maximum supported value.\n",
|
||||
xdev->close_percent);
|
||||
xdev->closed_timeout = max_per_window_ms * ms_count;
|
||||
xilinx_wwdt_wdd->min_hw_heartbeat_ms = max_per_window_ms;
|
||||
} else if (xilinx_wwdt_wdd->min_hw_heartbeat_ms < min_per_window_ms) {
|
||||
dev_info(xilinx_wwdt_wdd->parent,
|
||||
"Closed window cannot be set to %d%%. Using minimum supported value.\n",
|
||||
xdev->close_percent);
|
||||
xdev->closed_timeout = min_per_window_ms * ms_count;
|
||||
xilinx_wwdt_wdd->min_hw_heartbeat_ms = min_per_window_ms;
|
||||
}
|
||||
}
|
||||
xdev->open_timeout = timeout_count - xdev->closed_timeout;
|
||||
}
|
||||
|
||||
spin_lock_init(&xdev->spinlock);
|
||||
watchdog_set_drvdata(xilinx_wwdt_wdd, xdev);
|
||||
watchdog_set_nowayout(xilinx_wwdt_wdd, 1);
|
||||
|
@ -715,7 +715,7 @@ static void ziirave_wdt_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ziirave_wdt_id[] = {
|
||||
{ "rave-wdt", 0 },
|
||||
{ "rave-wdt" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);
|
||||
|
@ -78,6 +78,7 @@ struct da9063 {
|
||||
enum da9063_type type;
|
||||
unsigned char variant_code;
|
||||
unsigned int flags;
|
||||
bool use_sw_pm;
|
||||
|
||||
/* Control interface */
|
||||
struct regmap *regmap;
|
||||
|
Loading…
Reference in New Issue
Block a user