Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck: "This contains: - addition of the Intel MID watchdog - removal of W83697HF and W83697UG drivers (code was merged into w83627hf_wdt driver) - addition of Armada 375/380 SoC support - conversion of imx2_wdt to regmap API and to watchdog core API - lots of other small improvements and fixes" [ Wim was also tagged by gmail as a spammer, but not delayed by days unlike Ben ] * git://www.linux-watchdog.org/linux-watchdog: (25 commits) x86: intel-mid: add watchdog platform code for Merrifield watchdog: add Intel MID watchdog driver support watchdog: sp805: Set watchdog_device->timeout from ->set_timeout() booke/watchdog: refine and clean up the codes watchdog: iop_wdt only builds for mach-iop13xx watchdog: Remove drivers for W83697HF and W83697UG watchdog: w83627hf_wdt: Add early_disable module parameter ARM: mvebu: Add A375/A380 watchdog binding documentation watchdog: orion: Add Armada 375/380 SoC support watchdog: orion: Introduce per-SoC enabled() function watchdog: orion: Introduce per-SoC stop() function watchdog: orion: Remove unneeded atomic access watchdog: orion: Introduce a SoC-specific RSTOUT mapping watchdog: orion: Move the register ioremap'ing to its own function watchdog: xilinx: Make of_device_id array const watchdog: imx2_wdt: convert to watchdog core api watchdog: imx2_wdt: convert to use regmap API. watchdog: imx2_wdt: Sort the header files alphabetically watchdog: ath79_wdt: switch to clk_prepare/clk_disable watchdog: ath79_wdt: avoid spurious restarts on AR934x ...
This commit is contained in:
Коммит
dfb945473a
|
@ -5,11 +5,18 @@ Required Properties:
|
||||||
- Compatibility : "marvell,orion-wdt"
|
- Compatibility : "marvell,orion-wdt"
|
||||||
"marvell,armada-370-wdt"
|
"marvell,armada-370-wdt"
|
||||||
"marvell,armada-xp-wdt"
|
"marvell,armada-xp-wdt"
|
||||||
|
"marvell,armada-375-wdt"
|
||||||
|
"marvell,armada-380-wdt"
|
||||||
|
|
||||||
- reg : Should contain two entries: first one with the
|
- reg : Should contain two entries: first one with the
|
||||||
timer control address, second one with the
|
timer control address, second one with the
|
||||||
rstout enable address.
|
rstout enable address.
|
||||||
|
|
||||||
|
For "marvell,armada-375-wdt" and "marvell,armada-380-wdt":
|
||||||
|
|
||||||
|
- reg : A third entry is mandatory and should contain the
|
||||||
|
shared mask/unmask RSTOUT address.
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
|
||||||
- interrupts : Contains the IRQ for watchdog expiration
|
- interrupts : Contains the IRQ for watchdog expiration
|
||||||
|
|
|
@ -728,33 +728,6 @@ static int powerpc_debugfs_init(void)
|
||||||
arch_initcall(powerpc_debugfs_init);
|
arch_initcall(powerpc_debugfs_init);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_BOOKE_WDT
|
|
||||||
extern u32 booke_wdt_enabled;
|
|
||||||
extern u32 booke_wdt_period;
|
|
||||||
|
|
||||||
/* Checks wdt=x and wdt_period=xx command-line option */
|
|
||||||
notrace int __init early_parse_wdt(char *p)
|
|
||||||
{
|
|
||||||
if (p && strncmp(p, "0", 1) != 0)
|
|
||||||
booke_wdt_enabled = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
early_param("wdt", early_parse_wdt);
|
|
||||||
|
|
||||||
int __init early_parse_wdt_period(char *p)
|
|
||||||
{
|
|
||||||
unsigned long ret;
|
|
||||||
if (p) {
|
|
||||||
if (!kstrtol(p, 0, &ret))
|
|
||||||
booke_wdt_period = ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
early_param("wdt_period", early_parse_wdt_period);
|
|
||||||
#endif /* CONFIG_BOOKE_WDT */
|
|
||||||
|
|
||||||
void ppc_printk_progress(char *s, unsigned short hex)
|
void ppc_printk_progress(char *s, unsigned short hex)
|
||||||
{
|
{
|
||||||
pr_info("%s\n", s);
|
pr_info("%s\n", s);
|
||||||
|
|
|
@ -20,3 +20,4 @@ obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o
|
||||||
obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o
|
obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o
|
||||||
# MISC Devices
|
# MISC Devices
|
||||||
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
|
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
|
||||||
|
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* platform_wdt.c: Watchdog platform library file
|
||||||
|
*
|
||||||
|
* (C) Copyright 2014 Intel Corporation
|
||||||
|
* Author: David Cohen <david.a.cohen@linux.intel.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; version 2
|
||||||
|
* of the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/platform_data/intel-mid_wdt.h>
|
||||||
|
#include <asm/intel-mid.h>
|
||||||
|
#include <asm/io_apic.h>
|
||||||
|
|
||||||
|
#define TANGIER_EXT_TIMER0_MSI 15
|
||||||
|
|
||||||
|
static struct platform_device wdt_dev = {
|
||||||
|
.name = "intel_mid_wdt",
|
||||||
|
.id = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tangier_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ioapic;
|
||||||
|
int irq;
|
||||||
|
struct intel_mid_wdt_pdata *pdata = pdev->dev.platform_data;
|
||||||
|
struct io_apic_irq_attr irq_attr = { 0 };
|
||||||
|
|
||||||
|
if (!pdata)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
irq = pdata->irq;
|
||||||
|
ioapic = mp_find_ioapic(irq);
|
||||||
|
if (ioapic >= 0) {
|
||||||
|
int ret;
|
||||||
|
irq_attr.ioapic = ioapic;
|
||||||
|
irq_attr.ioapic_pin = irq;
|
||||||
|
irq_attr.trigger = 1;
|
||||||
|
/* irq_attr.polarity = 0; -> Active high */
|
||||||
|
ret = io_apic_set_pci_routing(NULL, irq, &irq_attr);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
dev_warn(&pdev->dev, "cannot find interrupt %d in ioapic\n",
|
||||||
|
irq);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct intel_mid_wdt_pdata tangier_pdata = {
|
||||||
|
.irq = TANGIER_EXT_TIMER0_MSI,
|
||||||
|
.probe = tangier_probe,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init register_mid_wdt(void)
|
||||||
|
{
|
||||||
|
if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER) {
|
||||||
|
wdt_dev.dev.platform_data = &tangier_pdata;
|
||||||
|
return platform_device_register(&wdt_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfs_initcall(register_mid_wdt);
|
|
@ -272,7 +272,7 @@ config PNX4008_WATCHDOG
|
||||||
|
|
||||||
config IOP_WATCHDOG
|
config IOP_WATCHDOG
|
||||||
tristate "IOP Watchdog"
|
tristate "IOP Watchdog"
|
||||||
depends on PLAT_IOP
|
depends on ARCH_IOP13XX
|
||||||
select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X)
|
select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X)
|
||||||
help
|
help
|
||||||
Say Y here if to include support for the watchdog timer
|
Say Y here if to include support for the watchdog timer
|
||||||
|
@ -378,6 +378,8 @@ config MAX63XX_WATCHDOG
|
||||||
config IMX2_WDT
|
config IMX2_WDT
|
||||||
tristate "IMX2+ Watchdog"
|
tristate "IMX2+ Watchdog"
|
||||||
depends on ARCH_MXC
|
depends on ARCH_MXC
|
||||||
|
select REGMAP_MMIO
|
||||||
|
select WATCHDOG_CORE
|
||||||
help
|
help
|
||||||
This is the driver for the hardware watchdog
|
This is the driver for the hardware watchdog
|
||||||
on the Freescale IMX2 and later processors.
|
on the Freescale IMX2 and later processors.
|
||||||
|
@ -663,6 +665,19 @@ config INTEL_SCU_WATCHDOG
|
||||||
|
|
||||||
To compile this driver as a module, choose M here.
|
To compile this driver as a module, choose M here.
|
||||||
|
|
||||||
|
config INTEL_MID_WATCHDOG
|
||||||
|
tristate "Intel MID Watchdog Timer"
|
||||||
|
depends on X86_INTEL_MID
|
||||||
|
select WATCHDOG_CORE
|
||||||
|
---help---
|
||||||
|
Watchdog timer driver built into the Intel SCU for Intel MID
|
||||||
|
Platforms.
|
||||||
|
|
||||||
|
This driver currently supports only the watchdog evolution
|
||||||
|
implementation in SCU, available for Merrifield generation.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here.
|
||||||
|
|
||||||
config ITCO_WDT
|
config ITCO_WDT
|
||||||
tristate "Intel TCO Timer/Watchdog"
|
tristate "Intel TCO Timer/Watchdog"
|
||||||
depends on (X86 || IA64) && PCI
|
depends on (X86 || IA64) && PCI
|
||||||
|
@ -835,7 +850,7 @@ config 60XX_WDT
|
||||||
|
|
||||||
config SBC8360_WDT
|
config SBC8360_WDT
|
||||||
tristate "SBC8360 Watchdog Timer"
|
tristate "SBC8360 Watchdog Timer"
|
||||||
depends on X86
|
depends on X86_32
|
||||||
---help---
|
---help---
|
||||||
|
|
||||||
This is the driver for the hardware watchdog on the SBC8360 Single
|
This is the driver for the hardware watchdog on the SBC8360 Single
|
||||||
|
@ -938,36 +953,6 @@ config W83627HF_WDT
|
||||||
|
|
||||||
Most people will say N.
|
Most people will say N.
|
||||||
|
|
||||||
config W83697HF_WDT
|
|
||||||
tristate "W83697HF/W83697HG Watchdog Timer"
|
|
||||||
depends on X86
|
|
||||||
---help---
|
|
||||||
This is the driver for the hardware watchdog on the W83697HF/HG
|
|
||||||
chipset as used in Dedibox/VIA motherboards (and likely others).
|
|
||||||
This watchdog simply watches your kernel to make sure it doesn't
|
|
||||||
freeze, and if it does, it reboots your computer after a certain
|
|
||||||
amount of time.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called w83697hf_wdt.
|
|
||||||
|
|
||||||
Most people will say N.
|
|
||||||
|
|
||||||
config W83697UG_WDT
|
|
||||||
tristate "W83697UG/W83697UF Watchdog Timer"
|
|
||||||
depends on X86
|
|
||||||
---help---
|
|
||||||
This is the driver for the hardware watchdog on the W83697UG/UF
|
|
||||||
chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others).
|
|
||||||
This watchdog simply watches your kernel to make sure it doesn't
|
|
||||||
freeze, and if it does, it reboots your computer after a certain
|
|
||||||
amount of time.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called w83697ug_wdt.
|
|
||||||
|
|
||||||
Most people will say N.
|
|
||||||
|
|
||||||
config W83877F_WDT
|
config W83877F_WDT
|
||||||
tristate "W83877F (EMACS) Watchdog Timer"
|
tristate "W83877F (EMACS) Watchdog Timer"
|
||||||
depends on X86
|
depends on X86
|
||||||
|
|
|
@ -107,13 +107,12 @@ obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
|
||||||
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
|
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
|
||||||
obj-$(CONFIG_VIA_WDT) += via_wdt.o
|
obj-$(CONFIG_VIA_WDT) += via_wdt.o
|
||||||
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
|
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
|
||||||
obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
|
|
||||||
obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
|
|
||||||
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
|
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
|
||||||
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
|
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
|
||||||
obj-$(CONFIG_MACHZ_WDT) += machzwd.o
|
obj-$(CONFIG_MACHZ_WDT) += machzwd.o
|
||||||
obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
|
obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
|
||||||
obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
|
obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
|
||||||
|
obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
|
||||||
|
|
||||||
# M32R Architecture
|
# M32R Architecture
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
@ -90,6 +91,15 @@ static inline void ath79_wdt_keepalive(void)
|
||||||
static inline void ath79_wdt_enable(void)
|
static inline void ath79_wdt_enable(void)
|
||||||
{
|
{
|
||||||
ath79_wdt_keepalive();
|
ath79_wdt_keepalive();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updating the TIMER register requires a few microseconds
|
||||||
|
* on the AR934x SoCs at least. Use a small delay to ensure
|
||||||
|
* that the TIMER register is updated within the hardware
|
||||||
|
* before enabling the watchdog.
|
||||||
|
*/
|
||||||
|
udelay(2);
|
||||||
|
|
||||||
ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_FCR);
|
ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_FCR);
|
||||||
/* flush write */
|
/* flush write */
|
||||||
ath79_wdt_rr(WDOG_REG_CTRL);
|
ath79_wdt_rr(WDOG_REG_CTRL);
|
||||||
|
@ -255,7 +265,7 @@ static int ath79_wdt_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(wdt_clk))
|
if (IS_ERR(wdt_clk))
|
||||||
return PTR_ERR(wdt_clk);
|
return PTR_ERR(wdt_clk);
|
||||||
|
|
||||||
err = clk_enable(wdt_clk);
|
err = clk_prepare_enable(wdt_clk);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -286,14 +296,14 @@ static int ath79_wdt_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_clk_disable:
|
err_clk_disable:
|
||||||
clk_disable(wdt_clk);
|
clk_disable_unprepare(wdt_clk);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ath79_wdt_remove(struct platform_device *pdev)
|
static int ath79_wdt_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
misc_deregister(&ath79_wdt_miscdev);
|
misc_deregister(&ath79_wdt_miscdev);
|
||||||
clk_disable(wdt_clk);
|
clk_disable_unprepare(wdt_clk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,28 @@ u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT;
|
||||||
#define WDTP_MASK (TCR_WP_MASK)
|
#define WDTP_MASK (TCR_WP_MASK)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Checks wdt=x and wdt_period=xx command-line option */
|
||||||
|
notrace int __init early_parse_wdt(char *p)
|
||||||
|
{
|
||||||
|
if (p && strncmp(p, "0", 1) != 0)
|
||||||
|
booke_wdt_enabled = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
early_param("wdt", early_parse_wdt);
|
||||||
|
|
||||||
|
int __init early_parse_wdt_period(char *p)
|
||||||
|
{
|
||||||
|
unsigned long ret;
|
||||||
|
if (p) {
|
||||||
|
if (!kstrtol(p, 0, &ret))
|
||||||
|
booke_wdt_period = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
early_param("wdt_period", early_parse_wdt_period);
|
||||||
|
|
||||||
#ifdef CONFIG_PPC_FSL_BOOK3E
|
#ifdef CONFIG_PPC_FSL_BOOK3E
|
||||||
|
|
||||||
/* For the specified period, determine the number of seconds
|
/* For the specified period, determine the number of seconds
|
||||||
|
@ -103,17 +125,18 @@ static unsigned int sec_to_period(unsigned int secs)
|
||||||
static void __booke_wdt_set(void *data)
|
static void __booke_wdt_set(void *data)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
struct watchdog_device *wdog = data;
|
||||||
|
|
||||||
val = mfspr(SPRN_TCR);
|
val = mfspr(SPRN_TCR);
|
||||||
val &= ~WDTP_MASK;
|
val &= ~WDTP_MASK;
|
||||||
val |= WDTP(booke_wdt_period);
|
val |= WDTP(sec_to_period(wdog->timeout));
|
||||||
|
|
||||||
mtspr(SPRN_TCR, val);
|
mtspr(SPRN_TCR, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void booke_wdt_set(void)
|
static void booke_wdt_set(void *data)
|
||||||
{
|
{
|
||||||
on_each_cpu(__booke_wdt_set, NULL, 0);
|
on_each_cpu(__booke_wdt_set, data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __booke_wdt_ping(void *data)
|
static void __booke_wdt_ping(void *data)
|
||||||
|
@ -131,12 +154,13 @@ static int booke_wdt_ping(struct watchdog_device *wdog)
|
||||||
static void __booke_wdt_enable(void *data)
|
static void __booke_wdt_enable(void *data)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
struct watchdog_device *wdog = data;
|
||||||
|
|
||||||
/* clear status before enabling watchdog */
|
/* clear status before enabling watchdog */
|
||||||
__booke_wdt_ping(NULL);
|
__booke_wdt_ping(NULL);
|
||||||
val = mfspr(SPRN_TCR);
|
val = mfspr(SPRN_TCR);
|
||||||
val &= ~WDTP_MASK;
|
val &= ~WDTP_MASK;
|
||||||
val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period));
|
val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(sec_to_period(wdog->timeout)));
|
||||||
|
|
||||||
mtspr(SPRN_TCR, val);
|
mtspr(SPRN_TCR, val);
|
||||||
}
|
}
|
||||||
|
@ -162,25 +186,17 @@ static void __booke_wdt_disable(void *data)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __booke_wdt_start(struct watchdog_device *wdog)
|
|
||||||
{
|
|
||||||
on_each_cpu(__booke_wdt_enable, NULL, 0);
|
|
||||||
pr_debug("watchdog enabled (timeout = %u sec)\n", wdog->timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int booke_wdt_start(struct watchdog_device *wdog)
|
static int booke_wdt_start(struct watchdog_device *wdog)
|
||||||
{
|
{
|
||||||
if (booke_wdt_enabled == 0) {
|
on_each_cpu(__booke_wdt_enable, wdog, 0);
|
||||||
booke_wdt_enabled = 1;
|
pr_debug("watchdog enabled (timeout = %u sec)\n", wdog->timeout);
|
||||||
__booke_wdt_start(wdog);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int booke_wdt_stop(struct watchdog_device *wdog)
|
static int booke_wdt_stop(struct watchdog_device *wdog)
|
||||||
{
|
{
|
||||||
on_each_cpu(__booke_wdt_disable, NULL, 0);
|
on_each_cpu(__booke_wdt_disable, NULL, 0);
|
||||||
booke_wdt_enabled = 0;
|
|
||||||
pr_debug("watchdog disabled\n");
|
pr_debug("watchdog disabled\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -191,9 +207,8 @@ static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||||
{
|
{
|
||||||
if (timeout > MAX_WDT_TIMEOUT)
|
if (timeout > MAX_WDT_TIMEOUT)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
booke_wdt_period = sec_to_period(timeout);
|
|
||||||
wdt_dev->timeout = timeout;
|
wdt_dev->timeout = timeout;
|
||||||
booke_wdt_set();
|
booke_wdt_set(wdt_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -231,10 +246,10 @@ static int __init booke_wdt_init(void)
|
||||||
pr_info("powerpc book-e watchdog driver loaded\n");
|
pr_info("powerpc book-e watchdog driver loaded\n");
|
||||||
booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value;
|
booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value;
|
||||||
booke_wdt_set_timeout(&booke_wdt_dev,
|
booke_wdt_set_timeout(&booke_wdt_dev,
|
||||||
period_to_sec(CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT));
|
period_to_sec(booke_wdt_period));
|
||||||
watchdog_set_nowayout(&booke_wdt_dev, nowayout);
|
watchdog_set_nowayout(&booke_wdt_dev, nowayout);
|
||||||
if (booke_wdt_enabled)
|
if (booke_wdt_enabled)
|
||||||
__booke_wdt_start(&booke_wdt_dev);
|
booke_wdt_start(&booke_wdt_dev);
|
||||||
|
|
||||||
ret = watchdog_register_device(&booke_wdt_dev);
|
ret = watchdog_register_device(&booke_wdt_dev);
|
||||||
|
|
||||||
|
|
|
@ -21,19 +21,17 @@
|
||||||
* Halt on suspend: Manual Can be automatic
|
* Halt on suspend: Manual Can be automatic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/watchdog.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/clk.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/watchdog.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "imx2-wdt"
|
#define DRIVER_NAME "imx2-wdt"
|
||||||
|
|
||||||
|
@ -55,19 +53,12 @@
|
||||||
|
|
||||||
#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
|
#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
|
||||||
|
|
||||||
#define IMX2_WDT_STATUS_OPEN 0
|
struct imx2_wdt_device {
|
||||||
#define IMX2_WDT_STATUS_STARTED 1
|
|
||||||
#define IMX2_WDT_EXPECT_CLOSE 2
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
void __iomem *base;
|
struct regmap *regmap;
|
||||||
unsigned timeout;
|
|
||||||
unsigned long status;
|
|
||||||
struct timer_list timer; /* Pings the watchdog when closed */
|
struct timer_list timer; /* Pings the watchdog when closed */
|
||||||
} imx2_wdt;
|
struct watchdog_device wdog;
|
||||||
|
};
|
||||||
static struct miscdevice imx2_wdt_miscdev;
|
|
||||||
|
|
||||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||||
module_param(nowayout, bool, 0);
|
module_param(nowayout, bool, 0);
|
||||||
|
@ -85,9 +76,12 @@ static const struct watchdog_info imx2_wdt_info = {
|
||||||
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
|
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void imx2_wdt_setup(void)
|
static inline void imx2_wdt_setup(struct watchdog_device *wdog)
|
||||||
{
|
{
|
||||||
u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
regmap_read(wdev->regmap, IMX2_WDT_WCR, &val);
|
||||||
|
|
||||||
/* Suspend timer in low power mode, write once-only */
|
/* Suspend timer in low power mode, write once-only */
|
||||||
val |= IMX2_WDT_WCR_WDZST;
|
val |= IMX2_WDT_WCR_WDZST;
|
||||||
|
@ -98,227 +92,199 @@ static inline void imx2_wdt_setup(void)
|
||||||
/* Keep Watchdog Disabled */
|
/* Keep Watchdog Disabled */
|
||||||
val &= ~IMX2_WDT_WCR_WDE;
|
val &= ~IMX2_WDT_WCR_WDE;
|
||||||
/* Set the watchdog's Time-Out value */
|
/* Set the watchdog's Time-Out value */
|
||||||
val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout);
|
val |= WDOG_SEC_TO_COUNT(wdog->timeout);
|
||||||
|
|
||||||
__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
|
regmap_write(wdev->regmap, IMX2_WDT_WCR, val);
|
||||||
|
|
||||||
/* enable the watchdog */
|
/* enable the watchdog */
|
||||||
val |= IMX2_WDT_WCR_WDE;
|
val |= IMX2_WDT_WCR_WDE;
|
||||||
__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
|
regmap_write(wdev->regmap, IMX2_WDT_WCR, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void imx2_wdt_ping(void)
|
static inline bool imx2_wdt_is_running(struct imx2_wdt_device *wdev)
|
||||||
{
|
{
|
||||||
__raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR);
|
u32 val;
|
||||||
__raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR);
|
|
||||||
|
regmap_read(wdev->regmap, IMX2_WDT_WCR, &val);
|
||||||
|
|
||||||
|
return val & IMX2_WDT_WCR_WDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int imx2_wdt_ping(struct watchdog_device *wdog)
|
||||||
|
{
|
||||||
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
|
|
||||||
|
regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1);
|
||||||
|
regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void imx2_wdt_timer_ping(unsigned long arg)
|
static void imx2_wdt_timer_ping(unsigned long arg)
|
||||||
{
|
{
|
||||||
/* ping it every imx2_wdt.timeout / 2 seconds to prevent reboot */
|
struct watchdog_device *wdog = (struct watchdog_device *)arg;
|
||||||
imx2_wdt_ping();
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
mod_timer(&imx2_wdt.timer, jiffies + imx2_wdt.timeout * HZ / 2);
|
|
||||||
|
/* ping it every wdog->timeout / 2 seconds to prevent reboot */
|
||||||
|
imx2_wdt_ping(wdog);
|
||||||
|
mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void imx2_wdt_start(void)
|
static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
|
||||||
|
unsigned int new_timeout)
|
||||||
{
|
{
|
||||||
if (!test_and_set_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
/* at our first start we enable clock and do initialisations */
|
|
||||||
clk_prepare_enable(imx2_wdt.clk);
|
|
||||||
|
|
||||||
imx2_wdt_setup();
|
regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
|
||||||
} else /* delete the timer that pings the watchdog after close */
|
WDOG_SEC_TO_COUNT(new_timeout));
|
||||||
del_timer_sync(&imx2_wdt.timer);
|
|
||||||
|
|
||||||
/* Watchdog is enabled - time to reload the timeout value */
|
|
||||||
imx2_wdt_ping();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void imx2_wdt_stop(void)
|
|
||||||
{
|
|
||||||
/* we don't need a clk_disable, it cannot be disabled once started.
|
|
||||||
* We use a timer to ping the watchdog while /dev/watchdog is closed */
|
|
||||||
imx2_wdt_timer_ping(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void imx2_wdt_set_timeout(int new_timeout)
|
|
||||||
{
|
|
||||||
u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
|
|
||||||
|
|
||||||
/* set the new timeout value in the WSR */
|
|
||||||
val &= ~IMX2_WDT_WCR_WT;
|
|
||||||
val |= WDOG_SEC_TO_COUNT(new_timeout);
|
|
||||||
__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int imx2_wdt_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
imx2_wdt_start();
|
|
||||||
return nonseekable_open(inode, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int imx2_wdt_close(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout)
|
|
||||||
imx2_wdt_stop();
|
|
||||||
else {
|
|
||||||
dev_crit(imx2_wdt_miscdev.parent,
|
|
||||||
"Unexpected close: Expect reboot!\n");
|
|
||||||
imx2_wdt_ping();
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
|
|
||||||
clear_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
|
static int imx2_wdt_start(struct watchdog_device *wdog)
|
||||||
unsigned long arg)
|
|
||||||
{
|
{
|
||||||
void __user *argp = (void __user *)arg;
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
int __user *p = argp;
|
|
||||||
int new_value;
|
|
||||||
u16 val;
|
|
||||||
|
|
||||||
switch (cmd) {
|
if (imx2_wdt_is_running(wdev)) {
|
||||||
case WDIOC_GETSUPPORT:
|
/* delete the timer that pings the watchdog after close */
|
||||||
return copy_to_user(argp, &imx2_wdt_info,
|
del_timer_sync(&wdev->timer);
|
||||||
sizeof(struct watchdog_info)) ? -EFAULT : 0;
|
imx2_wdt_set_timeout(wdog, wdog->timeout);
|
||||||
|
} else
|
||||||
|
imx2_wdt_setup(wdog);
|
||||||
|
|
||||||
case WDIOC_GETSTATUS:
|
return imx2_wdt_ping(wdog);
|
||||||
return put_user(0, p);
|
}
|
||||||
|
|
||||||
case WDIOC_GETBOOTSTATUS:
|
static int imx2_wdt_stop(struct watchdog_device *wdog)
|
||||||
val = __raw_readw(imx2_wdt.base + IMX2_WDT_WRSR);
|
{
|
||||||
new_value = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
|
/*
|
||||||
return put_user(new_value, p);
|
* We don't need a clk_disable, it cannot be disabled once started.
|
||||||
|
* We use a timer to ping the watchdog while /dev/watchdog is closed
|
||||||
|
*/
|
||||||
|
imx2_wdt_timer_ping((unsigned long)wdog);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
case WDIOC_KEEPALIVE:
|
static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog)
|
||||||
imx2_wdt_ping();
|
{
|
||||||
return 0;
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
|
|
||||||
case WDIOC_SETTIMEOUT:
|
if (imx2_wdt_is_running(wdev)) {
|
||||||
if (get_user(new_value, p))
|
imx2_wdt_set_timeout(wdog, wdog->timeout);
|
||||||
return -EFAULT;
|
imx2_wdt_timer_ping((unsigned long)wdog);
|
||||||
if ((new_value < 1) || (new_value > IMX2_WDT_MAX_TIME))
|
|
||||||
return -EINVAL;
|
|
||||||
imx2_wdt_set_timeout(new_value);
|
|
||||||
imx2_wdt.timeout = new_value;
|
|
||||||
imx2_wdt_ping();
|
|
||||||
|
|
||||||
/* Fallthrough to return current value */
|
|
||||||
case WDIOC_GETTIMEOUT:
|
|
||||||
return put_user(imx2_wdt.timeout, p);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -ENOTTY;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
|
static struct watchdog_ops imx2_wdt_ops = {
|
||||||
size_t len, loff_t *ppos)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
if (len == 0) /* Can we see this even ? */
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
|
|
||||||
/* scan to see whether or not we got the magic character */
|
|
||||||
for (i = 0; i != len; i++) {
|
|
||||||
if (get_user(c, data + i))
|
|
||||||
return -EFAULT;
|
|
||||||
if (c == 'V')
|
|
||||||
set_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
imx2_wdt_ping();
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations imx2_wdt_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = no_llseek,
|
.start = imx2_wdt_start,
|
||||||
.unlocked_ioctl = imx2_wdt_ioctl,
|
.stop = imx2_wdt_stop,
|
||||||
.open = imx2_wdt_open,
|
.ping = imx2_wdt_ping,
|
||||||
.release = imx2_wdt_close,
|
.set_timeout = imx2_wdt_set_timeout,
|
||||||
.write = imx2_wdt_write,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct miscdevice imx2_wdt_miscdev = {
|
static struct regmap_config imx2_wdt_regmap_config = {
|
||||||
.minor = WATCHDOG_MINOR,
|
.reg_bits = 16,
|
||||||
.name = "watchdog",
|
.reg_stride = 2,
|
||||||
.fops = &imx2_wdt_fops,
|
.val_bits = 16,
|
||||||
|
.max_register = 0x8,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init imx2_wdt_probe(struct platform_device *pdev)
|
static int __init imx2_wdt_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
struct imx2_wdt_device *wdev;
|
||||||
|
struct watchdog_device *wdog;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
void __iomem *base;
|
||||||
|
int ret;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
|
||||||
|
if (!wdev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
imx2_wdt.base = devm_ioremap_resource(&pdev->dev, res);
|
base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(imx2_wdt.base))
|
if (IS_ERR(base))
|
||||||
return PTR_ERR(imx2_wdt.base);
|
return PTR_ERR(base);
|
||||||
|
|
||||||
imx2_wdt.clk = devm_clk_get(&pdev->dev, NULL);
|
wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
|
||||||
if (IS_ERR(imx2_wdt.clk)) {
|
&imx2_wdt_regmap_config);
|
||||||
dev_err(&pdev->dev, "can't get Watchdog clock\n");
|
if (IS_ERR(wdev->regmap)) {
|
||||||
return PTR_ERR(imx2_wdt.clk);
|
dev_err(&pdev->dev, "regmap init failed\n");
|
||||||
|
return PTR_ERR(wdev->regmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
|
wdev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (imx2_wdt.timeout != timeout)
|
if (IS_ERR(wdev->clk)) {
|
||||||
dev_warn(&pdev->dev, "Initial timeout out of range! "
|
dev_err(&pdev->dev, "can't get Watchdog clock\n");
|
||||||
"Clamped from %u to %u\n", timeout, imx2_wdt.timeout);
|
return PTR_ERR(wdev->clk);
|
||||||
|
}
|
||||||
|
|
||||||
setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
|
wdog = &wdev->wdog;
|
||||||
|
wdog->info = &imx2_wdt_info;
|
||||||
|
wdog->ops = &imx2_wdt_ops;
|
||||||
|
wdog->min_timeout = 1;
|
||||||
|
wdog->max_timeout = IMX2_WDT_MAX_TIME;
|
||||||
|
|
||||||
imx2_wdt_miscdev.parent = &pdev->dev;
|
clk_prepare_enable(wdev->clk);
|
||||||
ret = misc_register(&imx2_wdt_miscdev);
|
|
||||||
if (ret)
|
regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
|
||||||
goto fail;
|
wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
|
||||||
|
|
||||||
|
wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
|
||||||
|
if (wdog->timeout != timeout)
|
||||||
|
dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
|
||||||
|
timeout, wdog->timeout);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, wdog);
|
||||||
|
watchdog_set_drvdata(wdog, wdev);
|
||||||
|
watchdog_set_nowayout(wdog, nowayout);
|
||||||
|
watchdog_init_timeout(wdog, timeout, &pdev->dev);
|
||||||
|
|
||||||
|
setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog);
|
||||||
|
|
||||||
|
imx2_wdt_ping_if_active(wdog);
|
||||||
|
|
||||||
|
ret = watchdog_register_device(wdog);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "cannot register watchdog device\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n",
|
||||||
|
wdog->timeout, nowayout);
|
||||||
|
|
||||||
dev_info(&pdev->dev,
|
|
||||||
"IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n",
|
|
||||||
imx2_wdt.timeout, nowayout);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
|
||||||
imx2_wdt_miscdev.parent = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __exit imx2_wdt_remove(struct platform_device *pdev)
|
static int __exit imx2_wdt_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
misc_deregister(&imx2_wdt_miscdev);
|
struct watchdog_device *wdog = platform_get_drvdata(pdev);
|
||||||
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
|
|
||||||
if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
|
watchdog_unregister_device(wdog);
|
||||||
del_timer_sync(&imx2_wdt.timer);
|
|
||||||
|
|
||||||
dev_crit(imx2_wdt_miscdev.parent,
|
if (imx2_wdt_is_running(wdev)) {
|
||||||
"Device removed: Expect reboot!\n");
|
del_timer_sync(&wdev->timer);
|
||||||
|
imx2_wdt_ping(wdog);
|
||||||
|
dev_crit(&pdev->dev, "Device removed: Expect reboot!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
imx2_wdt_miscdev.parent = NULL;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void imx2_wdt_shutdown(struct platform_device *pdev)
|
static void imx2_wdt_shutdown(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
|
struct watchdog_device *wdog = platform_get_drvdata(pdev);
|
||||||
/* we are running, we need to delete the timer but will give
|
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||||
* max timeout before reboot will take place */
|
|
||||||
del_timer_sync(&imx2_wdt.timer);
|
|
||||||
imx2_wdt_set_timeout(IMX2_WDT_MAX_TIME);
|
|
||||||
imx2_wdt_ping();
|
|
||||||
|
|
||||||
dev_crit(imx2_wdt_miscdev.parent,
|
if (imx2_wdt_is_running(wdev)) {
|
||||||
"Device shutdown: Expect reboot!\n");
|
/*
|
||||||
|
* We are running, we need to delete the timer but will
|
||||||
|
* give max timeout before reboot will take place
|
||||||
|
*/
|
||||||
|
del_timer_sync(&wdev->timer);
|
||||||
|
imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
|
||||||
|
imx2_wdt_ping(wdog);
|
||||||
|
dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
* intel-mid_wdt: generic Intel MID SCU watchdog driver
|
||||||
|
*
|
||||||
|
* Platforms supported so far:
|
||||||
|
* - Merrifield only
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Intel Corporation. All rights reserved.
|
||||||
|
* Contact: David Cohen <david.a.cohen@linux.intel.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General
|
||||||
|
* Public License as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/nmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/watchdog.h>
|
||||||
|
#include <linux/platform_data/intel-mid_wdt.h>
|
||||||
|
|
||||||
|
#include <asm/intel_scu_ipc.h>
|
||||||
|
#include <asm/intel-mid.h>
|
||||||
|
|
||||||
|
#define IPC_WATCHDOG 0xf8
|
||||||
|
|
||||||
|
#define MID_WDT_PRETIMEOUT 15
|
||||||
|
#define MID_WDT_TIMEOUT_MIN (1 + MID_WDT_PRETIMEOUT)
|
||||||
|
#define MID_WDT_TIMEOUT_MAX 170
|
||||||
|
#define MID_WDT_DEFAULT_TIMEOUT 90
|
||||||
|
|
||||||
|
/* SCU watchdog messages */
|
||||||
|
enum {
|
||||||
|
SCU_WATCHDOG_START = 0,
|
||||||
|
SCU_WATCHDOG_STOP,
|
||||||
|
SCU_WATCHDOG_KEEPALIVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int wdt_command(int sub, u32 *in, int inlen)
|
||||||
|
{
|
||||||
|
return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_start(struct watchdog_device *wd)
|
||||||
|
{
|
||||||
|
int ret, in_size;
|
||||||
|
int timeout = wd->timeout;
|
||||||
|
struct ipc_wd_start {
|
||||||
|
u32 pretimeout;
|
||||||
|
u32 timeout;
|
||||||
|
} ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SCU expects the input size for watchdog IPC to
|
||||||
|
* be based on 4 bytes
|
||||||
|
*/
|
||||||
|
in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
|
||||||
|
|
||||||
|
ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size);
|
||||||
|
if (ret) {
|
||||||
|
struct device *dev = watchdog_get_drvdata(wd);
|
||||||
|
dev_crit(dev, "error starting watchdog: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_ping(struct watchdog_device *wd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0);
|
||||||
|
if (ret) {
|
||||||
|
struct device *dev = watchdog_get_drvdata(wd);
|
||||||
|
dev_crit(dev, "Error executing keepalive: 0x%x\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdt_stop(struct watchdog_device *wd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0);
|
||||||
|
if (ret) {
|
||||||
|
struct device *dev = watchdog_get_drvdata(wd);
|
||||||
|
dev_crit(dev, "Error stopping watchdog: 0x%x\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t mid_wdt_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
panic("Kernel Watchdog");
|
||||||
|
|
||||||
|
/* This code should not be reached */
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct watchdog_info mid_wdt_info = {
|
||||||
|
.identity = "Intel MID SCU watchdog",
|
||||||
|
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct watchdog_ops mid_wdt_ops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.start = wdt_start,
|
||||||
|
.stop = wdt_stop,
|
||||||
|
.ping = wdt_ping,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mid_wdt_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct watchdog_device *wdt_dev;
|
||||||
|
struct intel_mid_wdt_pdata *pdata = pdev->dev.platform_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!pdata) {
|
||||||
|
dev_err(&pdev->dev, "missing platform data\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata->probe) {
|
||||||
|
ret = pdata->probe(pdev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
wdt_dev = devm_kzalloc(&pdev->dev, sizeof(*wdt_dev), GFP_KERNEL);
|
||||||
|
if (!wdt_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
wdt_dev->info = &mid_wdt_info;
|
||||||
|
wdt_dev->ops = &mid_wdt_ops;
|
||||||
|
wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
|
||||||
|
wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
|
||||||
|
wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
|
||||||
|
|
||||||
|
watchdog_set_drvdata(wdt_dev, &pdev->dev);
|
||||||
|
platform_set_drvdata(pdev, wdt_dev);
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, pdata->irq, mid_wdt_irq,
|
||||||
|
IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
|
||||||
|
wdt_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "error requesting warning irq %d\n",
|
||||||
|
pdata->irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = watchdog_register_device(wdt_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "error registering watchdog device\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "Intel MID watchdog device probed\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mid_wdt_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct watchdog_device *wd = platform_get_drvdata(pdev);
|
||||||
|
watchdog_unregister_device(wd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver mid_wdt_driver = {
|
||||||
|
.probe = mid_wdt_probe,
|
||||||
|
.remove = mid_wdt_remove,
|
||||||
|
.driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "intel_mid_wdt",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(mid_wdt_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
|
||||||
|
MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -162,7 +162,7 @@ static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data,
|
||||||
kempld_get_mutex(pld);
|
kempld_get_mutex(pld);
|
||||||
stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
|
stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
|
||||||
stage_cfg &= ~STAGE_CFG_PRESCALER_MASK;
|
stage_cfg &= ~STAGE_CFG_PRESCALER_MASK;
|
||||||
stage_cfg |= STAGE_CFG_SET_PRESCALER(prescaler);
|
stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21);
|
||||||
kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
|
kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
|
||||||
kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id),
|
kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id),
|
||||||
stage_timeout);
|
stage_timeout);
|
||||||
|
|
|
@ -225,7 +225,7 @@ static int xwdt_remove(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Match table for of_platform binding */
|
/* Match table for of_platform binding */
|
||||||
static struct of_device_id xwdt_of_match[] = {
|
static const struct of_device_id xwdt_of_match[] = {
|
||||||
{ .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
|
{ .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
|
||||||
{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
|
{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -55,15 +55,19 @@ struct orion_watchdog_data {
|
||||||
int wdt_counter_offset;
|
int wdt_counter_offset;
|
||||||
int wdt_enable_bit;
|
int wdt_enable_bit;
|
||||||
int rstout_enable_bit;
|
int rstout_enable_bit;
|
||||||
|
int rstout_mask_bit;
|
||||||
int (*clock_init)(struct platform_device *,
|
int (*clock_init)(struct platform_device *,
|
||||||
struct orion_watchdog *);
|
struct orion_watchdog *);
|
||||||
|
int (*enabled)(struct orion_watchdog *);
|
||||||
int (*start)(struct watchdog_device *);
|
int (*start)(struct watchdog_device *);
|
||||||
|
int (*stop)(struct watchdog_device *);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct orion_watchdog {
|
struct orion_watchdog {
|
||||||
struct watchdog_device wdt;
|
struct watchdog_device wdt;
|
||||||
void __iomem *reg;
|
void __iomem *reg;
|
||||||
void __iomem *rstout;
|
void __iomem *rstout;
|
||||||
|
void __iomem *rstout_mask;
|
||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
const struct orion_watchdog_data *data;
|
const struct orion_watchdog_data *data;
|
||||||
|
@ -142,9 +146,10 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int armada370_start(struct watchdog_device *wdt_dev)
|
static int armada375_start(struct watchdog_device *wdt_dev)
|
||||||
{
|
{
|
||||||
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
/* Set watchdog duration */
|
/* Set watchdog duration */
|
||||||
writel(dev->clk_rate * wdt_dev->timeout,
|
writel(dev->clk_rate * wdt_dev->timeout,
|
||||||
|
@ -157,8 +162,35 @@ static int armada370_start(struct watchdog_device *wdt_dev)
|
||||||
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
|
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
|
||||||
dev->data->wdt_enable_bit);
|
dev->data->wdt_enable_bit);
|
||||||
|
|
||||||
atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
|
/* Enable reset on watchdog */
|
||||||
dev->data->rstout_enable_bit);
|
reg = readl(dev->rstout);
|
||||||
|
reg |= dev->data->rstout_enable_bit;
|
||||||
|
writel(reg, dev->rstout);
|
||||||
|
|
||||||
|
atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int armada370_start(struct watchdog_device *wdt_dev)
|
||||||
|
{
|
||||||
|
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* Set watchdog duration */
|
||||||
|
writel(dev->clk_rate * wdt_dev->timeout,
|
||||||
|
dev->reg + dev->data->wdt_counter_offset);
|
||||||
|
|
||||||
|
/* Clear the watchdog expiration bit */
|
||||||
|
atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
|
||||||
|
|
||||||
|
/* Enable watchdog timer */
|
||||||
|
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
|
||||||
|
dev->data->wdt_enable_bit);
|
||||||
|
|
||||||
|
/* Enable reset on watchdog */
|
||||||
|
reg = readl(dev->rstout);
|
||||||
|
reg |= dev->data->rstout_enable_bit;
|
||||||
|
writel(reg, dev->rstout);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +221,7 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev)
|
||||||
return dev->data->start(wdt_dev);
|
return dev->data->start(wdt_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int orion_wdt_stop(struct watchdog_device *wdt_dev)
|
static int orion_stop(struct watchdog_device *wdt_dev)
|
||||||
{
|
{
|
||||||
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||||
|
|
||||||
|
@ -202,7 +234,48 @@ static int orion_wdt_stop(struct watchdog_device *wdt_dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int orion_wdt_enabled(struct orion_watchdog *dev)
|
static int armada375_stop(struct watchdog_device *wdt_dev)
|
||||||
|
{
|
||||||
|
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* Disable reset on watchdog */
|
||||||
|
atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit,
|
||||||
|
dev->data->rstout_mask_bit);
|
||||||
|
reg = readl(dev->rstout);
|
||||||
|
reg &= ~dev->data->rstout_enable_bit;
|
||||||
|
writel(reg, dev->rstout);
|
||||||
|
|
||||||
|
/* Disable watchdog timer */
|
||||||
|
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int armada370_stop(struct watchdog_device *wdt_dev)
|
||||||
|
{
|
||||||
|
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* Disable reset on watchdog */
|
||||||
|
reg = readl(dev->rstout);
|
||||||
|
reg &= ~dev->data->rstout_enable_bit;
|
||||||
|
writel(reg, dev->rstout);
|
||||||
|
|
||||||
|
/* Disable watchdog timer */
|
||||||
|
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int orion_wdt_stop(struct watchdog_device *wdt_dev)
|
||||||
|
{
|
||||||
|
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||||
|
|
||||||
|
return dev->data->stop(wdt_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int orion_enabled(struct orion_watchdog *dev)
|
||||||
{
|
{
|
||||||
bool enabled, running;
|
bool enabled, running;
|
||||||
|
|
||||||
|
@ -212,6 +285,24 @@ static int orion_wdt_enabled(struct orion_watchdog *dev)
|
||||||
return enabled && running;
|
return enabled && running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int armada375_enabled(struct orion_watchdog *dev)
|
||||||
|
{
|
||||||
|
bool masked, enabled, running;
|
||||||
|
|
||||||
|
masked = readl(dev->rstout_mask) & dev->data->rstout_mask_bit;
|
||||||
|
enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
|
||||||
|
running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
|
||||||
|
|
||||||
|
return !masked && enabled && running;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int orion_wdt_enabled(struct watchdog_device *wdt_dev)
|
||||||
|
{
|
||||||
|
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||||
|
|
||||||
|
return dev->data->enabled(dev);
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
|
static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
|
||||||
{
|
{
|
||||||
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||||
|
@ -262,10 +353,6 @@ static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev,
|
||||||
return devm_ioremap(&pdev->dev, res->start,
|
return devm_ioremap(&pdev->dev, res->start,
|
||||||
resource_size(res));
|
resource_size(res));
|
||||||
|
|
||||||
/* This workaround works only for "orion-wdt", DT-enabled */
|
|
||||||
if (!of_device_is_compatible(pdev->dev.of_node, "marvell,orion-wdt"))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
|
rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
|
||||||
|
|
||||||
WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout);
|
WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout);
|
||||||
|
@ -277,7 +364,9 @@ static const struct orion_watchdog_data orion_data = {
|
||||||
.wdt_enable_bit = BIT(4),
|
.wdt_enable_bit = BIT(4),
|
||||||
.wdt_counter_offset = 0x24,
|
.wdt_counter_offset = 0x24,
|
||||||
.clock_init = orion_wdt_clock_init,
|
.clock_init = orion_wdt_clock_init,
|
||||||
|
.enabled = orion_enabled,
|
||||||
.start = orion_start,
|
.start = orion_start,
|
||||||
|
.stop = orion_stop,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct orion_watchdog_data armada370_data = {
|
static const struct orion_watchdog_data armada370_data = {
|
||||||
|
@ -285,7 +374,9 @@ static const struct orion_watchdog_data armada370_data = {
|
||||||
.wdt_enable_bit = BIT(8),
|
.wdt_enable_bit = BIT(8),
|
||||||
.wdt_counter_offset = 0x34,
|
.wdt_counter_offset = 0x34,
|
||||||
.clock_init = armada370_wdt_clock_init,
|
.clock_init = armada370_wdt_clock_init,
|
||||||
|
.enabled = orion_enabled,
|
||||||
.start = armada370_start,
|
.start = armada370_start,
|
||||||
|
.stop = armada370_stop,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct orion_watchdog_data armadaxp_data = {
|
static const struct orion_watchdog_data armadaxp_data = {
|
||||||
|
@ -293,7 +384,31 @@ static const struct orion_watchdog_data armadaxp_data = {
|
||||||
.wdt_enable_bit = BIT(8),
|
.wdt_enable_bit = BIT(8),
|
||||||
.wdt_counter_offset = 0x34,
|
.wdt_counter_offset = 0x34,
|
||||||
.clock_init = armadaxp_wdt_clock_init,
|
.clock_init = armadaxp_wdt_clock_init,
|
||||||
|
.enabled = orion_enabled,
|
||||||
.start = armada370_start,
|
.start = armada370_start,
|
||||||
|
.stop = armada370_stop,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct orion_watchdog_data armada375_data = {
|
||||||
|
.rstout_enable_bit = BIT(8),
|
||||||
|
.rstout_mask_bit = BIT(10),
|
||||||
|
.wdt_enable_bit = BIT(8),
|
||||||
|
.wdt_counter_offset = 0x34,
|
||||||
|
.clock_init = armada370_wdt_clock_init,
|
||||||
|
.enabled = armada375_enabled,
|
||||||
|
.start = armada375_start,
|
||||||
|
.stop = armada375_stop,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct orion_watchdog_data armada380_data = {
|
||||||
|
.rstout_enable_bit = BIT(8),
|
||||||
|
.rstout_mask_bit = BIT(10),
|
||||||
|
.wdt_enable_bit = BIT(8),
|
||||||
|
.wdt_counter_offset = 0x34,
|
||||||
|
.clock_init = armadaxp_wdt_clock_init,
|
||||||
|
.enabled = armada375_enabled,
|
||||||
|
.start = armada375_start,
|
||||||
|
.stop = armada375_stop,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id orion_wdt_of_match_table[] = {
|
static const struct of_device_id orion_wdt_of_match_table[] = {
|
||||||
|
@ -309,16 +424,78 @@ static const struct of_device_id orion_wdt_of_match_table[] = {
|
||||||
.compatible = "marvell,armada-xp-wdt",
|
.compatible = "marvell,armada-xp-wdt",
|
||||||
.data = &armadaxp_data,
|
.data = &armadaxp_data,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "marvell,armada-375-wdt",
|
||||||
|
.data = &armada375_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "marvell,armada-380-wdt",
|
||||||
|
.data = &armada380_data,
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
|
MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
|
||||||
|
|
||||||
|
static int orion_wdt_get_regs(struct platform_device *pdev,
|
||||||
|
struct orion_watchdog *dev)
|
||||||
|
{
|
||||||
|
struct device_node *node = pdev->dev.of_node;
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res)
|
||||||
|
return -ENODEV;
|
||||||
|
dev->reg = devm_ioremap(&pdev->dev, res->start,
|
||||||
|
resource_size(res));
|
||||||
|
if (!dev->reg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Each supported compatible has some RSTOUT register quirk */
|
||||||
|
if (of_device_is_compatible(node, "marvell,orion-wdt")) {
|
||||||
|
|
||||||
|
dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
|
||||||
|
INTERNAL_REGS_MASK);
|
||||||
|
if (!dev->rstout)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
} else if (of_device_is_compatible(node, "marvell,armada-370-wdt") ||
|
||||||
|
of_device_is_compatible(node, "marvell,armada-xp-wdt")) {
|
||||||
|
|
||||||
|
/* Dedicated RSTOUT register, can be requested. */
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
dev->rstout = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(dev->rstout))
|
||||||
|
return PTR_ERR(dev->rstout);
|
||||||
|
|
||||||
|
} else if (of_device_is_compatible(node, "marvell,armada-375-wdt") ||
|
||||||
|
of_device_is_compatible(node, "marvell,armada-380-wdt")) {
|
||||||
|
|
||||||
|
/* Dedicated RSTOUT register, can be requested. */
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
dev->rstout = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(dev->rstout))
|
||||||
|
return PTR_ERR(dev->rstout);
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||||
|
if (!res)
|
||||||
|
return -ENODEV;
|
||||||
|
dev->rstout_mask = devm_ioremap(&pdev->dev, res->start,
|
||||||
|
resource_size(res));
|
||||||
|
if (!dev->rstout_mask)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int orion_wdt_probe(struct platform_device *pdev)
|
static int orion_wdt_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct orion_watchdog *dev;
|
struct orion_watchdog *dev;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
unsigned int wdt_max_duration; /* (seconds) */
|
unsigned int wdt_max_duration; /* (seconds) */
|
||||||
struct resource *res;
|
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
|
|
||||||
dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
|
dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
|
||||||
|
@ -336,19 +513,9 @@ static int orion_wdt_probe(struct platform_device *pdev)
|
||||||
dev->wdt.min_timeout = 1;
|
dev->wdt.min_timeout = 1;
|
||||||
dev->data = match->data;
|
dev->data = match->data;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
ret = orion_wdt_get_regs(pdev, dev);
|
||||||
if (!res)
|
if (ret)
|
||||||
return -ENODEV;
|
return ret;
|
||||||
|
|
||||||
dev->reg = devm_ioremap(&pdev->dev, res->start,
|
|
||||||
resource_size(res));
|
|
||||||
if (!dev->reg)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
|
|
||||||
INTERNAL_REGS_MASK);
|
|
||||||
if (!dev->rstout)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
ret = dev->data->clock_init(pdev, dev);
|
ret = dev->data->clock_init(pdev, dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -371,7 +538,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
|
||||||
* removed and re-insterted, or if the bootloader explicitly
|
* removed and re-insterted, or if the bootloader explicitly
|
||||||
* set a running watchdog before booting the kernel.
|
* set a running watchdog before booting the kernel.
|
||||||
*/
|
*/
|
||||||
if (!orion_wdt_enabled(dev))
|
if (!orion_wdt_enabled(&dev->wdt))
|
||||||
orion_wdt_stop(&dev->wdt);
|
orion_wdt_stop(&dev->wdt);
|
||||||
|
|
||||||
/* Request the IRQ only after the watchdog is disabled */
|
/* Request the IRQ only after the watchdog is disabled */
|
||||||
|
|
|
@ -282,8 +282,6 @@ static int sh_wdt_probe(struct platform_device *pdev)
|
||||||
wdt->timer.data = (unsigned long)wdt;
|
wdt->timer.data = (unsigned long)wdt;
|
||||||
wdt->timer.expires = next_ping_period(clock_division_ratio);
|
wdt->timer.expires = next_ping_period(clock_division_ratio);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, wdt);
|
|
||||||
|
|
||||||
dev_info(&pdev->dev, "initialized.\n");
|
dev_info(&pdev->dev, "initialized.\n");
|
||||||
|
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
|
@ -59,7 +59,6 @@
|
||||||
* @adev: amba device structure of wdt
|
* @adev: amba device structure of wdt
|
||||||
* @status: current status of wdt
|
* @status: current status of wdt
|
||||||
* @load_val: load value to be set for current timeout
|
* @load_val: load value to be set for current timeout
|
||||||
* @timeout: current programmed timeout
|
|
||||||
*/
|
*/
|
||||||
struct sp805_wdt {
|
struct sp805_wdt {
|
||||||
struct watchdog_device wdd;
|
struct watchdog_device wdd;
|
||||||
|
@ -68,7 +67,6 @@ struct sp805_wdt {
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct amba_device *adev;
|
struct amba_device *adev;
|
||||||
unsigned int load_val;
|
unsigned int load_val;
|
||||||
unsigned int timeout;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||||
|
@ -98,7 +96,7 @@ static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout)
|
||||||
spin_lock(&wdt->lock);
|
spin_lock(&wdt->lock);
|
||||||
wdt->load_val = load;
|
wdt->load_val = load;
|
||||||
/* roundup timeout to closest positive integer value */
|
/* roundup timeout to closest positive integer value */
|
||||||
wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
|
wdd->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
|
||||||
spin_unlock(&wdt->lock);
|
spin_unlock(&wdt->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -57,17 +57,17 @@ struct sunxi_wdt_dev {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const int wdt_timeout_map[] = {
|
static const int wdt_timeout_map[] = {
|
||||||
[1] = 0b0001, /* 1s */
|
[1] = 0x1, /* 1s */
|
||||||
[2] = 0b0010, /* 2s */
|
[2] = 0x2, /* 2s */
|
||||||
[3] = 0b0011, /* 3s */
|
[3] = 0x3, /* 3s */
|
||||||
[4] = 0b0100, /* 4s */
|
[4] = 0x4, /* 4s */
|
||||||
[5] = 0b0101, /* 5s */
|
[5] = 0x5, /* 5s */
|
||||||
[6] = 0b0110, /* 6s */
|
[6] = 0x6, /* 6s */
|
||||||
[8] = 0b0111, /* 8s */
|
[8] = 0x7, /* 8s */
|
||||||
[10] = 0b1000, /* 10s */
|
[10] = 0x8, /* 10s */
|
||||||
[12] = 0b1001, /* 12s */
|
[12] = 0x9, /* 12s */
|
||||||
[14] = 0b1010, /* 14s */
|
[14] = 0xA, /* 14s */
|
||||||
[16] = 0b1011, /* 16s */
|
[16] = 0xB, /* 16s */
|
||||||
};
|
};
|
||||||
|
|
||||||
static int sunxi_wdt_ping(struct watchdog_device *wdt_dev)
|
static int sunxi_wdt_ping(struct watchdog_device *wdt_dev)
|
||||||
|
|
|
@ -232,7 +232,7 @@ err_out_disable_device:
|
||||||
static void wdt_remove(struct pci_dev *pdev)
|
static void wdt_remove(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
watchdog_unregister_device(&wdt_dev);
|
watchdog_unregister_device(&wdt_dev);
|
||||||
del_timer(&timer);
|
del_timer_sync(&timer);
|
||||||
iounmap(wdt_mem);
|
iounmap(wdt_mem);
|
||||||
release_mem_region(mmio, VIA_WDT_MMIO_LEN);
|
release_mem_region(mmio, VIA_WDT_MMIO_LEN);
|
||||||
release_resource(&wdt_res);
|
release_resource(&wdt_res);
|
||||||
|
|
|
@ -64,6 +64,10 @@ MODULE_PARM_DESC(nowayout,
|
||||||
"Watchdog cannot be stopped once started (default="
|
"Watchdog cannot be stopped once started (default="
|
||||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||||
|
|
||||||
|
static int early_disable;
|
||||||
|
module_param(early_disable, int, 0);
|
||||||
|
MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Kernel methods.
|
* Kernel methods.
|
||||||
*/
|
*/
|
||||||
|
@ -208,9 +212,14 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
|
||||||
|
|
||||||
t = superio_inb(cr_wdt_timeout);
|
t = superio_inb(cr_wdt_timeout);
|
||||||
if (t != 0) {
|
if (t != 0) {
|
||||||
pr_info("Watchdog already running. Resetting timeout to %d sec\n",
|
if (early_disable) {
|
||||||
wdog->timeout);
|
pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
|
||||||
superio_outb(cr_wdt_timeout, wdog->timeout);
|
superio_outb(cr_wdt_timeout, 0);
|
||||||
|
} else {
|
||||||
|
pr_info("Watchdog already running. Resetting timeout to %d sec\n",
|
||||||
|
wdog->timeout);
|
||||||
|
superio_outb(cr_wdt_timeout, wdog->timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set second mode & disable keyboard turning off watchdog */
|
/* set second mode & disable keyboard turning off watchdog */
|
||||||
|
|
|
@ -1,460 +0,0 @@
|
||||||
/*
|
|
||||||
* w83697hf/hg WDT driver
|
|
||||||
*
|
|
||||||
* (c) Copyright 2006 Samuel Tardieu <sam@rfc1149.net>
|
|
||||||
* (c) Copyright 2006 Marcus Junker <junker@anduras.de>
|
|
||||||
*
|
|
||||||
* Based on w83627hf_wdt.c which is based on advantechwdt.c
|
|
||||||
* which is based on wdt.c.
|
|
||||||
* Original copyright messages:
|
|
||||||
*
|
|
||||||
* (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
|
|
||||||
*
|
|
||||||
* (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
|
|
||||||
*
|
|
||||||
* (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
|
|
||||||
* All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version
|
|
||||||
* 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Neither Marcus Junker nor ANDURAS AG admit liability nor provide
|
|
||||||
* warranty for any of this software. This material is provided
|
|
||||||
* "AS-IS" and at no charge.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/moduleparam.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/watchdog.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/notifier.h>
|
|
||||||
#include <linux/reboot.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define WATCHDOG_NAME "w83697hf/hg WDT"
|
|
||||||
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
|
|
||||||
#define WATCHDOG_EARLY_DISABLE 1 /* Disable until userland kicks in */
|
|
||||||
|
|
||||||
static unsigned long wdt_is_open;
|
|
||||||
static char expect_close;
|
|
||||||
static DEFINE_SPINLOCK(io_lock);
|
|
||||||
|
|
||||||
/* You must set this - there is no sane way to probe for this board. */
|
|
||||||
static int wdt_io = 0x2e;
|
|
||||||
module_param(wdt_io, int, 0);
|
|
||||||
MODULE_PARM_DESC(wdt_io,
|
|
||||||
"w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)");
|
|
||||||
|
|
||||||
static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
|
|
||||||
module_param(timeout, int, 0);
|
|
||||||
MODULE_PARM_DESC(timeout,
|
|
||||||
"Watchdog timeout in seconds. 1<= timeout <=255 (default="
|
|
||||||
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
|
|
||||||
|
|
||||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
|
||||||
module_param(nowayout, bool, 0);
|
|
||||||
MODULE_PARM_DESC(nowayout,
|
|
||||||
"Watchdog cannot be stopped once started (default="
|
|
||||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
|
||||||
|
|
||||||
static int early_disable = WATCHDOG_EARLY_DISABLE;
|
|
||||||
module_param(early_disable, int, 0);
|
|
||||||
MODULE_PARM_DESC(early_disable,
|
|
||||||
"Watchdog gets disabled at boot time (default="
|
|
||||||
__MODULE_STRING(WATCHDOG_EARLY_DISABLE) ")");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Kernel methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define W83697HF_EFER (wdt_io + 0) /* Extended Function Enable Register */
|
|
||||||
#define W83697HF_EFIR (wdt_io + 0) /* Extended Function Index Register
|
|
||||||
(same as EFER) */
|
|
||||||
#define W83697HF_EFDR (wdt_io + 1) /* Extended Function Data Register */
|
|
||||||
|
|
||||||
static inline void w83697hf_unlock(void)
|
|
||||||
{
|
|
||||||
outb_p(0x87, W83697HF_EFER); /* Enter extended function mode */
|
|
||||||
outb_p(0x87, W83697HF_EFER); /* Again according to manual */
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void w83697hf_lock(void)
|
|
||||||
{
|
|
||||||
outb_p(0xAA, W83697HF_EFER); /* Leave extended function mode */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The three functions w83697hf_get_reg(), w83697hf_set_reg() and
|
|
||||||
* w83697hf_write_timeout() must be called with the device unlocked.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static unsigned char w83697hf_get_reg(unsigned char reg)
|
|
||||||
{
|
|
||||||
outb_p(reg, W83697HF_EFIR);
|
|
||||||
return inb_p(W83697HF_EFDR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void w83697hf_set_reg(unsigned char reg, unsigned char data)
|
|
||||||
{
|
|
||||||
outb_p(reg, W83697HF_EFIR);
|
|
||||||
outb_p(data, W83697HF_EFDR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void w83697hf_write_timeout(int timeout)
|
|
||||||
{
|
|
||||||
/* Write Timeout counter to CRF4 */
|
|
||||||
w83697hf_set_reg(0xF4, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void w83697hf_select_wdt(void)
|
|
||||||
{
|
|
||||||
w83697hf_unlock();
|
|
||||||
w83697hf_set_reg(0x07, 0x08); /* Switch to logic device 8 (GPIO2) */
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void w83697hf_deselect_wdt(void)
|
|
||||||
{
|
|
||||||
w83697hf_lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void w83697hf_init(void)
|
|
||||||
{
|
|
||||||
unsigned char bbuf;
|
|
||||||
|
|
||||||
w83697hf_select_wdt();
|
|
||||||
|
|
||||||
bbuf = w83697hf_get_reg(0x29);
|
|
||||||
bbuf &= ~0x60;
|
|
||||||
bbuf |= 0x20;
|
|
||||||
|
|
||||||
/* Set pin 119 to WDTO# mode (= CR29, WDT0) */
|
|
||||||
w83697hf_set_reg(0x29, bbuf);
|
|
||||||
|
|
||||||
bbuf = w83697hf_get_reg(0xF3);
|
|
||||||
bbuf &= ~0x04;
|
|
||||||
w83697hf_set_reg(0xF3, bbuf); /* Count mode is seconds */
|
|
||||||
|
|
||||||
w83697hf_deselect_wdt();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wdt_ping(void)
|
|
||||||
{
|
|
||||||
spin_lock(&io_lock);
|
|
||||||
w83697hf_select_wdt();
|
|
||||||
|
|
||||||
w83697hf_write_timeout(timeout);
|
|
||||||
|
|
||||||
w83697hf_deselect_wdt();
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wdt_enable(void)
|
|
||||||
{
|
|
||||||
spin_lock(&io_lock);
|
|
||||||
w83697hf_select_wdt();
|
|
||||||
|
|
||||||
w83697hf_write_timeout(timeout);
|
|
||||||
w83697hf_set_reg(0x30, 1); /* Enable timer */
|
|
||||||
|
|
||||||
w83697hf_deselect_wdt();
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wdt_disable(void)
|
|
||||||
{
|
|
||||||
spin_lock(&io_lock);
|
|
||||||
w83697hf_select_wdt();
|
|
||||||
|
|
||||||
w83697hf_set_reg(0x30, 0); /* Disable timer */
|
|
||||||
w83697hf_write_timeout(0);
|
|
||||||
|
|
||||||
w83697hf_deselect_wdt();
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char wdt_running(void)
|
|
||||||
{
|
|
||||||
unsigned char t;
|
|
||||||
|
|
||||||
spin_lock(&io_lock);
|
|
||||||
w83697hf_select_wdt();
|
|
||||||
|
|
||||||
t = w83697hf_get_reg(0xF4); /* Read timer */
|
|
||||||
|
|
||||||
w83697hf_deselect_wdt();
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wdt_set_heartbeat(int t)
|
|
||||||
{
|
|
||||||
if (t < 1 || t > 255)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
timeout = t;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t wdt_write(struct file *file, const char __user *buf,
|
|
||||||
size_t count, loff_t *ppos)
|
|
||||||
{
|
|
||||||
if (count) {
|
|
||||||
if (!nowayout) {
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
expect_close = 0;
|
|
||||||
|
|
||||||
for (i = 0; i != count; i++) {
|
|
||||||
char c;
|
|
||||||
if (get_user(c, buf + i))
|
|
||||||
return -EFAULT;
|
|
||||||
if (c == 'V')
|
|
||||||
expect_close = 42;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wdt_ping();
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
||||||
{
|
|
||||||
void __user *argp = (void __user *)arg;
|
|
||||||
int __user *p = argp;
|
|
||||||
int new_timeout;
|
|
||||||
static const struct watchdog_info ident = {
|
|
||||||
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
|
|
||||||
| WDIOF_MAGICCLOSE,
|
|
||||||
.firmware_version = 1,
|
|
||||||
.identity = "W83697HF WDT",
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case WDIOC_GETSUPPORT:
|
|
||||||
if (copy_to_user(argp, &ident, sizeof(ident)))
|
|
||||||
return -EFAULT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WDIOC_GETSTATUS:
|
|
||||||
case WDIOC_GETBOOTSTATUS:
|
|
||||||
return put_user(0, p);
|
|
||||||
|
|
||||||
case WDIOC_SETOPTIONS:
|
|
||||||
{
|
|
||||||
int options, retval = -EINVAL;
|
|
||||||
|
|
||||||
if (get_user(options, p))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (options & WDIOS_DISABLECARD) {
|
|
||||||
wdt_disable();
|
|
||||||
retval = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options & WDIOS_ENABLECARD) {
|
|
||||||
wdt_enable();
|
|
||||||
retval = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
case WDIOC_KEEPALIVE:
|
|
||||||
wdt_ping();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WDIOC_SETTIMEOUT:
|
|
||||||
if (get_user(new_timeout, p))
|
|
||||||
return -EFAULT;
|
|
||||||
if (wdt_set_heartbeat(new_timeout))
|
|
||||||
return -EINVAL;
|
|
||||||
wdt_ping();
|
|
||||||
/* Fall */
|
|
||||||
|
|
||||||
case WDIOC_GETTIMEOUT:
|
|
||||||
return put_user(timeout, p);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -ENOTTY;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wdt_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
if (test_and_set_bit(0, &wdt_is_open))
|
|
||||||
return -EBUSY;
|
|
||||||
/*
|
|
||||||
* Activate
|
|
||||||
*/
|
|
||||||
|
|
||||||
wdt_enable();
|
|
||||||
return nonseekable_open(inode, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wdt_close(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
if (expect_close == 42)
|
|
||||||
wdt_disable();
|
|
||||||
else {
|
|
||||||
pr_crit("Unexpected close, not stopping watchdog!\n");
|
|
||||||
wdt_ping();
|
|
||||||
}
|
|
||||||
expect_close = 0;
|
|
||||||
clear_bit(0, &wdt_is_open);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Notifier for system down
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
|
|
||||||
void *unused)
|
|
||||||
{
|
|
||||||
if (code == SYS_DOWN || code == SYS_HALT)
|
|
||||||
wdt_disable(); /* Turn the WDT off */
|
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Kernel Interfaces
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const struct file_operations wdt_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.llseek = no_llseek,
|
|
||||||
.write = wdt_write,
|
|
||||||
.unlocked_ioctl = wdt_ioctl,
|
|
||||||
.open = wdt_open,
|
|
||||||
.release = wdt_close,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct miscdevice wdt_miscdev = {
|
|
||||||
.minor = WATCHDOG_MINOR,
|
|
||||||
.name = "watchdog",
|
|
||||||
.fops = &wdt_fops,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The WDT needs to learn about soft shutdowns in order to
|
|
||||||
* turn the timebomb registers off.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct notifier_block wdt_notifier = {
|
|
||||||
.notifier_call = wdt_notify_sys,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int w83697hf_check_wdt(void)
|
|
||||||
{
|
|
||||||
if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
|
|
||||||
pr_err("I/O address 0x%x already in use\n", wdt_io);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_debug("Looking for watchdog at address 0x%x\n", wdt_io);
|
|
||||||
w83697hf_unlock();
|
|
||||||
if (w83697hf_get_reg(0x20) == 0x60) {
|
|
||||||
pr_info("watchdog found at address 0x%x\n", wdt_io);
|
|
||||||
w83697hf_lock();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* Reprotect in case it was a compatible device */
|
|
||||||
w83697hf_lock();
|
|
||||||
|
|
||||||
pr_info("watchdog not found at address 0x%x\n", wdt_io);
|
|
||||||
release_region(wdt_io, 2);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 };
|
|
||||||
|
|
||||||
static int __init wdt_init(void)
|
|
||||||
{
|
|
||||||
int ret, i, found = 0;
|
|
||||||
|
|
||||||
pr_info("WDT driver for W83697HF/HG initializing\n");
|
|
||||||
|
|
||||||
if (wdt_io == 0) {
|
|
||||||
/* we will autodetect the W83697HF/HG watchdog */
|
|
||||||
for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) {
|
|
||||||
wdt_io = w83697hf_ioports[i];
|
|
||||||
if (!w83697hf_check_wdt())
|
|
||||||
found++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!w83697hf_check_wdt())
|
|
||||||
found++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
pr_err("No W83697HF/HG could be found\n");
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
w83697hf_init();
|
|
||||||
if (early_disable) {
|
|
||||||
if (wdt_running())
|
|
||||||
pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
|
|
||||||
wdt_disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wdt_set_heartbeat(timeout)) {
|
|
||||||
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
|
|
||||||
pr_info("timeout value must be 1 <= timeout <= 255, using %d\n",
|
|
||||||
WATCHDOG_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = register_reboot_notifier(&wdt_notifier);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("cannot register reboot notifier (err=%d)\n", ret);
|
|
||||||
goto unreg_regions;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = misc_register(&wdt_miscdev);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("cannot register miscdev on minor=%d (err=%d)\n",
|
|
||||||
WATCHDOG_MINOR, ret);
|
|
||||||
goto unreg_reboot;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
|
|
||||||
timeout, nowayout);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
unreg_reboot:
|
|
||||||
unregister_reboot_notifier(&wdt_notifier);
|
|
||||||
unreg_regions:
|
|
||||||
release_region(wdt_io, 2);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit wdt_exit(void)
|
|
||||||
{
|
|
||||||
misc_deregister(&wdt_miscdev);
|
|
||||||
unregister_reboot_notifier(&wdt_notifier);
|
|
||||||
release_region(wdt_io, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(wdt_init);
|
|
||||||
module_exit(wdt_exit);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_AUTHOR("Marcus Junker <junker@anduras.de>");
|
|
||||||
MODULE_AUTHOR("Samuel Tardieu <sam@rfc1149.net>");
|
|
||||||
MODULE_DESCRIPTION("w83697hf/hg WDT driver");
|
|
|
@ -1,397 +0,0 @@
|
||||||
/*
|
|
||||||
* w83697ug/uf WDT driver
|
|
||||||
*
|
|
||||||
* (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net>
|
|
||||||
* reused original code to support w83697ug/uf.
|
|
||||||
*
|
|
||||||
* Based on w83627hf_wdt.c which is based on advantechwdt.c
|
|
||||||
* which is based on wdt.c.
|
|
||||||
* Original copyright messages:
|
|
||||||
*
|
|
||||||
* (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
|
|
||||||
* added support for W83627THF.
|
|
||||||
*
|
|
||||||
* (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
|
|
||||||
*
|
|
||||||
* (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
|
|
||||||
*
|
|
||||||
* (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
|
|
||||||
* http://www.redhat.com
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version
|
|
||||||
* 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
|
|
||||||
* warranty for any of this software. This material is provided
|
|
||||||
* "AS-IS" and at no charge.
|
|
||||||
*
|
|
||||||
* (c) Copyright 1995 Alan Cox <alan@redhat.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/moduleparam.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/watchdog.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/notifier.h>
|
|
||||||
#include <linux/reboot.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define WATCHDOG_NAME "w83697ug/uf WDT"
|
|
||||||
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
|
|
||||||
|
|
||||||
static unsigned long wdt_is_open;
|
|
||||||
static char expect_close;
|
|
||||||
static DEFINE_SPINLOCK(io_lock);
|
|
||||||
|
|
||||||
static int wdt_io = 0x2e;
|
|
||||||
module_param(wdt_io, int, 0);
|
|
||||||
MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)");
|
|
||||||
|
|
||||||
static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
|
|
||||||
module_param(timeout, int, 0);
|
|
||||||
MODULE_PARM_DESC(timeout,
|
|
||||||
"Watchdog timeout in seconds. 1<= timeout <=255 (default="
|
|
||||||
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
|
|
||||||
|
|
||||||
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) ")");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Kernel methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
|
|
||||||
#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
|
|
||||||
(same as EFER) */
|
|
||||||
#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
|
|
||||||
|
|
||||||
static int w83697ug_select_wd_register(void)
|
|
||||||
{
|
|
||||||
unsigned char c;
|
|
||||||
unsigned char version;
|
|
||||||
|
|
||||||
outb_p(0x87, WDT_EFER); /* Enter extended function mode */
|
|
||||||
outb_p(0x87, WDT_EFER); /* Again according to manual */
|
|
||||||
|
|
||||||
outb(0x20, WDT_EFER); /* check chip version */
|
|
||||||
version = inb(WDT_EFDR);
|
|
||||||
|
|
||||||
if (version == 0x68) { /* W83697UG */
|
|
||||||
pr_info("Watchdog chip version 0x%02x = W83697UG/UF found at 0x%04x\n",
|
|
||||||
version, wdt_io);
|
|
||||||
|
|
||||||
outb_p(0x2b, WDT_EFER);
|
|
||||||
c = inb_p(WDT_EFDR); /* select WDT0 */
|
|
||||||
c &= ~0x04;
|
|
||||||
outb_p(0x2b, WDT_EFER);
|
|
||||||
outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */
|
|
||||||
|
|
||||||
} else {
|
|
||||||
pr_err("No W83697UG/UF could be found\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
outb_p(0x07, WDT_EFER); /* point to logical device number reg */
|
|
||||||
outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
|
|
||||||
outb_p(0x30, WDT_EFER); /* select CR30 */
|
|
||||||
c = inb_p(WDT_EFDR);
|
|
||||||
outb_p(c | 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void w83697ug_unselect_wd_register(void)
|
|
||||||
{
|
|
||||||
outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int w83697ug_init(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
unsigned char t;
|
|
||||||
|
|
||||||
ret = w83697ug_select_wd_register();
|
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
outb_p(0xF6, WDT_EFER); /* Select CRF6 */
|
|
||||||
t = inb_p(WDT_EFDR); /* read CRF6 */
|
|
||||||
if (t != 0) {
|
|
||||||
pr_info("Watchdog already running. Resetting timeout to %d sec\n",
|
|
||||||
timeout);
|
|
||||||
outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
|
|
||||||
}
|
|
||||||
outb_p(0xF5, WDT_EFER); /* Select CRF5 */
|
|
||||||
t = inb_p(WDT_EFDR); /* read CRF5 */
|
|
||||||
t &= ~0x0C; /* set second mode &
|
|
||||||
disable keyboard turning off watchdog */
|
|
||||||
outb_p(t, WDT_EFDR); /* Write back to CRF5 */
|
|
||||||
|
|
||||||
w83697ug_unselect_wd_register();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wdt_ctrl(int timeout)
|
|
||||||
{
|
|
||||||
spin_lock(&io_lock);
|
|
||||||
|
|
||||||
if (w83697ug_select_wd_register() < 0) {
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
outb_p(0xF4, WDT_EFER); /* Select CRF4 */
|
|
||||||
outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
|
|
||||||
|
|
||||||
w83697ug_unselect_wd_register();
|
|
||||||
|
|
||||||
spin_unlock(&io_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wdt_ping(void)
|
|
||||||
{
|
|
||||||
wdt_ctrl(timeout);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wdt_disable(void)
|
|
||||||
{
|
|
||||||
wdt_ctrl(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wdt_set_heartbeat(int t)
|
|
||||||
{
|
|
||||||
if (t < 1 || t > 255)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
timeout = t;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t wdt_write(struct file *file, const char __user *buf,
|
|
||||||
size_t count, loff_t *ppos)
|
|
||||||
{
|
|
||||||
if (count) {
|
|
||||||
if (!nowayout) {
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
expect_close = 0;
|
|
||||||
|
|
||||||
for (i = 0; i != count; i++) {
|
|
||||||
char c;
|
|
||||||
if (get_user(c, buf + i))
|
|
||||||
return -EFAULT;
|
|
||||||
if (c == 'V')
|
|
||||||
expect_close = 42;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wdt_ping();
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
||||||
{
|
|
||||||
void __user *argp = (void __user *)arg;
|
|
||||||
int __user *p = argp;
|
|
||||||
int new_timeout;
|
|
||||||
static const struct watchdog_info ident = {
|
|
||||||
.options = WDIOF_KEEPALIVEPING |
|
|
||||||
WDIOF_SETTIMEOUT |
|
|
||||||
WDIOF_MAGICCLOSE,
|
|
||||||
.firmware_version = 1,
|
|
||||||
.identity = "W83697UG WDT",
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case WDIOC_GETSUPPORT:
|
|
||||||
if (copy_to_user(argp, &ident, sizeof(ident)))
|
|
||||||
return -EFAULT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WDIOC_GETSTATUS:
|
|
||||||
case WDIOC_GETBOOTSTATUS:
|
|
||||||
return put_user(0, p);
|
|
||||||
|
|
||||||
case WDIOC_SETOPTIONS:
|
|
||||||
{
|
|
||||||
int options, retval = -EINVAL;
|
|
||||||
|
|
||||||
if (get_user(options, p))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (options & WDIOS_DISABLECARD) {
|
|
||||||
wdt_disable();
|
|
||||||
retval = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options & WDIOS_ENABLECARD) {
|
|
||||||
wdt_ping();
|
|
||||||
retval = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
case WDIOC_KEEPALIVE:
|
|
||||||
wdt_ping();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WDIOC_SETTIMEOUT:
|
|
||||||
if (get_user(new_timeout, p))
|
|
||||||
return -EFAULT;
|
|
||||||
if (wdt_set_heartbeat(new_timeout))
|
|
||||||
return -EINVAL;
|
|
||||||
wdt_ping();
|
|
||||||
/* Fall */
|
|
||||||
|
|
||||||
case WDIOC_GETTIMEOUT:
|
|
||||||
return put_user(timeout, p);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -ENOTTY;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wdt_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
if (test_and_set_bit(0, &wdt_is_open))
|
|
||||||
return -EBUSY;
|
|
||||||
/*
|
|
||||||
* Activate
|
|
||||||
*/
|
|
||||||
|
|
||||||
wdt_ping();
|
|
||||||
return nonseekable_open(inode, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wdt_close(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
if (expect_close == 42)
|
|
||||||
wdt_disable();
|
|
||||||
else {
|
|
||||||
pr_crit("Unexpected close, not stopping watchdog!\n");
|
|
||||||
wdt_ping();
|
|
||||||
}
|
|
||||||
expect_close = 0;
|
|
||||||
clear_bit(0, &wdt_is_open);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Notifier for system down
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
|
|
||||||
void *unused)
|
|
||||||
{
|
|
||||||
if (code == SYS_DOWN || code == SYS_HALT)
|
|
||||||
wdt_disable(); /* Turn the WDT off */
|
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Kernel Interfaces
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const struct file_operations wdt_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.llseek = no_llseek,
|
|
||||||
.write = wdt_write,
|
|
||||||
.unlocked_ioctl = wdt_ioctl,
|
|
||||||
.open = wdt_open,
|
|
||||||
.release = wdt_close,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct miscdevice wdt_miscdev = {
|
|
||||||
.minor = WATCHDOG_MINOR,
|
|
||||||
.name = "watchdog",
|
|
||||||
.fops = &wdt_fops,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The WDT needs to learn about soft shutdowns in order to
|
|
||||||
* turn the timebomb registers off.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct notifier_block wdt_notifier = {
|
|
||||||
.notifier_call = wdt_notify_sys,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init wdt_init(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
pr_info("WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising\n");
|
|
||||||
|
|
||||||
if (wdt_set_heartbeat(timeout)) {
|
|
||||||
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
|
|
||||||
pr_info("timeout value must be 1<=timeout<=255, using %d\n",
|
|
||||||
WATCHDOG_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
|
|
||||||
pr_err("I/O address 0x%04x already in use\n", wdt_io);
|
|
||||||
ret = -EIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = w83697ug_init();
|
|
||||||
if (ret != 0)
|
|
||||||
goto unreg_regions;
|
|
||||||
|
|
||||||
ret = register_reboot_notifier(&wdt_notifier);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("cannot register reboot notifier (err=%d)\n", ret);
|
|
||||||
goto unreg_regions;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = misc_register(&wdt_miscdev);
|
|
||||||
if (ret != 0) {
|
|
||||||
pr_err("cannot register miscdev on minor=%d (err=%d)\n",
|
|
||||||
WATCHDOG_MINOR, ret);
|
|
||||||
goto unreg_reboot;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
|
|
||||||
timeout, nowayout);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
unreg_reboot:
|
|
||||||
unregister_reboot_notifier(&wdt_notifier);
|
|
||||||
unreg_regions:
|
|
||||||
release_region(wdt_io, 1);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit wdt_exit(void)
|
|
||||||
{
|
|
||||||
misc_deregister(&wdt_miscdev);
|
|
||||||
unregister_reboot_notifier(&wdt_notifier);
|
|
||||||
release_region(wdt_io, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(wdt_init);
|
|
||||||
module_exit(wdt_exit);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
|
|
||||||
MODULE_DESCRIPTION("w83697ug/uf WDT driver");
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* intel-mid_wdt: generic Intel MID SCU watchdog driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Intel Corporation. All rights reserved.
|
||||||
|
* Contact: David Cohen <david.a.cohen@linux.intel.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General
|
||||||
|
* Public License as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __INTEL_MID_WDT_H__
|
||||||
|
#define __INTEL_MID_WDT_H__
|
||||||
|
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
struct intel_mid_wdt_pdata {
|
||||||
|
int irq;
|
||||||
|
int (*probe)(struct platform_device *pdev);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /*__INTEL_MID_WDT_H__*/
|
Загрузка…
Ссылка в новой задаче