mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
linux-watchdog 5.16-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAmGKax0ACgkQ+iyteGJfRsomvgCgpAmE/d5d9mSpq0zGaLa9ocxt bfQAniIp5n+wklRuiqYRAyCBdNqbKhAI =DlM4 -----END PGP SIGNATURE----- Merge tag 'linux-watchdog-5.16-rc1' of git://www.linux-watchdog.org/linux-watchdog Pull watchdog updates from Wim Van Sebroeck: - f71808e_wdt: convert to watchdog framework - db8500_wdt: Rename driver (was ux500_wdt.c) - sunxi: Add compatibles for R329 and D1 - mtk: add disable_wdt_extrst support - several other small fixes and improvements * tag 'linux-watchdog-5.16-rc1' of git://www.linux-watchdog.org/linux-watchdog: (30 commits) watchdog: db8500_wdt: Rename symbols watchdog: db8500_wdt: Rename driver watchdog: ux500_wdt: Drop platform data watchdog: bcm63xx_wdt: fix fallthrough warning watchdog: iTCO_wdt: No need to stop the timer in probe watchdog: s3c2410: describe driver in KConfig watchdog: sp5100_tco: Add support for get_timeleft watchdog: mtk: add disable_wdt_extrst support dt-bindings: watchdog: mtk-wdt: add disable_wdt_extrst support watchdog: rza_wdt: Use semicolons instead of commas watchdog: mlx-wdt: Use regmap_write_bits() watchdog: rti-wdt: Make use of the helper function devm_platform_ioremap_resource() watchdog: iTCO_wdt: Make use of the helper function devm_platform_ioremap_resource() watchdog: ar7_wdt: Make use of the helper function devm_platform_ioremap_resource_byname() watchdog: sunxi_wdt: Add support for D1 dt-bindings: watchdog: sunxi: Add compatibles for D1 ar7: fix kernel builds for compiler test dt-bindings: watchdog: sunxi: Add compatibles for R329 watchdog: meson_gxbb_wdt: add timeout parameter watchdog: meson_gxbb_wdt: add nowayout parameter ...
This commit is contained in:
commit
89d714ab60
@ -24,16 +24,33 @@ properties:
|
||||
- allwinner,sun50i-a100-wdt
|
||||
- allwinner,sun50i-h6-wdt
|
||||
- allwinner,sun50i-h616-wdt
|
||||
- allwinner,sun50i-r329-wdt
|
||||
- allwinner,sun50i-r329-wdt-reset
|
||||
- const: allwinner,sun6i-a31-wdt
|
||||
- items:
|
||||
- const: allwinner,suniv-f1c100s-wdt
|
||||
- const: allwinner,sun4i-a10-wdt
|
||||
- const: allwinner,sun20i-d1-wdt
|
||||
- items:
|
||||
- const: allwinner,sun20i-d1-wdt-reset
|
||||
- const: allwinner,sun20i-d1-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- description: High-frequency oscillator input, divided internally
|
||||
- description: Low-frequency oscillator input, only found on some variants
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- const: hosc
|
||||
- const: losc
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
@ -44,6 +61,35 @@ required:
|
||||
- clocks
|
||||
- interrupts
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- allwinner,sun20i-d1-wdt
|
||||
- allwinner,sun20i-d1-wdt-reset
|
||||
- allwinner,sun50i-r329-wdt
|
||||
- allwinner,sun50i-r329-wdt-reset
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
|
||||
required:
|
||||
- clock-names
|
||||
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -22,6 +22,7 @@ Required properties:
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
|
||||
Optional properties:
|
||||
- mediatek,disable-extrst: disable send output reset signal
|
||||
- interrupts: Watchdog pre-timeout (bark) interrupt.
|
||||
- timeout-sec: contains the watchdog timeout in seconds.
|
||||
- #reset-cells: Should be 1.
|
||||
@ -31,6 +32,7 @@ Example:
|
||||
watchdog: watchdog@10007000 {
|
||||
compatible = "mediatek,mt8183-wdt",
|
||||
"mediatek,mt6589-wdt";
|
||||
mediatek,disable-extrst;
|
||||
reg = <0 0x10007000 0 0x100>;
|
||||
interrupts = <GIC_SPI 139 IRQ_TYPE_NONE>;
|
||||
timeout-sec = <10>;
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/regulator/db8500-prcmu.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/platform_data/ux500_wdt.h>
|
||||
#include "db8500-prcmu-regs.h"
|
||||
|
||||
/* Index of different voltages to be used when accessing AVSData */
|
||||
@ -2939,18 +2938,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct ux500_wdt_data db8500_wdt_pdata = {
|
||||
.timeout = 600, /* 10 minutes */
|
||||
.has_28_bits_resolution = true,
|
||||
};
|
||||
|
||||
static const struct mfd_cell common_prcmu_devs[] = {
|
||||
{
|
||||
.name = "ux500_wdt",
|
||||
.platform_data = &db8500_wdt_pdata,
|
||||
.pdata_size = sizeof(db8500_wdt_pdata),
|
||||
.id = -1,
|
||||
},
|
||||
MFD_CELL_NAME("db8500_wdt"),
|
||||
MFD_CELL_NAME("db8500-cpuidle"),
|
||||
};
|
||||
|
||||
|
@ -496,16 +496,18 @@ config S3C2410_WATCHDOG
|
||||
select WATCHDOG_CORE
|
||||
select MFD_SYSCON if ARCH_EXYNOS
|
||||
help
|
||||
Watchdog timer block in the Samsung SoCs. This will reboot
|
||||
the system when the timer expires with the watchdog enabled.
|
||||
Watchdog timer block in the Samsung S3C24xx, S3C64xx, S5Pv210 and
|
||||
Exynos SoCs. This will reboot the system when the timer expires with
|
||||
the watchdog enabled.
|
||||
|
||||
The driver is limited by the speed of the system's PCLK
|
||||
signal, so with reasonably fast systems (PCLK around 50-66MHz)
|
||||
then watchdog intervals of over approximately 20seconds are
|
||||
unavailable.
|
||||
|
||||
Choose Y/M here only if you build for such Samsung SoC.
|
||||
The driver can be built as a module by choosing M, and will
|
||||
be called s3c2410_wdt
|
||||
be called s3c2410_wdt.
|
||||
|
||||
config SA1100_WATCHDOG
|
||||
tristate "SA1100/PXA2xx watchdog"
|
||||
@ -561,22 +563,6 @@ config PNX4008_WATCHDOG
|
||||
|
||||
Say N if you are unsure.
|
||||
|
||||
config IOP_WATCHDOG
|
||||
tristate "IOP Watchdog"
|
||||
depends on ARCH_IOP13XX
|
||||
select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X)
|
||||
help
|
||||
Say Y here if to include support for the watchdog timer
|
||||
in the Intel IOP3XX & IOP13XX I/O Processors. This driver can
|
||||
be built as a module by choosing M. The module will
|
||||
be called iop_wdt.
|
||||
|
||||
Note: The IOP13XX watchdog does an Internal Bus Reset which will
|
||||
affect both cores and the peripherals of the IOP. The ATU-X
|
||||
and/or ATUe configuration registers will remain intact, but if
|
||||
operating as an Root Complex and/or Central Resource, the PCI-X
|
||||
and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER.
|
||||
|
||||
config DAVINCI_WATCHDOG
|
||||
tristate "DaVinci watchdog"
|
||||
depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST
|
||||
@ -743,17 +729,17 @@ config IMX7ULP_WDT
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx7ulp_wdt.
|
||||
|
||||
config UX500_WATCHDOG
|
||||
tristate "ST-Ericsson Ux500 watchdog"
|
||||
config DB500_WATCHDOG
|
||||
tristate "ST-Ericsson DB800 watchdog"
|
||||
depends on MFD_DB8500_PRCMU
|
||||
select WATCHDOG_CORE
|
||||
default y
|
||||
help
|
||||
Say Y here to include Watchdog timer support for the watchdog
|
||||
existing in the prcmu of ST-Ericsson Ux500 series platforms.
|
||||
existing in the prcmu of ST-Ericsson DB8500 platform.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ux500_wdt.
|
||||
module will be called db500_wdt.
|
||||
|
||||
config RETU_WATCHDOG
|
||||
tristate "Retu watchdog"
|
||||
@ -1052,6 +1038,7 @@ config EBC_C384_WDT
|
||||
config F71808E_WDT
|
||||
tristate "Fintek F718xx, F818xx Super I/O Watchdog"
|
||||
depends on X86
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This is the driver for the hardware watchdog on the Fintek F71808E,
|
||||
F71862FG, F71868, F71869, F71882FG, F71889FG, F81803, F81865, and
|
||||
@ -1679,7 +1666,7 @@ config SIBYTE_WDOG
|
||||
|
||||
config AR7_WDT
|
||||
tristate "TI AR7 Watchdog Timer"
|
||||
depends on AR7 || (MIPS && COMPILE_TEST)
|
||||
depends on AR7 || (MIPS && 32BIT && COMPILE_TEST)
|
||||
help
|
||||
Hardware driver for the TI AR7 Watchdog Timer.
|
||||
|
||||
|
@ -56,7 +56,6 @@ obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
|
||||
obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
|
||||
obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
|
||||
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
|
||||
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
|
||||
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
|
||||
obj-$(CONFIG_K3_RTI_WATCHDOG) += rti_wdt.o
|
||||
obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
|
||||
@ -69,7 +68,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
|
||||
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
|
||||
obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o
|
||||
obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o
|
||||
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
|
||||
obj-$(CONFIG_DB500_WATCHDOG) += db8500_wdt.o
|
||||
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
|
||||
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
|
||||
obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
|
||||
|
@ -63,8 +63,6 @@ static DEFINE_SPINLOCK(wdt_lock);
|
||||
/* XXX currently fixed, allows max margin ~68.72 secs */
|
||||
#define prescale_value 0xffff
|
||||
|
||||
/* Resource of the WDT registers */
|
||||
static struct resource *ar7_regs_wdt;
|
||||
/* Pointer to the remapped WDT IO space */
|
||||
static struct ar7_wdt *ar7_wdt;
|
||||
|
||||
@ -265,9 +263,7 @@ static int ar7_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ar7_regs_wdt =
|
||||
platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
ar7_wdt = devm_ioremap_resource(&pdev->dev, ar7_regs_wdt);
|
||||
ar7_wdt = devm_platform_ioremap_resource_byname(pdev, "regs");
|
||||
if (IS_ERR(ar7_wdt))
|
||||
return PTR_ERR(ar7_wdt);
|
||||
|
||||
|
@ -207,6 +207,8 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
|
||||
bcm63xx_wdt_pet();
|
||||
|
||||
fallthrough;
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(wdt_time, p);
|
||||
|
||||
|
@ -117,6 +117,13 @@ static int da9062_wdt_ping(struct watchdog_device *wdd)
|
||||
struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Prevent pings from occurring late in system poweroff/reboot sequence
|
||||
* and possibly locking out restart handler from accessing i2c bus.
|
||||
*/
|
||||
if (system_state > SYSTEM_RUNNING)
|
||||
return 0;
|
||||
|
||||
ret = da9062_reset_watchdog_timer(wdt);
|
||||
if (ret)
|
||||
dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
|
||||
|
@ -121,6 +121,13 @@ static int da9063_wdt_ping(struct watchdog_device *wdd)
|
||||
struct da9063 *da9063 = watchdog_get_drvdata(wdd);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Prevent pings from occurring late in system poweroff/reboot sequence
|
||||
* and possibly locking out restart handler from accessing i2c bus.
|
||||
*/
|
||||
if (system_state > SYSTEM_RUNNING)
|
||||
return 0;
|
||||
|
||||
ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F,
|
||||
DA9063_WATCHDOG);
|
||||
if (ret)
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/ux500_wdt.h>
|
||||
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
|
||||
@ -23,7 +22,6 @@
|
||||
|
||||
#define WATCHDOG_MIN 0
|
||||
#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */
|
||||
#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */
|
||||
|
||||
static unsigned int timeout = WATCHDOG_TIMEOUT;
|
||||
module_param(timeout, uint, 0);
|
||||
@ -37,67 +35,60 @@ MODULE_PARM_DESC(nowayout,
|
||||
"Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
static int ux500_wdt_start(struct watchdog_device *wdd)
|
||||
static int db8500_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
return prcmu_enable_a9wdog(PRCMU_WDOG_ALL);
|
||||
}
|
||||
|
||||
static int ux500_wdt_stop(struct watchdog_device *wdd)
|
||||
static int db8500_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
return prcmu_disable_a9wdog(PRCMU_WDOG_ALL);
|
||||
}
|
||||
|
||||
static int ux500_wdt_keepalive(struct watchdog_device *wdd)
|
||||
static int db8500_wdt_keepalive(struct watchdog_device *wdd)
|
||||
{
|
||||
return prcmu_kick_a9wdog(PRCMU_WDOG_ALL);
|
||||
}
|
||||
|
||||
static int ux500_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
static int db8500_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
ux500_wdt_stop(wdd);
|
||||
db8500_wdt_stop(wdd);
|
||||
prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
|
||||
ux500_wdt_start(wdd);
|
||||
db8500_wdt_start(wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ux500_wdt_info = {
|
||||
static const struct watchdog_info db8500_wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||
.identity = "Ux500 WDT",
|
||||
.identity = "DB8500 WDT",
|
||||
.firmware_version = 1,
|
||||
};
|
||||
|
||||
static const struct watchdog_ops ux500_wdt_ops = {
|
||||
static const struct watchdog_ops db8500_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = ux500_wdt_start,
|
||||
.stop = ux500_wdt_stop,
|
||||
.ping = ux500_wdt_keepalive,
|
||||
.set_timeout = ux500_wdt_set_timeout,
|
||||
.start = db8500_wdt_start,
|
||||
.stop = db8500_wdt_stop,
|
||||
.ping = db8500_wdt_keepalive,
|
||||
.set_timeout = db8500_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static struct watchdog_device ux500_wdt = {
|
||||
.info = &ux500_wdt_info,
|
||||
.ops = &ux500_wdt_ops,
|
||||
static struct watchdog_device db8500_wdt = {
|
||||
.info = &db8500_wdt_info,
|
||||
.ops = &db8500_wdt_ops,
|
||||
.min_timeout = WATCHDOG_MIN,
|
||||
.max_timeout = WATCHDOG_MAX32,
|
||||
.max_timeout = WATCHDOG_MAX28,
|
||||
};
|
||||
|
||||
static int ux500_wdt_probe(struct platform_device *pdev)
|
||||
static int db8500_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
struct ux500_wdt_data *pdata = dev_get_platdata(dev);
|
||||
|
||||
if (pdata) {
|
||||
if (pdata->timeout > 0)
|
||||
timeout = pdata->timeout;
|
||||
if (pdata->has_28_bits_resolution)
|
||||
ux500_wdt.max_timeout = WATCHDOG_MAX28;
|
||||
}
|
||||
|
||||
ux500_wdt.parent = dev;
|
||||
watchdog_set_nowayout(&ux500_wdt, nowayout);
|
||||
timeout = 600; /* Default to 10 minutes */
|
||||
db8500_wdt.parent = dev;
|
||||
watchdog_set_nowayout(&db8500_wdt, nowayout);
|
||||
|
||||
/* disable auto off on sleep */
|
||||
prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
|
||||
@ -105,7 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev)
|
||||
/* set HW initial value */
|
||||
prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
|
||||
|
||||
ret = devm_watchdog_register_device(dev, &ux500_wdt);
|
||||
ret = devm_watchdog_register_device(dev, &db8500_wdt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -115,47 +106,47 @@ static int ux500_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ux500_wdt_suspend(struct platform_device *pdev,
|
||||
static int db8500_wdt_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
if (watchdog_active(&ux500_wdt)) {
|
||||
ux500_wdt_stop(&ux500_wdt);
|
||||
if (watchdog_active(&db8500_wdt)) {
|
||||
db8500_wdt_stop(&db8500_wdt);
|
||||
prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true);
|
||||
|
||||
prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
|
||||
ux500_wdt_start(&ux500_wdt);
|
||||
db8500_wdt_start(&db8500_wdt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ux500_wdt_resume(struct platform_device *pdev)
|
||||
static int db8500_wdt_resume(struct platform_device *pdev)
|
||||
{
|
||||
if (watchdog_active(&ux500_wdt)) {
|
||||
ux500_wdt_stop(&ux500_wdt);
|
||||
if (watchdog_active(&db8500_wdt)) {
|
||||
db8500_wdt_stop(&db8500_wdt);
|
||||
prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
|
||||
|
||||
prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
|
||||
ux500_wdt_start(&ux500_wdt);
|
||||
db8500_wdt_start(&db8500_wdt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ux500_wdt_suspend NULL
|
||||
#define ux500_wdt_resume NULL
|
||||
#define db8500_wdt_suspend NULL
|
||||
#define db8500_wdt_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver ux500_wdt_driver = {
|
||||
.probe = ux500_wdt_probe,
|
||||
.suspend = ux500_wdt_suspend,
|
||||
.resume = ux500_wdt_resume,
|
||||
static struct platform_driver db8500_wdt_driver = {
|
||||
.probe = db8500_wdt_probe,
|
||||
.suspend = db8500_wdt_suspend,
|
||||
.resume = db8500_wdt_resume,
|
||||
.driver = {
|
||||
.name = "ux500_wdt",
|
||||
.name = "db8500_wdt",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ux500_wdt_driver);
|
||||
module_platform_driver(db8500_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
|
||||
MODULE_DESCRIPTION("Ux500 Watchdog Driver");
|
||||
MODULE_DESCRIPTION("DB8500 Watchdog Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:ux500_wdt");
|
||||
MODULE_ALIAS("platform:db8500_wdt");
|
@ -9,16 +9,11 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define DRVNAME "f71808e_wdt"
|
||||
@ -81,7 +76,6 @@ static unsigned short force_id;
|
||||
module_param(force_id, ushort, 0);
|
||||
MODULE_PARM_DESC(force_id, "Override the detected device ID");
|
||||
|
||||
static const int max_timeout = WATCHDOG_MAX_TIMEOUT;
|
||||
static int timeout = WATCHDOG_TIMEOUT; /* default timeout in seconds */
|
||||
module_param(timeout, int, 0);
|
||||
MODULE_PARM_DESC(timeout,
|
||||
@ -113,7 +107,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
|
||||
enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
|
||||
f81803, f81865, f81866};
|
||||
|
||||
static const char *f71808e_names[] = {
|
||||
static const char * const fintek_wdt_names[] = {
|
||||
"f71808fg",
|
||||
"f71858fg",
|
||||
"f71862fg",
|
||||
@ -136,24 +130,20 @@ static inline int superio_enter(int base);
|
||||
static inline void superio_select(int base, int ld);
|
||||
static inline void superio_exit(int base);
|
||||
|
||||
struct watchdog_data {
|
||||
struct fintek_wdt {
|
||||
struct watchdog_device wdd;
|
||||
unsigned short sioaddr;
|
||||
enum chips type;
|
||||
unsigned long opened;
|
||||
struct mutex lock;
|
||||
char expect_close;
|
||||
struct watchdog_info ident;
|
||||
|
||||
unsigned short timeout;
|
||||
u8 timer_val; /* content for the wd_time register */
|
||||
char minutes_mode;
|
||||
u8 pulse_val; /* pulse width flag */
|
||||
char pulse_mode; /* enable pulse output mode? */
|
||||
char caused_reboot; /* last reboot was by the watchdog */
|
||||
};
|
||||
|
||||
static struct watchdog_data watchdog = {
|
||||
.lock = __MUTEX_INITIALIZER(watchdog.lock),
|
||||
struct fintek_wdt_pdata {
|
||||
enum chips type;
|
||||
};
|
||||
|
||||
/* Super I/O functions */
|
||||
@ -218,156 +208,142 @@ static inline void superio_exit(int base)
|
||||
release_region(base, 2);
|
||||
}
|
||||
|
||||
static int watchdog_set_timeout(int timeout)
|
||||
static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
|
||||
{
|
||||
if (timeout <= 0
|
||||
|| timeout > max_timeout) {
|
||||
pr_err("watchdog timeout out of range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
|
||||
|
||||
mutex_lock(&watchdog.lock);
|
||||
|
||||
watchdog.timeout = timeout;
|
||||
if (timeout > 0xff) {
|
||||
watchdog.timer_val = DIV_ROUND_UP(timeout, 60);
|
||||
watchdog.minutes_mode = true;
|
||||
wd->timer_val = DIV_ROUND_UP(timeout, 60);
|
||||
wd->minutes_mode = true;
|
||||
timeout = wd->timer_val * 60;
|
||||
} else {
|
||||
watchdog.timer_val = timeout;
|
||||
watchdog.minutes_mode = false;
|
||||
wd->timer_val = timeout;
|
||||
wd->minutes_mode = false;
|
||||
}
|
||||
|
||||
mutex_unlock(&watchdog.lock);
|
||||
wdd->timeout = timeout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watchdog_set_pulse_width(unsigned int pw)
|
||||
static int fintek_wdt_set_pulse_width(struct fintek_wdt *wd, unsigned int pw)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned int t1 = 25, t2 = 125, t3 = 5000;
|
||||
|
||||
if (watchdog.type == f71868) {
|
||||
if (wd->type == f71868) {
|
||||
t1 = 30;
|
||||
t2 = 150;
|
||||
t3 = 6000;
|
||||
}
|
||||
|
||||
mutex_lock(&watchdog.lock);
|
||||
|
||||
if (pw <= 1) {
|
||||
watchdog.pulse_val = 0;
|
||||
wd->pulse_val = 0;
|
||||
} else if (pw <= t1) {
|
||||
watchdog.pulse_val = 1;
|
||||
wd->pulse_val = 1;
|
||||
} else if (pw <= t2) {
|
||||
watchdog.pulse_val = 2;
|
||||
wd->pulse_val = 2;
|
||||
} else if (pw <= t3) {
|
||||
watchdog.pulse_val = 3;
|
||||
wd->pulse_val = 3;
|
||||
} else {
|
||||
pr_err("pulse width out of range\n");
|
||||
err = -EINVAL;
|
||||
goto exit_unlock;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
watchdog.pulse_mode = pw;
|
||||
wd->pulse_mode = pw;
|
||||
|
||||
exit_unlock:
|
||||
mutex_unlock(&watchdog.lock);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watchdog_keepalive(void)
|
||||
static int fintek_wdt_keepalive(struct watchdog_device *wdd)
|
||||
{
|
||||
int err = 0;
|
||||
struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
|
||||
int err;
|
||||
|
||||
mutex_lock(&watchdog.lock);
|
||||
err = superio_enter(watchdog.sioaddr);
|
||||
err = superio_enter(wd->sioaddr);
|
||||
if (err)
|
||||
goto exit_unlock;
|
||||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
return err;
|
||||
superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
|
||||
|
||||
if (watchdog.minutes_mode)
|
||||
if (wd->minutes_mode)
|
||||
/* select minutes for timer units */
|
||||
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
|
||||
superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_UNIT);
|
||||
else
|
||||
/* select seconds for timer units */
|
||||
superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
|
||||
superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_UNIT);
|
||||
|
||||
/* Set timer value */
|
||||
superio_outb(watchdog.sioaddr, F71808FG_REG_WD_TIME,
|
||||
watchdog.timer_val);
|
||||
superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME,
|
||||
wd->timer_val);
|
||||
|
||||
superio_exit(watchdog.sioaddr);
|
||||
superio_exit(wd->sioaddr);
|
||||
|
||||
exit_unlock:
|
||||
mutex_unlock(&watchdog.lock);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watchdog_start(void)
|
||||
static int fintek_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
|
||||
int err;
|
||||
u8 tmp;
|
||||
|
||||
/* Make sure we don't die as soon as the watchdog is enabled below */
|
||||
err = watchdog_keepalive();
|
||||
err = fintek_wdt_keepalive(wdd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&watchdog.lock);
|
||||
err = superio_enter(watchdog.sioaddr);
|
||||
err = superio_enter(wd->sioaddr);
|
||||
if (err)
|
||||
goto exit_unlock;
|
||||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
return err;
|
||||
superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
|
||||
|
||||
/* Watchdog pin configuration */
|
||||
switch (watchdog.type) {
|
||||
switch (wd->type) {
|
||||
case f71808fg:
|
||||
/* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT2, 3);
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 3);
|
||||
superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3);
|
||||
superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3);
|
||||
break;
|
||||
|
||||
case f71862fg:
|
||||
if (f71862fg_pin == 63) {
|
||||
/* SPI must be disabled first to use this pin! */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_ROM_ADDR_SEL, 6);
|
||||
superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 4);
|
||||
superio_clear_bit(wd->sioaddr, SIO_REG_ROM_ADDR_SEL, 6);
|
||||
superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT3, 4);
|
||||
} else if (f71862fg_pin == 56) {
|
||||
superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1);
|
||||
superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case f71868:
|
||||
case f71869:
|
||||
/* GPIO14 --> WDTRST# */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4);
|
||||
superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4);
|
||||
break;
|
||||
|
||||
case f71882fg:
|
||||
/* Set pin 56 to WDTRST# */
|
||||
superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1);
|
||||
superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
|
||||
break;
|
||||
|
||||
case f71889fg:
|
||||
/* set pin 40 to WDTRST# */
|
||||
superio_outb(watchdog.sioaddr, SIO_REG_MFUNCT3,
|
||||
superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf);
|
||||
superio_outb(wd->sioaddr, SIO_REG_MFUNCT3,
|
||||
superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf);
|
||||
break;
|
||||
|
||||
case f81803:
|
||||
/* Enable TSI Level register bank */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_CLOCK_SEL, 3);
|
||||
superio_clear_bit(wd->sioaddr, SIO_REG_CLOCK_SEL, 3);
|
||||
/* Set pin 27 to WDTRST# */
|
||||
superio_outb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
|
||||
superio_inb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL));
|
||||
superio_outb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
|
||||
superio_inb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL));
|
||||
break;
|
||||
|
||||
case f81865:
|
||||
/* Set pin 70 to WDTRST# */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5);
|
||||
superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5);
|
||||
break;
|
||||
|
||||
case f81866:
|
||||
@ -377,12 +353,12 @@ static int watchdog_start(void)
|
||||
* BIT5: 0 -> WDTRST#
|
||||
* 1 -> GPIO15
|
||||
*/
|
||||
tmp = superio_inb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL);
|
||||
tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL);
|
||||
tmp &= ~(BIT(3) | BIT(0));
|
||||
tmp |= BIT(2);
|
||||
superio_outb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, tmp);
|
||||
superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp);
|
||||
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, 5);
|
||||
superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -394,300 +370,114 @@ static int watchdog_start(void)
|
||||
goto exit_superio;
|
||||
}
|
||||
|
||||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0);
|
||||
superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
|
||||
superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0);
|
||||
|
||||
if (watchdog.type == f81865 || watchdog.type == f81866)
|
||||
superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF,
|
||||
if (wd->type == f81865 || wd->type == f81866)
|
||||
superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF,
|
||||
F81865_FLAG_WDOUT_EN);
|
||||
else
|
||||
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF,
|
||||
superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF,
|
||||
F71808FG_FLAG_WDOUT_EN);
|
||||
|
||||
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
|
||||
superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_EN);
|
||||
|
||||
if (watchdog.pulse_mode) {
|
||||
if (wd->pulse_mode) {
|
||||
/* Select "pulse" output mode with given duration */
|
||||
u8 wdt_conf = superio_inb(watchdog.sioaddr,
|
||||
u8 wdt_conf = superio_inb(wd->sioaddr,
|
||||
F71808FG_REG_WDT_CONF);
|
||||
|
||||
/* Set WD_PSWIDTH bits (1:0) */
|
||||
wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03);
|
||||
wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_val & 0x03);
|
||||
/* Set WD_PULSE to "pulse" mode */
|
||||
wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE);
|
||||
|
||||
superio_outb(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
|
||||
superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF,
|
||||
wdt_conf);
|
||||
} else {
|
||||
/* Select "level" output mode */
|
||||
superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
|
||||
superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_PULSE);
|
||||
}
|
||||
|
||||
exit_superio:
|
||||
superio_exit(watchdog.sioaddr);
|
||||
exit_unlock:
|
||||
mutex_unlock(&watchdog.lock);
|
||||
superio_exit(wd->sioaddr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int watchdog_stop(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&watchdog.lock);
|
||||
err = superio_enter(watchdog.sioaddr);
|
||||
if (err)
|
||||
goto exit_unlock;
|
||||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
|
||||
superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_EN);
|
||||
|
||||
superio_exit(watchdog.sioaddr);
|
||||
|
||||
exit_unlock:
|
||||
mutex_unlock(&watchdog.lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int watchdog_get_status(void)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
mutex_lock(&watchdog.lock);
|
||||
status = (watchdog.caused_reboot) ? WDIOF_CARDRESET : 0;
|
||||
mutex_unlock(&watchdog.lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool watchdog_is_running(void)
|
||||
{
|
||||
/*
|
||||
* if we fail to determine the watchdog's status assume it to be
|
||||
* running to be on the safe side
|
||||
*/
|
||||
bool is_running = true;
|
||||
|
||||
mutex_lock(&watchdog.lock);
|
||||
if (superio_enter(watchdog.sioaddr))
|
||||
goto exit_unlock;
|
||||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
|
||||
is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0))
|
||||
&& (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF)
|
||||
& BIT(F71808FG_FLAG_WD_EN));
|
||||
|
||||
superio_exit(watchdog.sioaddr);
|
||||
|
||||
exit_unlock:
|
||||
mutex_unlock(&watchdog.lock);
|
||||
return is_running;
|
||||
}
|
||||
|
||||
/* /dev/watchdog api */
|
||||
|
||||
static int watchdog_open(struct inode *inode, struct file *file)
|
||||
static int fintek_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
|
||||
int err;
|
||||
|
||||
/* If the watchdog is alive we don't need to start it again */
|
||||
if (test_and_set_bit(0, &watchdog.opened))
|
||||
return -EBUSY;
|
||||
|
||||
err = watchdog_start();
|
||||
if (err) {
|
||||
clear_bit(0, &watchdog.opened);
|
||||
err = superio_enter(wd->sioaddr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
|
||||
|
||||
if (nowayout)
|
||||
__module_get(THIS_MODULE);
|
||||
superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_EN);
|
||||
|
||||
watchdog.expect_close = 0;
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
superio_exit(wd->sioaddr);
|
||||
|
||||
static int watchdog_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
clear_bit(0, &watchdog.opened);
|
||||
|
||||
if (!watchdog.expect_close) {
|
||||
watchdog_keepalive();
|
||||
pr_crit("Unexpected close, not stopping watchdog!\n");
|
||||
} else if (!nowayout) {
|
||||
watchdog_stop();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_write:
|
||||
* @file: file handle to the watchdog
|
||||
* @buf: buffer to write
|
||||
* @count: count of bytes
|
||||
* @ppos: pointer to the position to write. No seeks allowed
|
||||
*
|
||||
* A write to a watchdog device is defined as a keepalive signal. Any
|
||||
* write of data will do, as we we don't define content meaning.
|
||||
*/
|
||||
|
||||
static ssize_t watchdog_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
static bool fintek_wdt_is_running(struct fintek_wdt *wd, u8 wdt_conf)
|
||||
{
|
||||
if (count) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
/* In case it was set long ago */
|
||||
bool expect_close = false;
|
||||
|
||||
for (i = 0; i != count; i++) {
|
||||
char c;
|
||||
if (get_user(c, buf + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
expect_close = true;
|
||||
}
|
||||
|
||||
/* Properly order writes across fork()ed processes */
|
||||
mutex_lock(&watchdog.lock);
|
||||
watchdog.expect_close = expect_close;
|
||||
mutex_unlock(&watchdog.lock);
|
||||
}
|
||||
|
||||
/* someone wrote to us, we should restart timer */
|
||||
watchdog_keepalive();
|
||||
}
|
||||
return count;
|
||||
return (superio_inb(wd->sioaddr, SIO_REG_ENABLE) & BIT(0))
|
||||
&& (wdt_conf & BIT(F71808FG_FLAG_WD_EN));
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_ioctl:
|
||||
* @inode: inode of the device
|
||||
* @file: file handle to the device
|
||||
* @cmd: watchdog command
|
||||
* @arg: argument pointer
|
||||
*
|
||||
* The watchdog API defines a common set of functions for all watchdogs
|
||||
* according to their available features.
|
||||
*/
|
||||
static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int status;
|
||||
int new_options;
|
||||
int new_timeout;
|
||||
union {
|
||||
struct watchdog_info __user *ident;
|
||||
int __user *i;
|
||||
} uarg;
|
||||
|
||||
uarg.i = (int __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(uarg.ident, &watchdog.ident,
|
||||
sizeof(watchdog.ident)) ? -EFAULT : 0;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
status = watchdog_get_status();
|
||||
if (status < 0)
|
||||
return status;
|
||||
return put_user(status, uarg.i);
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, uarg.i);
|
||||
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(new_options, uarg.i))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_options & WDIOS_DISABLECARD)
|
||||
watchdog_stop();
|
||||
|
||||
if (new_options & WDIOS_ENABLECARD)
|
||||
return watchdog_start();
|
||||
fallthrough;
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
watchdog_keepalive();
|
||||
return 0;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(new_timeout, uarg.i))
|
||||
return -EFAULT;
|
||||
|
||||
if (watchdog_set_timeout(new_timeout))
|
||||
return -EINVAL;
|
||||
|
||||
watchdog_keepalive();
|
||||
fallthrough;
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(watchdog.timeout, uarg.i);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int watchdog_notify_sys(struct notifier_block *this, unsigned long code,
|
||||
void *unused)
|
||||
{
|
||||
if (code == SYS_DOWN || code == SYS_HALT)
|
||||
watchdog_stop();
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static const struct file_operations watchdog_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = watchdog_open,
|
||||
.release = watchdog_release,
|
||||
.write = watchdog_write,
|
||||
.unlocked_ioctl = watchdog_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
static const struct watchdog_ops fintek_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = fintek_wdt_start,
|
||||
.stop = fintek_wdt_stop,
|
||||
.ping = fintek_wdt_keepalive,
|
||||
.set_timeout = fintek_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static struct miscdevice watchdog_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &watchdog_fops,
|
||||
};
|
||||
|
||||
static struct notifier_block watchdog_notifier = {
|
||||
.notifier_call = watchdog_notify_sys,
|
||||
};
|
||||
|
||||
static int __init watchdog_init(int sioaddr)
|
||||
static int fintek_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fintek_wdt_pdata *pdata;
|
||||
struct watchdog_device *wdd;
|
||||
struct fintek_wdt *wd;
|
||||
int wdt_conf, err = 0;
|
||||
struct resource *res;
|
||||
int sioaddr;
|
||||
|
||||
/* No need to lock watchdog.lock here because no entry points
|
||||
* into the module have been registered yet.
|
||||
*/
|
||||
watchdog.sioaddr = sioaddr;
|
||||
watchdog.ident.options = WDIOF_MAGICCLOSE
|
||||
| WDIOF_KEEPALIVEPING
|
||||
| WDIOF_CARDRESET;
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res)
|
||||
return -ENXIO;
|
||||
|
||||
snprintf(watchdog.ident.identity,
|
||||
sizeof(watchdog.ident.identity), "%s watchdog",
|
||||
f71808e_names[watchdog.type]);
|
||||
sioaddr = res->start;
|
||||
|
||||
wd = devm_kzalloc(dev, sizeof(*wd), GFP_KERNEL);
|
||||
if (!wd)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = dev->platform_data;
|
||||
|
||||
wd->type = pdata->type;
|
||||
wd->sioaddr = sioaddr;
|
||||
wd->ident.options = WDIOF_SETTIMEOUT
|
||||
| WDIOF_MAGICCLOSE
|
||||
| WDIOF_KEEPALIVEPING
|
||||
| WDIOF_CARDRESET;
|
||||
|
||||
snprintf(wd->ident.identity,
|
||||
sizeof(wd->ident.identity), "%s watchdog",
|
||||
fintek_wdt_names[wd->type]);
|
||||
|
||||
err = superio_enter(sioaddr);
|
||||
if (err)
|
||||
return err;
|
||||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
|
||||
|
||||
wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
|
||||
watchdog.caused_reboot = wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS);
|
||||
|
||||
/*
|
||||
* We don't want WDTMOUT_STS to stick around till regular reboot.
|
||||
@ -696,84 +486,54 @@ static int __init watchdog_init(int sioaddr)
|
||||
superio_outb(sioaddr, F71808FG_REG_WDT_CONF,
|
||||
wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS));
|
||||
|
||||
wdd = &wd->wdd;
|
||||
|
||||
if (fintek_wdt_is_running(wd, wdt_conf))
|
||||
set_bit(WDOG_HW_RUNNING, &wdd->status);
|
||||
|
||||
superio_exit(sioaddr);
|
||||
|
||||
err = watchdog_set_timeout(timeout);
|
||||
if (err)
|
||||
return err;
|
||||
err = watchdog_set_pulse_width(pulse_width);
|
||||
if (err)
|
||||
return err;
|
||||
wdd->parent = dev;
|
||||
wdd->info = &wd->ident;
|
||||
wdd->ops = &fintek_wdt_ops;
|
||||
wdd->min_timeout = 1;
|
||||
wdd->max_timeout = WATCHDOG_MAX_TIMEOUT;
|
||||
|
||||
err = register_reboot_notifier(&watchdog_notifier);
|
||||
if (err)
|
||||
return err;
|
||||
watchdog_set_drvdata(wdd, wd);
|
||||
watchdog_set_nowayout(wdd, nowayout);
|
||||
watchdog_stop_on_unregister(wdd);
|
||||
watchdog_stop_on_reboot(wdd);
|
||||
watchdog_init_timeout(wdd, start_withtimeout ?: timeout, NULL);
|
||||
|
||||
err = misc_register(&watchdog_miscdev);
|
||||
if (err) {
|
||||
pr_err("cannot register miscdev on minor=%d\n",
|
||||
watchdog_miscdev.minor);
|
||||
goto exit_reboot;
|
||||
}
|
||||
if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS))
|
||||
wdd->bootstatus = WDIOF_CARDRESET;
|
||||
|
||||
/*
|
||||
* WATCHDOG_HANDLE_BOOT_ENABLED can result in keepalive being directly
|
||||
* called without a set_timeout before, so it needs to be done here
|
||||
* unconditionally.
|
||||
*/
|
||||
fintek_wdt_set_timeout(wdd, wdd->timeout);
|
||||
fintek_wdt_set_pulse_width(wd, pulse_width);
|
||||
|
||||
if (start_withtimeout) {
|
||||
if (start_withtimeout <= 0
|
||||
|| start_withtimeout > max_timeout) {
|
||||
pr_err("starting timeout out of range\n");
|
||||
err = -EINVAL;
|
||||
goto exit_miscdev;
|
||||
}
|
||||
|
||||
err = watchdog_start();
|
||||
err = fintek_wdt_start(wdd);
|
||||
if (err) {
|
||||
pr_err("cannot start watchdog timer\n");
|
||||
goto exit_miscdev;
|
||||
dev_err(dev, "cannot start watchdog timer\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
mutex_lock(&watchdog.lock);
|
||||
err = superio_enter(sioaddr);
|
||||
if (err)
|
||||
goto exit_unlock;
|
||||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
|
||||
if (start_withtimeout > 0xff) {
|
||||
/* select minutes for timer units */
|
||||
superio_set_bit(sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_UNIT);
|
||||
superio_outb(sioaddr, F71808FG_REG_WD_TIME,
|
||||
DIV_ROUND_UP(start_withtimeout, 60));
|
||||
} else {
|
||||
/* select seconds for timer units */
|
||||
superio_clear_bit(sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_UNIT);
|
||||
superio_outb(sioaddr, F71808FG_REG_WD_TIME,
|
||||
start_withtimeout);
|
||||
}
|
||||
|
||||
superio_exit(sioaddr);
|
||||
mutex_unlock(&watchdog.lock);
|
||||
|
||||
if (nowayout)
|
||||
__module_get(THIS_MODULE);
|
||||
|
||||
pr_info("watchdog started with initial timeout of %u sec\n",
|
||||
start_withtimeout);
|
||||
set_bit(WDOG_HW_RUNNING, &wdd->status);
|
||||
dev_info(dev, "watchdog started with initial timeout of %u sec\n",
|
||||
start_withtimeout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_unlock:
|
||||
mutex_unlock(&watchdog.lock);
|
||||
exit_miscdev:
|
||||
misc_deregister(&watchdog_miscdev);
|
||||
exit_reboot:
|
||||
unregister_reboot_notifier(&watchdog_notifier);
|
||||
|
||||
return err;
|
||||
return devm_watchdog_register_device(dev, wdd);
|
||||
}
|
||||
|
||||
static int __init f71808e_find(int sioaddr)
|
||||
static int __init fintek_wdt_find(int sioaddr)
|
||||
{
|
||||
enum chips type;
|
||||
u16 devid;
|
||||
int err = superio_enter(sioaddr);
|
||||
if (err)
|
||||
@ -789,36 +549,36 @@ static int __init f71808e_find(int sioaddr)
|
||||
devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
|
||||
switch (devid) {
|
||||
case SIO_F71808_ID:
|
||||
watchdog.type = f71808fg;
|
||||
type = f71808fg;
|
||||
break;
|
||||
case SIO_F71862_ID:
|
||||
watchdog.type = f71862fg;
|
||||
type = f71862fg;
|
||||
break;
|
||||
case SIO_F71868_ID:
|
||||
watchdog.type = f71868;
|
||||
type = f71868;
|
||||
break;
|
||||
case SIO_F71869_ID:
|
||||
case SIO_F71869A_ID:
|
||||
watchdog.type = f71869;
|
||||
type = f71869;
|
||||
break;
|
||||
case SIO_F71882_ID:
|
||||
watchdog.type = f71882fg;
|
||||
type = f71882fg;
|
||||
break;
|
||||
case SIO_F71889_ID:
|
||||
watchdog.type = f71889fg;
|
||||
type = f71889fg;
|
||||
break;
|
||||
case SIO_F71858_ID:
|
||||
/* Confirmed (by datasheet) not to have a watchdog. */
|
||||
err = -ENODEV;
|
||||
goto exit;
|
||||
case SIO_F81803_ID:
|
||||
watchdog.type = f81803;
|
||||
type = f81803;
|
||||
break;
|
||||
case SIO_F81865_ID:
|
||||
watchdog.type = f81865;
|
||||
type = f81865;
|
||||
break;
|
||||
case SIO_F81866_ID:
|
||||
watchdog.type = f81866;
|
||||
type = f81866;
|
||||
break;
|
||||
default:
|
||||
pr_info("Unrecognized Fintek device: %04x\n",
|
||||
@ -828,17 +588,29 @@ static int __init f71808e_find(int sioaddr)
|
||||
}
|
||||
|
||||
pr_info("Found %s watchdog chip, revision %d\n",
|
||||
f71808e_names[watchdog.type],
|
||||
fintek_wdt_names[type],
|
||||
(int)superio_inb(sioaddr, SIO_REG_DEVREV));
|
||||
|
||||
exit:
|
||||
superio_exit(sioaddr);
|
||||
return err;
|
||||
return err ? err : type;
|
||||
}
|
||||
|
||||
static int __init f71808e_init(void)
|
||||
static struct platform_driver fintek_wdt_driver = {
|
||||
.probe = fintek_wdt_probe,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device *fintek_wdt_pdev;
|
||||
|
||||
static int __init fintek_wdt_init(void)
|
||||
{
|
||||
static const unsigned short addrs[] = { 0x2e, 0x4e };
|
||||
int err = -ENODEV;
|
||||
struct fintek_wdt_pdata pdata;
|
||||
struct resource wdt_res = {};
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (f71862fg_pin != 63 && f71862fg_pin != 56) {
|
||||
@ -847,29 +619,42 @@ static int __init f71808e_init(void)
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(addrs); i++) {
|
||||
err = f71808e_find(addrs[i]);
|
||||
if (err == 0)
|
||||
ret = fintek_wdt_find(addrs[i]);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(addrs))
|
||||
return err;
|
||||
return ret;
|
||||
|
||||
return watchdog_init(addrs[i]);
|
||||
pdata.type = ret;
|
||||
|
||||
platform_driver_register(&fintek_wdt_driver);
|
||||
|
||||
wdt_res.name = "superio port";
|
||||
wdt_res.flags = IORESOURCE_IO;
|
||||
wdt_res.start = addrs[i];
|
||||
wdt_res.end = addrs[i] + 1;
|
||||
|
||||
fintek_wdt_pdev = platform_device_register_resndata(NULL, DRVNAME, -1,
|
||||
&wdt_res, 1,
|
||||
&pdata, sizeof(pdata));
|
||||
if (IS_ERR(fintek_wdt_pdev)) {
|
||||
platform_driver_unregister(&fintek_wdt_driver);
|
||||
return PTR_ERR(fintek_wdt_pdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit f71808e_exit(void)
|
||||
static void __exit fintek_wdt_exit(void)
|
||||
{
|
||||
if (watchdog_is_running()) {
|
||||
pr_warn("Watchdog timer still running, stopping it\n");
|
||||
watchdog_stop();
|
||||
}
|
||||
misc_deregister(&watchdog_miscdev);
|
||||
unregister_reboot_notifier(&watchdog_notifier);
|
||||
platform_device_unregister(fintek_wdt_pdev);
|
||||
platform_driver_unregister(&fintek_wdt_driver);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("F71808E Watchdog Driver");
|
||||
MODULE_AUTHOR("Giel van Schijndel <me@mortis.eu>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(f71808e_init);
|
||||
module_exit(f71808e_exit);
|
||||
module_init(fintek_wdt_init);
|
||||
module_exit(fintek_wdt_exit);
|
||||
|
@ -94,7 +94,6 @@ struct iTCO_wdt_private {
|
||||
* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2),
|
||||
* or memory-mapped PMC register bit 4 (TCO version 3).
|
||||
*/
|
||||
struct resource *gcs_pmc_res;
|
||||
unsigned long __iomem *gcs_pmc;
|
||||
/* the lock for io operations */
|
||||
spinlock_t io_lock;
|
||||
@ -424,6 +423,16 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
|
||||
return time_left;
|
||||
}
|
||||
|
||||
static void iTCO_wdt_set_running(struct iTCO_wdt_private *p)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
/* Bit 11: TCO Timer Halt -> 0 = The TCO timer is * enabled */
|
||||
val = inw(TCO1_CNT(p));
|
||||
if (!(val & BIT(11)))
|
||||
set_bit(WDOG_HW_RUNNING, &p->wddev.status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Kernel Interfaces
|
||||
*/
|
||||
@ -497,10 +506,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
|
||||
*/
|
||||
if (p->iTCO_version >= 2 && p->iTCO_version < 6 &&
|
||||
!pdata->no_reboot_use_pmc) {
|
||||
p->gcs_pmc_res = platform_get_resource(pdev,
|
||||
IORESOURCE_MEM,
|
||||
ICH_RES_MEM_GCS_PMC);
|
||||
p->gcs_pmc = devm_ioremap_resource(dev, p->gcs_pmc_res);
|
||||
p->gcs_pmc = devm_platform_ioremap_resource(pdev, ICH_RES_MEM_GCS_PMC);
|
||||
if (IS_ERR(p->gcs_pmc))
|
||||
return PTR_ERR(p->gcs_pmc);
|
||||
}
|
||||
@ -566,8 +572,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_drvdata(&p->wddev, p);
|
||||
platform_set_drvdata(pdev, p);
|
||||
|
||||
/* Make sure the watchdog is not running */
|
||||
iTCO_wdt_stop(&p->wddev);
|
||||
iTCO_wdt_set_running(p);
|
||||
|
||||
/* Check that the heartbeat value is within it's range;
|
||||
if not reset to the default */
|
||||
|
@ -1,250 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* drivers/char/watchdog/iop_wdt.c
|
||||
*
|
||||
* WDT driver for Intel I/O Processors
|
||||
* Copyright (C) 2005, Intel Corporation.
|
||||
*
|
||||
* Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc.
|
||||
*
|
||||
* Curt E Bruns <curt.e.bruns@intel.com>
|
||||
* Peter Milne <peter.milne@d-tacq.com>
|
||||
* Dan Williams <dan.j.williams@intel.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
static unsigned long wdt_status;
|
||||
static unsigned long boot_status;
|
||||
static DEFINE_SPINLOCK(wdt_lock);
|
||||
|
||||
#define WDT_IN_USE 0
|
||||
#define WDT_OK_TO_CLOSE 1
|
||||
#define WDT_ENABLED 2
|
||||
|
||||
static unsigned long iop_watchdog_timeout(void)
|
||||
{
|
||||
return (0xffffffffUL / get_iop_tick_rate());
|
||||
}
|
||||
|
||||
/**
|
||||
* wdt_supports_disable - determine if we are accessing a iop13xx watchdog
|
||||
* or iop3xx by whether it has a disable command
|
||||
*/
|
||||
static int wdt_supports_disable(void)
|
||||
{
|
||||
int can_disable;
|
||||
|
||||
if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM)
|
||||
can_disable = 1;
|
||||
else
|
||||
can_disable = 0;
|
||||
|
||||
return can_disable;
|
||||
}
|
||||
|
||||
static void wdt_enable(void)
|
||||
{
|
||||
/* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF
|
||||
* Takes approx. 10.7s to timeout
|
||||
*/
|
||||
spin_lock(&wdt_lock);
|
||||
write_wdtcr(IOP_WDTCR_EN_ARM);
|
||||
write_wdtcr(IOP_WDTCR_EN);
|
||||
spin_unlock(&wdt_lock);
|
||||
}
|
||||
|
||||
/* returns 0 if the timer was successfully disabled */
|
||||
static int wdt_disable(void)
|
||||
{
|
||||
/* Stop Counting */
|
||||
if (wdt_supports_disable()) {
|
||||
spin_lock(&wdt_lock);
|
||||
write_wdtcr(IOP_WDTCR_DIS_ARM);
|
||||
write_wdtcr(IOP_WDTCR_DIS);
|
||||
clear_bit(WDT_ENABLED, &wdt_status);
|
||||
spin_unlock(&wdt_lock);
|
||||
pr_info("Disabled\n");
|
||||
return 0;
|
||||
} else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int iop_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
|
||||
return -EBUSY;
|
||||
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
wdt_enable();
|
||||
set_bit(WDT_ENABLED, &wdt_status);
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
static ssize_t iop_wdt_write(struct file *file, const char *data, size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
if (len) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
|
||||
for (i = 0; i != len; i++) {
|
||||
char c;
|
||||
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
}
|
||||
}
|
||||
wdt_enable();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||
.identity = "iop watchdog",
|
||||
};
|
||||
|
||||
static long iop_wdt_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int options;
|
||||
int ret = -ENOTTY;
|
||||
int __user *argp = (int __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
if (copy_to_user(argp, &ident, sizeof(ident)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
ret = put_user(0, argp);
|
||||
break;
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
ret = put_user(boot_status, argp);
|
||||
break;
|
||||
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(options, (int *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
if (options & WDIOS_DISABLECARD) {
|
||||
if (!nowayout) {
|
||||
if (wdt_disable() == 0) {
|
||||
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
ret = 0;
|
||||
} else
|
||||
ret = -ENXIO;
|
||||
} else
|
||||
ret = 0;
|
||||
}
|
||||
if (options & WDIOS_ENABLECARD) {
|
||||
wdt_enable();
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
wdt_enable();
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
ret = put_user(iop_watchdog_timeout(), argp);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iop_wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int state = 1;
|
||||
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
|
||||
if (test_bit(WDT_ENABLED, &wdt_status))
|
||||
state = wdt_disable();
|
||||
|
||||
/* if the timer is not disabled reload and notify that we are still
|
||||
* going down
|
||||
*/
|
||||
if (state != 0) {
|
||||
wdt_enable();
|
||||
pr_crit("Device closed unexpectedly - reset in %lu seconds\n",
|
||||
iop_watchdog_timeout());
|
||||
}
|
||||
|
||||
clear_bit(WDT_IN_USE, &wdt_status);
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations iop_wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = iop_wdt_write,
|
||||
.unlocked_ioctl = iop_wdt_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.open = iop_wdt_open,
|
||||
.release = iop_wdt_release,
|
||||
};
|
||||
|
||||
static struct miscdevice iop_wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &iop_wdt_fops,
|
||||
};
|
||||
|
||||
static int __init iop_wdt_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* check if the reset was caused by the watchdog timer */
|
||||
boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0;
|
||||
|
||||
/* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset
|
||||
* NOTE: An IB Reset will Reset both cores in the IOP342
|
||||
*/
|
||||
write_wdtsr(IOP13XX_WDTCR_IB_RESET);
|
||||
|
||||
/* Register after we have the device set up so we cannot race
|
||||
with an open */
|
||||
ret = misc_register(&iop_wdt_miscdev);
|
||||
if (ret == 0)
|
||||
pr_info("timeout %lu sec\n", iop_watchdog_timeout());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit iop_wdt_exit(void)
|
||||
{
|
||||
misc_deregister(&iop_wdt_miscdev);
|
||||
}
|
||||
|
||||
module_init(iop_wdt_init);
|
||||
module_exit(iop_wdt_exit);
|
||||
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
|
||||
|
||||
MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>");
|
||||
MODULE_DESCRIPTION("iop watchdog timer driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -29,6 +29,16 @@
|
||||
#define GXBB_WDT_TCNT_SETUP_MASK (BIT(16) - 1)
|
||||
#define GXBB_WDT_TCNT_CNT_SHIFT 16
|
||||
|
||||
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 unsigned int timeout;
|
||||
module_param(timeout, uint, 0);
|
||||
MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds="
|
||||
__MODULE_STRING(DEFAULT_TIMEOUT) ")");
|
||||
|
||||
struct meson_gxbb_wdt {
|
||||
void __iomem *reg_base;
|
||||
struct watchdog_device wdt_dev;
|
||||
@ -175,6 +185,8 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
|
||||
data->wdt_dev.max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK;
|
||||
data->wdt_dev.min_timeout = 1;
|
||||
data->wdt_dev.timeout = DEFAULT_TIMEOUT;
|
||||
watchdog_init_timeout(&data->wdt_dev, timeout, dev);
|
||||
watchdog_set_nowayout(&data->wdt_dev, nowayout);
|
||||
watchdog_set_drvdata(&data->wdt_dev, data);
|
||||
|
||||
/* Setup with 1ms timebase */
|
||||
|
@ -100,9 +100,8 @@ static int mlxreg_wdt_ping(struct watchdog_device *wdd)
|
||||
struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
|
||||
|
||||
return regmap_update_bits_base(wdt->regmap, reg_data->reg,
|
||||
~reg_data->mask, BIT(reg_data->bit),
|
||||
NULL, false, true);
|
||||
return regmap_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
|
||||
BIT(reg_data->bit));
|
||||
}
|
||||
|
||||
static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
|
@ -65,6 +65,7 @@ struct mtk_wdt_dev {
|
||||
void __iomem *wdt_base;
|
||||
spinlock_t lock; /* protects WDT_SWSYSRST reg */
|
||||
struct reset_controller_dev rcdev;
|
||||
bool disable_wdt_extrst;
|
||||
};
|
||||
|
||||
struct mtk_wdt_data {
|
||||
@ -256,6 +257,8 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev)
|
||||
reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
|
||||
else
|
||||
reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
|
||||
if (mtk_wdt->disable_wdt_extrst)
|
||||
reg &= ~WDT_MODE_EXRST_EN;
|
||||
reg |= (WDT_MODE_EN | WDT_MODE_KEY);
|
||||
iowrite32(reg, wdt_base + WDT_MODE);
|
||||
|
||||
@ -381,6 +384,10 @@ static int mtk_wdt_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
mtk_wdt->disable_wdt_extrst =
|
||||
of_property_read_bool(dev->of_node, "mediatek,disable-extrst");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,6 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *wdt_mem;
|
||||
struct watchdog_device *wdd;
|
||||
struct rti_wdt_device *wdt;
|
||||
struct clk *clk;
|
||||
@ -246,8 +245,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_nowayout(wdd, 1);
|
||||
watchdog_set_restart_priority(wdd, 128);
|
||||
|
||||
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
wdt->base = devm_ioremap_resource(dev, wdt_mem);
|
||||
wdt->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(wdt->base)) {
|
||||
ret = PTR_ERR(wdt->base);
|
||||
goto err_iomap;
|
||||
|
@ -189,8 +189,8 @@ static int rza_wdt_probe(struct platform_device *pdev)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
priv->wdev.info = &rza_wdt_ident,
|
||||
priv->wdev.ops = &rza_wdt_ops,
|
||||
priv->wdev.info = &rza_wdt_ident;
|
||||
priv->wdev.ops = &rza_wdt_ops;
|
||||
priv->wdev.parent = dev;
|
||||
|
||||
priv->cks = (u8)(uintptr_t) of_device_get_match_data(dev);
|
||||
|
@ -10,6 +10,7 @@
|
||||
* https://www.kernelconcepts.de
|
||||
*
|
||||
* See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide",
|
||||
* AMD Publication 44413 "AMD SP5100 Register Reference Guide"
|
||||
* AMD Publication 45482 "AMD SB800-Series Southbridges Register
|
||||
* Reference Guide"
|
||||
* AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG)
|
||||
@ -144,6 +145,13 @@ static int tco_timer_set_timeout(struct watchdog_device *wdd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int tco_timer_get_timeleft(struct watchdog_device *wdd)
|
||||
{
|
||||
struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
|
||||
|
||||
return readl(SP5100_WDT_COUNT(tco->tcobase));
|
||||
}
|
||||
|
||||
static u8 sp5100_tco_read_pm_reg8(u8 index)
|
||||
{
|
||||
outb(index, SP5100_IO_PM_INDEX_REG);
|
||||
@ -386,6 +394,7 @@ static const struct watchdog_ops sp5100_tco_wdt_ops = {
|
||||
.stop = tco_timer_stop,
|
||||
.ping = tco_timer_ping,
|
||||
.set_timeout = tco_timer_set_timeout,
|
||||
.get_timeleft = tco_timer_get_timeleft,
|
||||
};
|
||||
|
||||
static int sp5100_tco_probe(struct platform_device *pdev)
|
||||
|
@ -237,10 +237,8 @@ static int stm32_iwdg_probe(struct platform_device *pdev)
|
||||
|
||||
/* This is the timer base. */
|
||||
wdt->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(wdt->regs)) {
|
||||
dev_err(dev, "Could not get resource\n");
|
||||
if (IS_ERR(wdt->regs))
|
||||
return PTR_ERR(wdt->regs);
|
||||
}
|
||||
|
||||
ret = stm32_iwdg_clk_init(pdev, wdt);
|
||||
if (ret)
|
||||
|
@ -48,6 +48,7 @@ struct sunxi_wdt_reg {
|
||||
u8 wdt_timeout_shift;
|
||||
u8 wdt_reset_mask;
|
||||
u8 wdt_reset_val;
|
||||
u32 wdt_key_val;
|
||||
};
|
||||
|
||||
struct sunxi_wdt_dev {
|
||||
@ -91,12 +92,14 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev,
|
||||
val = readl(wdt_base + regs->wdt_cfg);
|
||||
val &= ~(regs->wdt_reset_mask);
|
||||
val |= regs->wdt_reset_val;
|
||||
val |= regs->wdt_key_val;
|
||||
writel(val, wdt_base + regs->wdt_cfg);
|
||||
|
||||
/* Set lowest timeout and enable watchdog */
|
||||
val = readl(wdt_base + regs->wdt_mode);
|
||||
val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
|
||||
val |= WDT_MODE_EN;
|
||||
val |= regs->wdt_key_val;
|
||||
writel(val, wdt_base + regs->wdt_mode);
|
||||
|
||||
/*
|
||||
@ -109,6 +112,7 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev,
|
||||
mdelay(5);
|
||||
val = readl(wdt_base + regs->wdt_mode);
|
||||
val |= WDT_MODE_EN;
|
||||
val |= regs->wdt_key_val;
|
||||
writel(val, wdt_base + regs->wdt_mode);
|
||||
}
|
||||
return 0;
|
||||
@ -141,6 +145,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
reg = readl(wdt_base + regs->wdt_mode);
|
||||
reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
|
||||
reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
|
||||
reg |= regs->wdt_key_val;
|
||||
writel(reg, wdt_base + regs->wdt_mode);
|
||||
|
||||
sunxi_wdt_ping(wdt_dev);
|
||||
@ -154,7 +159,7 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev)
|
||||
void __iomem *wdt_base = sunxi_wdt->wdt_base;
|
||||
const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
|
||||
|
||||
writel(0, wdt_base + regs->wdt_mode);
|
||||
writel(regs->wdt_key_val, wdt_base + regs->wdt_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -176,11 +181,13 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
|
||||
reg = readl(wdt_base + regs->wdt_cfg);
|
||||
reg &= ~(regs->wdt_reset_mask);
|
||||
reg |= regs->wdt_reset_val;
|
||||
reg |= regs->wdt_key_val;
|
||||
writel(reg, wdt_base + regs->wdt_cfg);
|
||||
|
||||
/* Enable watchdog */
|
||||
reg = readl(wdt_base + regs->wdt_mode);
|
||||
reg |= WDT_MODE_EN;
|
||||
reg |= regs->wdt_key_val;
|
||||
writel(reg, wdt_base + regs->wdt_mode);
|
||||
|
||||
return 0;
|
||||
@ -220,9 +227,20 @@ static const struct sunxi_wdt_reg sun6i_wdt_reg = {
|
||||
.wdt_reset_val = 0x01,
|
||||
};
|
||||
|
||||
static const struct sunxi_wdt_reg sun20i_wdt_reg = {
|
||||
.wdt_ctrl = 0x10,
|
||||
.wdt_cfg = 0x14,
|
||||
.wdt_mode = 0x18,
|
||||
.wdt_timeout_shift = 4,
|
||||
.wdt_reset_mask = 0x03,
|
||||
.wdt_reset_val = 0x01,
|
||||
.wdt_key_val = 0x16aa0000,
|
||||
};
|
||||
|
||||
static const struct of_device_id sunxi_wdt_dt_ids[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg },
|
||||
{ .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg },
|
||||
{ .compatible = "allwinner,sun20i-d1-wdt", .data = &sun20i_wdt_reg },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
|
||||
|
@ -1,18 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) ST Ericsson SA 2011
|
||||
*
|
||||
* STE Ux500 Watchdog platform data
|
||||
*/
|
||||
#ifndef __UX500_WDT_H
|
||||
#define __UX500_WDT_H
|
||||
|
||||
/**
|
||||
* struct ux500_wdt_data
|
||||
*/
|
||||
struct ux500_wdt_data {
|
||||
unsigned int timeout;
|
||||
bool has_28_bits_resolution;
|
||||
};
|
||||
|
||||
#endif /* __UX500_WDT_H */
|
Loading…
Reference in New Issue
Block a user