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:
Linus Torvalds 2021-11-10 09:41:22 -08:00
commit 89d714ab60
22 changed files with 386 additions and 797 deletions

View File

@ -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:

View File

@ -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>;

View File

@ -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"),
};

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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",

View File

@ -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)

View File

@ -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");

View File

@ -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;
return (superio_inb(wd->sioaddr, SIO_REG_ENABLE) & BIT(0))
&& (wdt_conf & BIT(F71808FG_FLAG_WD_EN));
}
/* 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;
}
/*
* 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 = {
static const struct watchdog_ops fintek_wdt_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = watchdog_open,
.release = watchdog_release,
.write = watchdog_write,
.unlocked_ioctl = watchdog_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.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
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res)
return -ENXIO;
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(watchdog.ident.identity,
sizeof(watchdog.ident.identity), "%s watchdog",
f71808e_names[watchdog.type]);
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;
}
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);
}
return 0;
exit_unlock:
mutex_unlock(&watchdog.lock);
exit_miscdev:
misc_deregister(&watchdog_miscdev);
exit_reboot:
unregister_reboot_notifier(&watchdog_notifier);
dev_err(dev, "cannot start watchdog timer\n");
return err;
}
static int __init f71808e_find(int sioaddr)
set_bit(WDOG_HW_RUNNING, &wdd->status);
dev_info(dev, "watchdog started with initial timeout of %u sec\n",
start_withtimeout);
}
return devm_watchdog_register_device(dev, wdd);
}
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);
}
static void __exit f71808e_exit(void)
return 0;
}
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);

View File

@ -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 */

View File

@ -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");

View File

@ -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 */

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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 Developers 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)

View File

@ -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)

View File

@ -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);

View File

@ -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 */