Commit dfb94547 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents c5aec4c7 78a3bb9e
...@@ -5,11 +5,18 @@ Required Properties: ...@@ -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) ...@@ -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 ...@@ -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
/*
* 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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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; ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -162,25 +186,17 @@ static void __booke_wdt_disable(void *data)
} }
static void __booke_wdt_start(struct watchdog_device *wdog) static int booke_wdt_start(struct watchdog_device *wdog)
{ {
on_each_cpu(__booke_wdt_enable, NULL, 0); on_each_cpu(__booke_wdt_enable, wdog, 0);
pr_debug("watchdog enabled (timeout = %u sec)\n", wdog->timeout); pr_debug("watchdog enabled (timeout = %u sec)\n", wdog->timeout);
}
static int booke_wdt_start(struct watchdog_device *wdog)
{
if (booke_wdt_enabled == 0) {
booke_wdt_enabled = 1;
__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, ...@@ -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) ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 = { ...@@ -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) ...@@ -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);
}
static void imx2_wdt_timer_ping(unsigned long arg) regmap_read(wdev->regmap, IMX2_WDT_WCR, &val);
{
/* ping it every imx2_wdt.timeout / 2 seconds to prevent reboot */ return val & IMX2_WDT_WCR_WDE;
imx2_wdt_ping();
mod_timer(&imx2_wdt.timer, jiffies + imx2_wdt.timeout * HZ / 2);
} }
static void imx2_wdt_start(void) static int imx2_wdt_ping(struct watchdog_device *wdog)
{ {
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_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1);
} else /* delete the timer that pings the watchdog after close */ regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2);
del_timer_sync(&imx2_wdt.timer); return 0;
/* Watchdog is enabled - time to reload the timeout value */
imx2_wdt_ping();
} }
static void imx2_wdt_stop(void) static void imx2_wdt_timer_ping(unsigned long arg)
{ {
/* we don't need a clk_disable, it cannot be disabled once started. struct watchdog_device *wdog = (struct watchdog_device *)arg;
* We use a timer to ping the watchdog while /dev/watchdog is closed */ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
imx2_wdt_timer_ping(0);
/* 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_set_timeout(int new_timeout) static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
unsigned int new_timeout)
{ {
u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
/* set the new timeout value in the WSR */ regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
val &= ~IMX2_WDT_WCR_WT; WDOG_SEC_TO_COUNT(new_timeout));
val |= WDOG_SEC_TO_COUNT(new_timeout); return 0;
__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
} }
static int imx2_wdt_open(struct inode *inode, struct file *file) static int imx2_wdt_start(struct watchdog_device *wdog)
{ {
if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status)) struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
return -EBUSY;
if (imx2_wdt_is_running(wdev)) {
/* delete the timer that pings the watchdog after close */
del_timer_sync(&wdev->timer);
imx2_wdt_set_timeout(wdog, wdog->timeout);
} else
imx2_wdt_setup(wdog);
imx2_wdt_start(); return imx2_wdt_ping(wdog);
return nonseekable_open(inode, file);
} }
static int imx2_wdt_close(struct inode *inode, struct file *file) static int imx2_wdt_stop(struct watchdog_device *wdog)
{ {
if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout) /*
imx2_wdt_stop(); * We don't need a clk_disable, it cannot be disabled once started.
else { * We use a timer to ping the watchdog while /dev/watchdog is closed
dev_crit(imx2_wdt_miscdev.parent, */
"Unexpected close: Expect reboot!\n"); imx2_wdt_timer_ping((unsigned long)wdog);
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 inline void imx2_wdt_ping_if_active(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) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &imx2_wdt_info,
sizeof(struct watchdog_info)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
return put_user(0, p);
case WDIOC_GETBOOTSTATUS:
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);
case WDIOC_KEEPALIVE:
imx2_wdt_ping();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_value, p))
return -EFAULT;
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, if (imx2_wdt_is_running(wdev)) {
size_t len, loff_t *ppos) imx2_wdt_set_timeout(wdog, wdog->timeout);
{ imx2_wdt_timer_ping((unsigned long)wdog);
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 = { static struct watchdog_ops imx2_wdt_ops = {
.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);
wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
&imx2_wdt_regmap_config);
if (IS_ERR(wdev->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
return PTR_ERR(wdev->regmap);
}
imx2_wdt.clk = devm_clk_get(&pdev->dev, NULL); wdev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(imx2_wdt.clk)) { if (IS_ERR(wdev->clk)) {
dev_err(&pdev->dev, "can't get Watchdog clock\n"); dev_err(&pdev->dev, "can't get Watchdog clock\n");
return PTR_ERR(imx2_wdt.clk); return PTR_ERR(wdev->clk);
} }
imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME); wdog = &wdev->wdog;
if (imx2_wdt.timeout != timeout) wdog->info = &imx2_wdt_info;
dev_warn(&pdev->dev, "Initial timeout out of range! " wdog->ops = &imx2_wdt_ops;
"Clamped from %u to %u\n", timeout, imx2_wdt.timeout); wdog->min_timeout = 1;
wdog->max_timeout = IMX2_WDT_MAX_TIME;
setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0); clk_prepare_enable(wdev->clk);
imx2_wdt_miscdev.parent = &pdev->dev; regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
ret = misc_register(&imx2_wdt_miscdev); wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
if (ret)
goto fail;
dev_info(&pdev->dev, wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
"IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n", if (wdog->timeout != timeout)
imx2_wdt.timeout, nowayout); dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
return 0; 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);
fail: ret = watchdog_register_device(wdog);
imx2_wdt_miscdev.parent = NULL; if (ret) {
return 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);
return 0;
} }
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); if (imx2_wdt_is_running(wdev)) {
imx2_wdt_set_timeout(IMX2_WDT_MAX_TIME); /*
imx2_wdt_ping(); * We are running, we need to delete the timer but will
* give max timeout before reboot will take place
dev_crit(imx2_wdt_miscdev.parent, */
"Device shutdown: Expect reboot!\n"); 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");
} }
} }
......
/*
* 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, ...@@ -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) ...@@ -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 { ...@@ -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,35 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev) ...@@ -142,9 +146,35 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
return 0; return 0;
} }
static int armada375_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);
atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, 0);
return 0;
}
static int armada370_start(struct watchdog_device *wdt_dev) static int armada370_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 +187,10 @@ static int armada370_start(struct watchdog_device *wdt_dev) ...@@ -157,8 +187,10 @@ 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);
return 0; return 0;
} }
...@@ -189,7 +221,7 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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 = { ...@@ -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 = { ...@@ -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 = { ...@@ -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[] = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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 { ...@@ -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 @@ static int wdt_probe(struct pci_dev *pdev, ...@@ -232,7 +232,7 @@ static int wdt_probe(struct pci_dev *pdev,
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, ...@@ -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) ...@@ -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 */
......
/*
* 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");
/*
* 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");
/*
* 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__*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment