mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck: "This contains: - fixes and improvements - devicetree bindings - conversion to watchdog generic framework of the following drivers: - booke_wdt - bcm47xx_wdt.c - at91sam9_wdt - Removal of old STMP3xxx driver - Addition of following new drivers: - new driver for STMP3xxx and i.MX23/28 - Retu watchdog driver" * git://www.linux-watchdog.org/linux-watchdog: (30 commits) watchdog: sp805_wdt depends on ARM watchdog: davinci_wdt: update to devm_* API watchdog: davinci_wdt: use devm managed clk get watchdog: at91rm9200: add DT support watchdog: add timeout-sec property binding watchdog: at91sam9_wdt: Convert to use the watchdog framework watchdog: omap_wdt: Add option nowayout watchdog: core: dt: add support for the timeout-sec dt property watchdog: bcm47xx_wdt.c: add hard timer watchdog: bcm47xx_wdt.c: rename wdt_time to timeout watchdog: bcm47xx_wdt.c: rename ops methods watchdog: bcm47xx_wdt.c: use platform device watchdog: bcm47xx_wdt.c: convert to watchdog core api watchdog: Convert BookE watchdog driver to watchdog infrastructure watchdog: s3c2410_wdt: Use devm_* functions watchdog: remove old STMP3xxx driver watchdog: add new driver for STMP3xxx and i.MX23/28 rtc: stmp3xxx: add wdt-accessor function watchdog: introduce retu_wdt driver watchdog: intel_scu_watchdog: fix Kconfig dependency ...
This commit is contained in:
commit
68b86a2522
@ -0,0 +1,9 @@
|
||||
Atmel AT91RM9200 System Timer Watchdog
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "atmel,at91sam9260-wdt".
|
||||
|
||||
Example:
|
||||
watchdog@fffffd00 {
|
||||
compatible = "atmel,at91rm9200-wdt";
|
||||
};
|
@ -7,9 +7,13 @@ Required properties:
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec: contains the watchdog timeout in seconds.
|
||||
|
||||
Example:
|
||||
|
||||
watchdog@fffffd40 {
|
||||
compatible = "atmel,at91sam9260-wdt";
|
||||
reg = <0xfffffd40 0x10>;
|
||||
timeout-sec = <10>;
|
||||
};
|
||||
|
@ -5,10 +5,15 @@ Required Properties:
|
||||
- Compatibility : "marvell,orion-wdt"
|
||||
- reg : Address of the timer registers
|
||||
|
||||
Optional properties:
|
||||
|
||||
- timeout-sec : Contains the watchdog timeout in seconds
|
||||
|
||||
Example:
|
||||
|
||||
wdt@20300 {
|
||||
compatible = "marvell,orion-wdt";
|
||||
reg = <0x20300 0x28>;
|
||||
timeout-sec = <10>;
|
||||
status = "okay";
|
||||
};
|
||||
|
@ -5,9 +5,13 @@ Required properties:
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec: contains the watchdog timeout in seconds.
|
||||
|
||||
Example:
|
||||
|
||||
watchdog@4003C000 {
|
||||
compatible = "nxp,pnx4008-wdt";
|
||||
reg = <0x4003C000 0x1000>;
|
||||
timeout-sec = <10>;
|
||||
};
|
||||
|
@ -0,0 +1,13 @@
|
||||
* Qualcomm Atheros AR7130 Watchdog Timer (WDT) Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "qca,ar7130-wdt"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
|
||||
Example:
|
||||
|
||||
wdt@18060008 {
|
||||
compatible = "qca,ar9330-wdt", "qca,ar7130-wdt";
|
||||
reg = <0x18060008 0x8>;
|
||||
};
|
@ -9,3 +9,6 @@ Required properties:
|
||||
- reg : base physical address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts : interrupt number to the cpu.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec : contains the watchdog timeout in seconds.
|
||||
|
@ -1,6 +1,6 @@
|
||||
The Linux WatchDog Timer Driver Core kernel API.
|
||||
===============================================
|
||||
Last reviewed: 22-May-2012
|
||||
Last reviewed: 12-Feb-2013
|
||||
|
||||
Wim Van Sebroeck <wim@iguana.be>
|
||||
|
||||
@ -212,3 +212,15 @@ driver specific data to and a pointer to the data itself.
|
||||
The watchdog_get_drvdata function allows you to retrieve driver specific data.
|
||||
The argument of this function is the watchdog device where you want to retrieve
|
||||
data from. The function returns the pointer to the driver specific data.
|
||||
|
||||
To initialize the timeout field, the following function can be used:
|
||||
|
||||
extern int watchdog_init_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout_parm, struct device *dev);
|
||||
|
||||
The watchdog_init_timeout function allows you to initialize the timeout field
|
||||
using the module timeout parameter or by retrieving the timeout-sec property from
|
||||
the device tree (if the module timeout parameter is invalid). Best practice is
|
||||
to set the default timeout value as timeout value in the watchdog_device and
|
||||
then use this function to set the user "preferred" timeout value.
|
||||
This routine returns zero on success and a negative errno code for failure.
|
||||
|
@ -102,12 +102,15 @@ void __init ath79_register_uart(void)
|
||||
}
|
||||
}
|
||||
|
||||
static struct platform_device ath79_wdt_device = {
|
||||
.name = "ath79-wdt",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
void __init ath79_register_wdt(void)
|
||||
{
|
||||
platform_device_register(&ath79_wdt_device);
|
||||
struct resource res;
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
|
||||
res.flags = IORESOURCE_MEM;
|
||||
res.start = AR71XX_RESET_BASE + AR71XX_RESET_REG_WDOG_CTRL;
|
||||
res.end = res.start + 0x8 - 1;
|
||||
|
||||
platform_device_register_simple("ath79-wdt", -1, &res, 1);
|
||||
}
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/stmp_device.h>
|
||||
#include <linux/stmp3xxx_rtc_wdt.h>
|
||||
|
||||
#include <mach/common.h>
|
||||
|
||||
@ -36,6 +38,7 @@
|
||||
#define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001
|
||||
#define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002
|
||||
#define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004
|
||||
#define STMP3XXX_RTC_CTRL_WATCHDOGEN 0x00000010
|
||||
|
||||
#define STMP3XXX_RTC_STAT 0x10
|
||||
#define STMP3XXX_RTC_STAT_STALE_SHIFT 16
|
||||
@ -45,6 +48,8 @@
|
||||
|
||||
#define STMP3XXX_RTC_ALARM 0x40
|
||||
|
||||
#define STMP3XXX_RTC_WATCHDOG 0x50
|
||||
|
||||
#define STMP3XXX_RTC_PERSISTENT0 0x60
|
||||
#define STMP3XXX_RTC_PERSISTENT0_SET 0x64
|
||||
#define STMP3XXX_RTC_PERSISTENT0_CLR 0x68
|
||||
@ -52,12 +57,70 @@
|
||||
#define STMP3XXX_RTC_PERSISTENT0_ALARM_EN 0x00000004
|
||||
#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE 0x00000080
|
||||
|
||||
#define STMP3XXX_RTC_PERSISTENT1 0x70
|
||||
/* missing bitmask in headers */
|
||||
#define STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER 0x80000000
|
||||
|
||||
struct stmp3xxx_rtc_data {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *io;
|
||||
int irq_alarm;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_STMP3XXX_RTC_WATCHDOG)
|
||||
/**
|
||||
* stmp3xxx_wdt_set_timeout - configure the watchdog inside the STMP3xxx RTC
|
||||
* @dev: the parent device of the watchdog (= the RTC)
|
||||
* @timeout: the desired value for the timeout register of the watchdog.
|
||||
* 0 disables the watchdog
|
||||
*
|
||||
* The watchdog needs one register and two bits which are in the RTC domain.
|
||||
* To handle the resource conflict, the RTC driver will create another
|
||||
* platform_device for the watchdog driver as a child of the RTC device.
|
||||
* The watchdog driver is passed the below accessor function via platform_data
|
||||
* to configure the watchdog. Locking is not needed because accessing SET/CLR
|
||||
* registers is atomic.
|
||||
*/
|
||||
|
||||
static void stmp3xxx_wdt_set_timeout(struct device *dev, u32 timeout)
|
||||
{
|
||||
struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
|
||||
|
||||
if (timeout) {
|
||||
writel(timeout, rtc_data->io + STMP3XXX_RTC_WATCHDOG);
|
||||
writel(STMP3XXX_RTC_CTRL_WATCHDOGEN,
|
||||
rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET);
|
||||
writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER,
|
||||
rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_SET);
|
||||
} else {
|
||||
writel(STMP3XXX_RTC_CTRL_WATCHDOGEN,
|
||||
rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR);
|
||||
writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER,
|
||||
rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
}
|
||||
|
||||
static struct stmp3xxx_wdt_pdata wdt_pdata = {
|
||||
.wdt_set_timeout = stmp3xxx_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev)
|
||||
{
|
||||
struct platform_device *wdt_pdev =
|
||||
platform_device_alloc("stmp3xxx_rtc_wdt", rtc_pdev->id);
|
||||
|
||||
if (wdt_pdev) {
|
||||
wdt_pdev->dev.parent = &rtc_pdev->dev;
|
||||
wdt_pdev->dev.platform_data = &wdt_pdata;
|
||||
platform_device_add(wdt_pdev);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_STMP3XXX_RTC_WATCHDOG */
|
||||
|
||||
static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data)
|
||||
{
|
||||
/*
|
||||
@ -233,6 +296,7 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
|
||||
goto out_irq_alarm;
|
||||
}
|
||||
|
||||
stmp3xxx_wdt_register(pdev);
|
||||
return 0;
|
||||
|
||||
out_irq_alarm:
|
||||
|
@ -79,6 +79,7 @@ config DA9052_WATCHDOG
|
||||
config DA9055_WATCHDOG
|
||||
tristate "Dialog Semiconductor DA9055 Watchdog"
|
||||
depends on MFD_DA9055
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
If you say yes here you get support for watchdog on the Dialog
|
||||
Semiconductor DA9055 PMIC.
|
||||
@ -108,7 +109,7 @@ config WM8350_WATCHDOG
|
||||
|
||||
config ARM_SP805_WATCHDOG
|
||||
tristate "ARM SP805 Watchdog"
|
||||
depends on ARM_AMBA
|
||||
depends on ARM && ARM_AMBA
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
ARM Primecell SP805 Watchdog timer. This will reboot your system when
|
||||
@ -116,7 +117,7 @@ config ARM_SP805_WATCHDOG
|
||||
|
||||
config AT91RM9200_WATCHDOG
|
||||
tristate "AT91RM9200 watchdog"
|
||||
depends on ARCH_AT91RM9200
|
||||
depends on ARCH_AT91
|
||||
help
|
||||
Watchdog timer embedded into AT91RM9200 chips. This will reboot your
|
||||
system when the timeout is reached.
|
||||
@ -124,6 +125,7 @@ config AT91RM9200_WATCHDOG
|
||||
config AT91SAM9X_WATCHDOG
|
||||
tristate "AT91SAM9X / AT91CAP9 watchdog"
|
||||
depends on ARCH_AT91 && !ARCH_AT91RM9200
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will
|
||||
reboot your system when the timeout is reached.
|
||||
@ -316,14 +318,15 @@ config TWL4030_WATCHDOG
|
||||
Support for TI TWL4030 watchdog. Say 'Y' here to enable the
|
||||
watchdog timer support for TWL4030 chips.
|
||||
|
||||
config STMP3XXX_WATCHDOG
|
||||
tristate "Freescale STMP3XXX watchdog"
|
||||
depends on ARCH_STMP3XXX
|
||||
config STMP3XXX_RTC_WATCHDOG
|
||||
tristate "Freescale STMP3XXX & i.MX23/28 watchdog"
|
||||
depends on RTC_DRV_STMP
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here if to include support for the watchdog timer
|
||||
for the Sigmatel STMP37XX/378X SoC.
|
||||
Say Y here to include support for the watchdog timer inside
|
||||
the RTC for the STMP37XX/378X or i.MX23/28 SoC.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stmp3xxx_wdt.
|
||||
module will be called stmp3xxx_rtc_wdt.
|
||||
|
||||
config NUC900_WATCHDOG
|
||||
tristate "Nuvoton NUC900 watchdog"
|
||||
@ -376,6 +379,18 @@ config UX500_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ux500_wdt.
|
||||
|
||||
config RETU_WATCHDOG
|
||||
tristate "Retu watchdog"
|
||||
depends on MFD_RETU
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Retu watchdog driver for Nokia Internet Tablets (770, N800,
|
||||
N810). At least on N800 the watchdog cannot be disabled, so
|
||||
this driver is essential and you should enable it.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called retu_wdt.
|
||||
|
||||
# AVR32 Architecture
|
||||
|
||||
config AT32AP700X_WDT
|
||||
@ -593,7 +608,7 @@ config IE6XX_WDT
|
||||
|
||||
config INTEL_SCU_WATCHDOG
|
||||
bool "Intel SCU Watchdog for Mobile Platforms"
|
||||
depends on X86_MRST
|
||||
depends on X86_INTEL_MID
|
||||
---help---
|
||||
Hardware driver for the watchdog time built into the Intel SCU
|
||||
for Intel Mobile Platforms.
|
||||
@ -983,6 +998,7 @@ config ATH79_WDT
|
||||
config BCM47XX_WDT
|
||||
tristate "Broadcom BCM47xx Watchdog Timer"
|
||||
depends on BCM47XX
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Hardware driver for the Broadcom BCM47xx Watchdog Timer.
|
||||
|
||||
@ -1131,6 +1147,7 @@ config PIKA_WDT
|
||||
config BOOKE_WDT
|
||||
tristate "PowerPC Book-E Watchdog Timer"
|
||||
depends on BOOKE || 4xx
|
||||
select WATCHDOG_CORE
|
||||
---help---
|
||||
Watchdog driver for PowerPC Book-E chips, such as the Freescale
|
||||
MPC85xx SOCs and the IBM PowerPC 440.
|
||||
|
@ -48,11 +48,12 @@ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
|
||||
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
|
||||
obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
|
||||
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
|
||||
obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
|
||||
obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
|
||||
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
|
||||
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
|
||||
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
|
||||
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
|
||||
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
|
||||
|
||||
# AVR32 Architecture
|
||||
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <mach/at91_st.h>
|
||||
|
||||
#define WDT_DEFAULT_TIME 5 /* seconds */
|
||||
@ -252,6 +254,12 @@ static int at91wdt_resume(struct platform_device *pdev)
|
||||
#define at91wdt_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id at91_wdt_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91rm9200-wdt" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids);
|
||||
|
||||
static struct platform_driver at91wdt_driver = {
|
||||
.probe = at91wdt_probe,
|
||||
.remove = at91wdt_remove,
|
||||
@ -261,6 +269,7 @@ static struct platform_driver at91wdt_driver = {
|
||||
.driver = {
|
||||
.name = "at91_wdt",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(at91_wdt_dt_ids),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -18,11 +18,9 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -58,7 +56,7 @@
|
||||
|
||||
/* User land timeout */
|
||||
#define WDT_HEARTBEAT 15
|
||||
static int heartbeat = WDT_HEARTBEAT;
|
||||
static int heartbeat;
|
||||
module_param(heartbeat, int, 0);
|
||||
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
|
||||
"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
|
||||
@ -68,19 +66,17 @@ module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
|
||||
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
static struct watchdog_device at91_wdt_dev;
|
||||
static void at91_ping(unsigned long data);
|
||||
|
||||
static struct {
|
||||
void __iomem *base;
|
||||
unsigned long next_heartbeat; /* the next_heartbeat for the timer */
|
||||
unsigned long open;
|
||||
char expect_close;
|
||||
struct timer_list timer; /* The timer that pings the watchdog */
|
||||
} at91wdt_private;
|
||||
|
||||
/* ......................................................................... */
|
||||
|
||||
|
||||
/*
|
||||
* Reload the watchdog timer. (ie, pat the watchdog)
|
||||
*/
|
||||
@ -95,39 +91,37 @@ static inline void at91_wdt_reset(void)
|
||||
static void at91_ping(unsigned long data)
|
||||
{
|
||||
if (time_before(jiffies, at91wdt_private.next_heartbeat) ||
|
||||
(!nowayout && !at91wdt_private.open)) {
|
||||
(!watchdog_active(&at91_wdt_dev))) {
|
||||
at91_wdt_reset();
|
||||
mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
|
||||
} else
|
||||
pr_crit("I will reset your machine !\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Watchdog device is opened, and watchdog starts running.
|
||||
*/
|
||||
static int at91_wdt_open(struct inode *inode, struct file *file)
|
||||
static int at91_wdt_ping(struct watchdog_device *wdd)
|
||||
{
|
||||
if (test_and_set_bit(0, &at91wdt_private.open))
|
||||
return -EBUSY;
|
||||
|
||||
at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
|
||||
mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
/* calculate when the next userspace timeout will be */
|
||||
at91wdt_private.next_heartbeat = jiffies + wdd->timeout * HZ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the watchdog device.
|
||||
*/
|
||||
static int at91_wdt_close(struct inode *inode, struct file *file)
|
||||
static int at91_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
clear_bit(0, &at91wdt_private.open);
|
||||
/* calculate the next userspace timeout and modify the timer */
|
||||
at91_wdt_ping(wdd);
|
||||
mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stop internal ping */
|
||||
if (!at91wdt_private.expect_close)
|
||||
del_timer(&at91wdt_private.timer);
|
||||
static int at91_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
/* The watchdog timer hardware can not be stopped... */
|
||||
return 0;
|
||||
}
|
||||
|
||||
at91wdt_private.expect_close = 0;
|
||||
static int at91_wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout)
|
||||
{
|
||||
wdd->timeout = new_timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -163,96 +157,28 @@ static int at91_wdt_settimeout(unsigned int timeout)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ......................................................................... */
|
||||
|
||||
static const struct watchdog_info at91_wdt_info = {
|
||||
.identity = DRV_NAME,
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle commands from user-space.
|
||||
*/
|
||||
static long at91_wdt_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
int new_value;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(argp, &at91_wdt_info,
|
||||
sizeof(at91_wdt_info)) ? -EFAULT : 0;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
|
||||
return 0;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(new_value, p))
|
||||
return -EFAULT;
|
||||
|
||||
heartbeat = new_value;
|
||||
at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
|
||||
|
||||
return put_user(new_value, p); /* return current value */
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(heartbeat, p);
|
||||
}
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pat the watchdog whenever device is written to.
|
||||
*/
|
||||
static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
/* Scan for magic character */
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
at91wdt_private.expect_close = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
char c;
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V') {
|
||||
at91wdt_private.expect_close = 42;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* ......................................................................... */
|
||||
|
||||
static const struct file_operations at91wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = at91_wdt_ioctl,
|
||||
.open = at91_wdt_open,
|
||||
.release = at91_wdt_close,
|
||||
.write = at91_wdt_write,
|
||||
static const struct watchdog_ops at91_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = at91_wdt_start,
|
||||
.stop = at91_wdt_stop,
|
||||
.ping = at91_wdt_ping,
|
||||
.set_timeout = at91_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static struct miscdevice at91wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &at91wdt_fops,
|
||||
static struct watchdog_device at91_wdt_dev = {
|
||||
.info = &at91_wdt_info,
|
||||
.ops = &at91_wdt_ops,
|
||||
.timeout = WDT_HEARTBEAT,
|
||||
.min_timeout = 1,
|
||||
.max_timeout = 0xFFFF,
|
||||
};
|
||||
|
||||
static int __init at91wdt_probe(struct platform_device *pdev)
|
||||
@ -260,10 +186,6 @@ static int __init at91wdt_probe(struct platform_device *pdev)
|
||||
struct resource *r;
|
||||
int res;
|
||||
|
||||
if (at91wdt_miscdev.parent)
|
||||
return -EBUSY;
|
||||
at91wdt_miscdev.parent = &pdev->dev;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r)
|
||||
return -ENODEV;
|
||||
@ -273,38 +195,41 @@ static int __init at91wdt_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
at91_wdt_dev.parent = &pdev->dev;
|
||||
watchdog_init_timeout(&at91_wdt_dev, heartbeat, &pdev->dev);
|
||||
watchdog_set_nowayout(&at91_wdt_dev, nowayout);
|
||||
|
||||
/* Set watchdog */
|
||||
res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000));
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
res = misc_register(&at91wdt_miscdev);
|
||||
res = watchdog_register_device(&at91_wdt_dev);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
|
||||
at91wdt_private.next_heartbeat = jiffies + at91_wdt_dev.timeout * HZ;
|
||||
setup_timer(&at91wdt_private.timer, at91_ping, 0);
|
||||
mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
|
||||
|
||||
pr_info("enabled (heartbeat=%d sec, nowayout=%d)\n",
|
||||
heartbeat, nowayout);
|
||||
at91_wdt_dev.timeout, nowayout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit at91wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
int res;
|
||||
watchdog_unregister_device(&at91_wdt_dev);
|
||||
|
||||
res = misc_deregister(&at91wdt_miscdev);
|
||||
if (!res)
|
||||
at91wdt_miscdev.parent = NULL;
|
||||
pr_warn("I quit now, hardware will probably reboot!\n");
|
||||
del_timer(&at91wdt_private.timer);
|
||||
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id at91_wdt_dt_ids[] __initconst = {
|
||||
static const struct of_device_id at91_wdt_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91sam9260-wdt" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
@ -326,4 +251,3 @@ module_platform_driver_probe(at91wdt_driver, at91wdt_probe);
|
||||
MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>");
|
||||
MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
@ -32,14 +33,16 @@
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <asm/mach-ath79/ath79.h>
|
||||
#include <asm/mach-ath79/ar71xx_regs.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#define DRIVER_NAME "ath79-wdt"
|
||||
|
||||
#define WDT_TIMEOUT 15 /* seconds */
|
||||
|
||||
#define WDOG_REG_CTRL 0x00
|
||||
#define WDOG_REG_TIMER 0x04
|
||||
|
||||
#define WDOG_CTRL_LAST_RESET BIT(31)
|
||||
#define WDOG_CTRL_ACTION_MASK 3
|
||||
#define WDOG_CTRL_ACTION_NONE 0 /* no action */
|
||||
@ -66,27 +69,38 @@ static struct clk *wdt_clk;
|
||||
static unsigned long wdt_freq;
|
||||
static int boot_status;
|
||||
static int max_timeout;
|
||||
static void __iomem *wdt_base;
|
||||
|
||||
static inline void ath79_wdt_wr(unsigned reg, u32 val)
|
||||
{
|
||||
iowrite32(val, wdt_base + reg);
|
||||
}
|
||||
|
||||
static inline u32 ath79_wdt_rr(unsigned reg)
|
||||
{
|
||||
return ioread32(wdt_base + reg);
|
||||
}
|
||||
|
||||
static inline void ath79_wdt_keepalive(void)
|
||||
{
|
||||
ath79_reset_wr(AR71XX_RESET_REG_WDOG, wdt_freq * timeout);
|
||||
ath79_wdt_wr(WDOG_REG_TIMER, wdt_freq * timeout);
|
||||
/* flush write */
|
||||
ath79_reset_rr(AR71XX_RESET_REG_WDOG);
|
||||
ath79_wdt_rr(WDOG_REG_TIMER);
|
||||
}
|
||||
|
||||
static inline void ath79_wdt_enable(void)
|
||||
{
|
||||
ath79_wdt_keepalive();
|
||||
ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_FCR);
|
||||
ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_FCR);
|
||||
/* flush write */
|
||||
ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
|
||||
ath79_wdt_rr(WDOG_REG_CTRL);
|
||||
}
|
||||
|
||||
static inline void ath79_wdt_disable(void)
|
||||
{
|
||||
ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_NONE);
|
||||
ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_NONE);
|
||||
/* flush write */
|
||||
ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
|
||||
ath79_wdt_rr(WDOG_REG_CTRL);
|
||||
}
|
||||
|
||||
static int ath79_wdt_set_timeout(int val)
|
||||
@ -226,16 +240,32 @@ static struct miscdevice ath79_wdt_miscdev = {
|
||||
|
||||
static int ath79_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
u32 ctrl;
|
||||
int err;
|
||||
|
||||
wdt_clk = clk_get(&pdev->dev, "wdt");
|
||||
if (wdt_base)
|
||||
return -EBUSY;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no memory resource found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wdt_base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!wdt_base) {
|
||||
dev_err(&pdev->dev, "unable to remap memory region\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wdt_clk = devm_clk_get(&pdev->dev, "wdt");
|
||||
if (IS_ERR(wdt_clk))
|
||||
return PTR_ERR(wdt_clk);
|
||||
|
||||
err = clk_enable(wdt_clk);
|
||||
if (err)
|
||||
goto err_clk_put;
|
||||
return err;
|
||||
|
||||
wdt_freq = clk_get_rate(wdt_clk);
|
||||
if (!wdt_freq) {
|
||||
@ -251,7 +281,7 @@ static int ath79_wdt_probe(struct platform_device *pdev)
|
||||
max_timeout, timeout);
|
||||
}
|
||||
|
||||
ctrl = ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
|
||||
ctrl = ath79_wdt_rr(WDOG_REG_CTRL);
|
||||
boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
|
||||
|
||||
err = misc_register(&ath79_wdt_miscdev);
|
||||
@ -265,8 +295,6 @@ static int ath79_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable(wdt_clk);
|
||||
err_clk_put:
|
||||
clk_put(wdt_clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -274,7 +302,6 @@ static int ath79_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&ath79_wdt_miscdev);
|
||||
clk_disable(wdt_clk);
|
||||
clk_put(wdt_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -283,6 +310,14 @@ static void ath97_wdt_shutdown(struct platform_device *pdev)
|
||||
ath79_wdt_disable();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ath79_wdt_match[] = {
|
||||
{ .compatible = "qca,ar7130-wdt" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ath79_wdt_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver ath79_wdt_driver = {
|
||||
.probe = ath79_wdt_probe,
|
||||
.remove = ath79_wdt_remove,
|
||||
@ -290,6 +325,7 @@ static struct platform_driver ath79_wdt_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(ath79_wdt_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>
|
||||
* Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
|
||||
* Copyright (C) 2012-2013 Hauke Mehrtens <hauke@hauke-m.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -12,167 +13,145 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bcm47xx_wdt.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/ssb/ssb_embedded.h>
|
||||
#include <asm/mach-bcm47xx/bcm47xx.h>
|
||||
|
||||
#define DRV_NAME "bcm47xx_wdt"
|
||||
|
||||
#define WDT_DEFAULT_TIME 30 /* seconds */
|
||||
#define WDT_MAX_TIME 255 /* seconds */
|
||||
#define WDT_SOFTTIMER_MAX 255 /* seconds */
|
||||
#define WDT_SOFTTIMER_THRESHOLD 60 /* seconds */
|
||||
|
||||
static int wdt_time = WDT_DEFAULT_TIME;
|
||||
static int timeout = WDT_DEFAULT_TIME;
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
|
||||
module_param(wdt_time, int, 0);
|
||||
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
|
||||
module_param(timeout, int, 0);
|
||||
MODULE_PARM_DESC(timeout, "Watchdog time in seconds. (default="
|
||||
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
|
||||
|
||||
#ifdef CONFIG_WATCHDOG_NOWAYOUT
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout,
|
||||
"Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
#endif
|
||||
|
||||
static unsigned long bcm47xx_wdt_busy;
|
||||
static char expect_release;
|
||||
static struct timer_list wdt_timer;
|
||||
static atomic_t ticks;
|
||||
|
||||
static inline void bcm47xx_wdt_hw_start(void)
|
||||
static inline struct bcm47xx_wdt *bcm47xx_wdt_get(struct watchdog_device *wdd)
|
||||
{
|
||||
/* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */
|
||||
switch (bcm47xx_bus_type) {
|
||||
#ifdef CONFIG_BCM47XX_SSB
|
||||
case BCM47XX_BUS_TYPE_SSB:
|
||||
ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0xfffffff);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_BCM47XX_BCMA
|
||||
case BCM47XX_BUS_TYPE_BCMA:
|
||||
bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc,
|
||||
0xfffffff);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return container_of(wdd, struct bcm47xx_wdt, wdd);
|
||||
}
|
||||
|
||||
static inline int bcm47xx_wdt_hw_stop(void)
|
||||
static int bcm47xx_wdt_hard_keepalive(struct watchdog_device *wdd)
|
||||
{
|
||||
switch (bcm47xx_bus_type) {
|
||||
#ifdef CONFIG_BCM47XX_SSB
|
||||
case BCM47XX_BUS_TYPE_SSB:
|
||||
return ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0);
|
||||
#endif
|
||||
#ifdef CONFIG_BCM47XX_BCMA
|
||||
case BCM47XX_BUS_TYPE_BCMA:
|
||||
bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 0);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
return -EINVAL;
|
||||
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
|
||||
|
||||
wdt->timer_set_ms(wdt, wdd->timeout * 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm47xx_timer_tick(unsigned long unused)
|
||||
static int bcm47xx_wdt_hard_start(struct watchdog_device *wdd)
|
||||
{
|
||||
if (!atomic_dec_and_test(&ticks)) {
|
||||
bcm47xx_wdt_hw_start();
|
||||
mod_timer(&wdt_timer, jiffies + HZ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm47xx_wdt_hard_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
|
||||
|
||||
wdt->timer_set(wdt, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int new_time)
|
||||
{
|
||||
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
|
||||
u32 max_timer = wdt->max_timer_ms;
|
||||
|
||||
if (new_time < 1 || new_time > max_timer / 1000) {
|
||||
pr_warn("timeout value must be 1<=x<=%d, using %d\n",
|
||||
max_timer / 1000, new_time);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wdd->timeout = new_time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct watchdog_ops bcm47xx_wdt_hard_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = bcm47xx_wdt_hard_start,
|
||||
.stop = bcm47xx_wdt_hard_stop,
|
||||
.ping = bcm47xx_wdt_hard_keepalive,
|
||||
.set_timeout = bcm47xx_wdt_hard_set_timeout,
|
||||
};
|
||||
|
||||
static void bcm47xx_wdt_soft_timer_tick(unsigned long data)
|
||||
{
|
||||
struct bcm47xx_wdt *wdt = (struct bcm47xx_wdt *)data;
|
||||
u32 next_tick = min(wdt->wdd.timeout * 1000, wdt->max_timer_ms);
|
||||
|
||||
if (!atomic_dec_and_test(&wdt->soft_ticks)) {
|
||||
wdt->timer_set_ms(wdt, next_tick);
|
||||
mod_timer(&wdt->soft_timer, jiffies + HZ);
|
||||
} else {
|
||||
pr_crit("Watchdog will fire soon!!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void bcm47xx_wdt_pet(void)
|
||||
static int bcm47xx_wdt_soft_keepalive(struct watchdog_device *wdd)
|
||||
{
|
||||
atomic_set(&ticks, wdt_time);
|
||||
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
|
||||
|
||||
atomic_set(&wdt->soft_ticks, wdd->timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm47xx_wdt_start(void)
|
||||
static int bcm47xx_wdt_soft_start(struct watchdog_device *wdd)
|
||||
{
|
||||
bcm47xx_wdt_pet();
|
||||
bcm47xx_timer_tick(0);
|
||||
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
|
||||
|
||||
bcm47xx_wdt_soft_keepalive(wdd);
|
||||
bcm47xx_wdt_soft_timer_tick((unsigned long)wdt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm47xx_wdt_pause(void)
|
||||
static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
del_timer_sync(&wdt_timer);
|
||||
bcm47xx_wdt_hw_stop();
|
||||
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
|
||||
|
||||
del_timer_sync(&wdt->soft_timer);
|
||||
wdt->timer_set(wdt, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm47xx_wdt_stop(void)
|
||||
static int bcm47xx_wdt_soft_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int new_time)
|
||||
{
|
||||
bcm47xx_wdt_pause();
|
||||
}
|
||||
|
||||
static int bcm47xx_wdt_settimeout(int new_time)
|
||||
{
|
||||
if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
|
||||
if (new_time < 1 || new_time > WDT_SOFTTIMER_MAX) {
|
||||
pr_warn("timeout value must be 1<=x<=%d, using %d\n",
|
||||
WDT_SOFTTIMER_MAX, new_time);
|
||||
return -EINVAL;
|
||||
|
||||
wdt_time = new_time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm47xx_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(0, &bcm47xx_wdt_busy))
|
||||
return -EBUSY;
|
||||
|
||||
bcm47xx_wdt_start();
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static int bcm47xx_wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (expect_release == 42) {
|
||||
bcm47xx_wdt_stop();
|
||||
} else {
|
||||
pr_crit("Unexpected close, not stopping watchdog!\n");
|
||||
bcm47xx_wdt_start();
|
||||
}
|
||||
|
||||
clear_bit(0, &bcm47xx_wdt_busy);
|
||||
expect_release = 0;
|
||||
wdd->timeout = new_time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
if (len) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
expect_release = 0;
|
||||
|
||||
for (i = 0; i != len; i++) {
|
||||
char c;
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
expect_release = 42;
|
||||
}
|
||||
}
|
||||
bcm47xx_wdt_pet();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct watchdog_info bcm47xx_wdt_info = {
|
||||
.identity = DRV_NAME,
|
||||
.options = WDIOF_SETTIMEOUT |
|
||||
@ -180,130 +159,100 @@ static const struct watchdog_info bcm47xx_wdt_info = {
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static long bcm47xx_wdt_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
int new_value, retval = -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(argp, &bcm47xx_wdt_info,
|
||||
sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(new_value, p))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_value & WDIOS_DISABLECARD) {
|
||||
bcm47xx_wdt_stop();
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
if (new_value & WDIOS_ENABLECARD) {
|
||||
bcm47xx_wdt_start();
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
bcm47xx_wdt_pet();
|
||||
return 0;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(new_value, p))
|
||||
return -EFAULT;
|
||||
|
||||
if (bcm47xx_wdt_settimeout(new_value))
|
||||
return -EINVAL;
|
||||
|
||||
bcm47xx_wdt_pet();
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(wdt_time, p);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
|
||||
unsigned long code, void *unused)
|
||||
unsigned long code, void *unused)
|
||||
{
|
||||
struct bcm47xx_wdt *wdt;
|
||||
|
||||
wdt = container_of(this, struct bcm47xx_wdt, notifier);
|
||||
if (code == SYS_DOWN || code == SYS_HALT)
|
||||
bcm47xx_wdt_stop();
|
||||
wdt->wdd.ops->stop(&wdt->wdd);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static const struct file_operations bcm47xx_wdt_fops = {
|
||||
static struct watchdog_ops bcm47xx_wdt_soft_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = bcm47xx_wdt_ioctl,
|
||||
.open = bcm47xx_wdt_open,
|
||||
.release = bcm47xx_wdt_release,
|
||||
.write = bcm47xx_wdt_write,
|
||||
.start = bcm47xx_wdt_soft_start,
|
||||
.stop = bcm47xx_wdt_soft_stop,
|
||||
.ping = bcm47xx_wdt_soft_keepalive,
|
||||
.set_timeout = bcm47xx_wdt_soft_set_timeout,
|
||||
};
|
||||
|
||||
static struct miscdevice bcm47xx_wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &bcm47xx_wdt_fops,
|
||||
};
|
||||
|
||||
static struct notifier_block bcm47xx_wdt_notifier = {
|
||||
.notifier_call = bcm47xx_wdt_notify_sys,
|
||||
};
|
||||
|
||||
static int __init bcm47xx_wdt_init(void)
|
||||
static int bcm47xx_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
bool soft;
|
||||
struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (bcm47xx_wdt_hw_stop() < 0)
|
||||
return -ENODEV;
|
||||
if (!wdt)
|
||||
return -ENXIO;
|
||||
|
||||
setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
|
||||
soft = wdt->max_timer_ms < WDT_SOFTTIMER_THRESHOLD * 1000;
|
||||
|
||||
if (bcm47xx_wdt_settimeout(wdt_time)) {
|
||||
bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
|
||||
pr_info("wdt_time value must be 0 < wdt_time < %d, using %d\n",
|
||||
(WDT_MAX_TIME + 1), wdt_time);
|
||||
if (soft) {
|
||||
wdt->wdd.ops = &bcm47xx_wdt_soft_ops;
|
||||
setup_timer(&wdt->soft_timer, bcm47xx_wdt_soft_timer_tick,
|
||||
(long unsigned int)wdt);
|
||||
} else {
|
||||
wdt->wdd.ops = &bcm47xx_wdt_hard_ops;
|
||||
}
|
||||
|
||||
ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
|
||||
wdt->wdd.info = &bcm47xx_wdt_info;
|
||||
wdt->wdd.timeout = WDT_DEFAULT_TIME;
|
||||
ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_timer;
|
||||
watchdog_set_nowayout(&wdt->wdd, nowayout);
|
||||
|
||||
ret = misc_register(&bcm47xx_wdt_miscdev);
|
||||
if (ret) {
|
||||
unregister_reboot_notifier(&bcm47xx_wdt_notifier);
|
||||
return ret;
|
||||
}
|
||||
wdt->notifier.notifier_call = &bcm47xx_wdt_notify_sys;
|
||||
|
||||
ret = register_reboot_notifier(&wdt->notifier);
|
||||
if (ret)
|
||||
goto err_timer;
|
||||
|
||||
ret = watchdog_register_device(&wdt->wdd);
|
||||
if (ret)
|
||||
goto err_notifier;
|
||||
|
||||
dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n",
|
||||
timeout, nowayout ? ", nowayout" : "",
|
||||
soft ? ", Software Timer" : "");
|
||||
return 0;
|
||||
|
||||
err_notifier:
|
||||
unregister_reboot_notifier(&wdt->notifier);
|
||||
err_timer:
|
||||
if (soft)
|
||||
del_timer_sync(&wdt->soft_timer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm47xx_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!wdt)
|
||||
return -ENXIO;
|
||||
|
||||
watchdog_unregister_device(&wdt->wdd);
|
||||
unregister_reboot_notifier(&wdt->notifier);
|
||||
|
||||
pr_info("BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
|
||||
wdt_time, nowayout ? ", nowayout" : "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit bcm47xx_wdt_exit(void)
|
||||
{
|
||||
if (!nowayout)
|
||||
bcm47xx_wdt_stop();
|
||||
static struct platform_driver bcm47xx_wdt_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "bcm47xx-wdt",
|
||||
},
|
||||
.probe = bcm47xx_wdt_probe,
|
||||
.remove = bcm47xx_wdt_remove,
|
||||
};
|
||||
|
||||
misc_deregister(&bcm47xx_wdt_miscdev);
|
||||
|
||||
unregister_reboot_notifier(&bcm47xx_wdt_notifier);
|
||||
}
|
||||
|
||||
module_init(bcm47xx_wdt_init);
|
||||
module_exit(bcm47xx_wdt_exit);
|
||||
module_platform_driver(bcm47xx_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Aleksandar Radovanovic");
|
||||
MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
|
||||
MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
||||
|
@ -15,12 +15,8 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/reg_booke.h>
|
||||
#include <asm/time.h>
|
||||
@ -45,7 +41,7 @@ u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT;
|
||||
#define WDTP_MASK (TCR_WP_MASK)
|
||||
#endif
|
||||
|
||||
static DEFINE_SPINLOCK(booke_wdt_lock);
|
||||
#ifdef CONFIG_PPC_FSL_BOOK3E
|
||||
|
||||
/* For the specified period, determine the number of seconds
|
||||
* corresponding to the reset time. There will be a watchdog
|
||||
@ -86,6 +82,24 @@ static unsigned int sec_to_period(unsigned int secs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_WDT_TIMEOUT period_to_sec(1)
|
||||
|
||||
#else /* CONFIG_PPC_FSL_BOOK3E */
|
||||
|
||||
static unsigned long long period_to_sec(unsigned int period)
|
||||
{
|
||||
return period;
|
||||
}
|
||||
|
||||
static unsigned int sec_to_period(unsigned int secs)
|
||||
{
|
||||
return secs;
|
||||
}
|
||||
|
||||
#define MAX_WDT_TIMEOUT 3 /* from Kconfig */
|
||||
|
||||
#endif /* !CONFIG_PPC_FSL_BOOK3E */
|
||||
|
||||
static void __booke_wdt_set(void *data)
|
||||
{
|
||||
u32 val;
|
||||
@ -107,9 +121,11 @@ static void __booke_wdt_ping(void *data)
|
||||
mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
|
||||
}
|
||||
|
||||
static void booke_wdt_ping(void)
|
||||
static int booke_wdt_ping(struct watchdog_device *wdog)
|
||||
{
|
||||
on_each_cpu(__booke_wdt_ping, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __booke_wdt_enable(void *data)
|
||||
@ -146,152 +162,81 @@ static void __booke_wdt_disable(void *data)
|
||||
|
||||
}
|
||||
|
||||
static ssize_t booke_wdt_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
static void __booke_wdt_start(struct watchdog_device *wdog)
|
||||
{
|
||||
booke_wdt_ping();
|
||||
return count;
|
||||
on_each_cpu(__booke_wdt_enable, NULL, 0);
|
||||
pr_debug("watchdog enabled (timeout = %u sec)\n", wdog->timeout);
|
||||
}
|
||||
|
||||
static struct watchdog_info ident = {
|
||||
static int booke_wdt_start(struct watchdog_device *wdog)
|
||||
{
|
||||
if (booke_wdt_enabled == 0) {
|
||||
booke_wdt_enabled = 1;
|
||||
__booke_wdt_start(wdog);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int booke_wdt_stop(struct watchdog_device *wdog)
|
||||
{
|
||||
on_each_cpu(__booke_wdt_disable, NULL, 0);
|
||||
booke_wdt_enabled = 0;
|
||||
pr_debug("watchdog disabled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
if (timeout > MAX_WDT_TIMEOUT)
|
||||
return -EINVAL;
|
||||
booke_wdt_period = sec_to_period(timeout);
|
||||
wdt_dev->timeout = timeout;
|
||||
booke_wdt_set();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct watchdog_info booke_wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
|
||||
.identity = "PowerPC Book-E Watchdog",
|
||||
};
|
||||
|
||||
static long booke_wdt_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
u32 tmp = 0;
|
||||
u32 __user *p = (u32 __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(p, &ident, sizeof(ident)) ? -EFAULT : 0;
|
||||
case WDIOC_GETSTATUS:
|
||||
return put_user(0, p);
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
/* XXX: something is clearing TSR */
|
||||
tmp = mfspr(SPRN_TSR) & TSR_WRS(3);
|
||||
/* returns CARDRESET if last reset was caused by the WDT */
|
||||
return put_user((tmp ? WDIOF_CARDRESET : 0), p);
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(tmp, p))
|
||||
return -EFAULT;
|
||||
if (tmp == WDIOS_ENABLECARD) {
|
||||
booke_wdt_ping();
|
||||
break;
|
||||
} else
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
case WDIOC_KEEPALIVE:
|
||||
booke_wdt_ping();
|
||||
return 0;
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(tmp, p))
|
||||
return -EFAULT;
|
||||
#ifdef CONFIG_PPC_FSL_BOOK3E
|
||||
/* period of 1 gives the largest possible timeout */
|
||||
if (tmp > period_to_sec(1))
|
||||
return -EINVAL;
|
||||
booke_wdt_period = sec_to_period(tmp);
|
||||
#else
|
||||
booke_wdt_period = tmp;
|
||||
#endif
|
||||
booke_wdt_set();
|
||||
/* Fall */
|
||||
case WDIOC_GETTIMEOUT:
|
||||
#ifdef CONFIG_FSL_BOOKE
|
||||
return put_user(period_to_sec(booke_wdt_period), p);
|
||||
#else
|
||||
return put_user(booke_wdt_period, p);
|
||||
#endif
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wdt_is_active stores whether or not the /dev/watchdog device is opened */
|
||||
static unsigned long wdt_is_active;
|
||||
|
||||
static int booke_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* /dev/watchdog can only be opened once */
|
||||
if (test_and_set_bit(0, &wdt_is_active))
|
||||
return -EBUSY;
|
||||
|
||||
spin_lock(&booke_wdt_lock);
|
||||
if (booke_wdt_enabled == 0) {
|
||||
booke_wdt_enabled = 1;
|
||||
on_each_cpu(__booke_wdt_enable, NULL, 0);
|
||||
pr_debug("watchdog enabled (timeout = %llu sec)\n",
|
||||
period_to_sec(booke_wdt_period));
|
||||
}
|
||||
spin_unlock(&booke_wdt_lock);
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static int booke_wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
#ifndef CONFIG_WATCHDOG_NOWAYOUT
|
||||
/* Normally, the watchdog is disabled when /dev/watchdog is closed, but
|
||||
* if CONFIG_WATCHDOG_NOWAYOUT is defined, then it means that the
|
||||
* watchdog should remain enabled. So we disable it only if
|
||||
* CONFIG_WATCHDOG_NOWAYOUT is not defined.
|
||||
*/
|
||||
on_each_cpu(__booke_wdt_disable, NULL, 0);
|
||||
booke_wdt_enabled = 0;
|
||||
pr_debug("watchdog disabled\n");
|
||||
#endif
|
||||
|
||||
clear_bit(0, &wdt_is_active);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations booke_wdt_fops = {
|
||||
static struct watchdog_ops booke_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = booke_wdt_write,
|
||||
.unlocked_ioctl = booke_wdt_ioctl,
|
||||
.open = booke_wdt_open,
|
||||
.release = booke_wdt_release,
|
||||
.start = booke_wdt_start,
|
||||
.stop = booke_wdt_stop,
|
||||
.ping = booke_wdt_ping,
|
||||
.set_timeout = booke_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static struct miscdevice booke_wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &booke_wdt_fops,
|
||||
static struct watchdog_device booke_wdt_dev = {
|
||||
.info = &booke_wdt_info,
|
||||
.ops = &booke_wdt_ops,
|
||||
.min_timeout = 1,
|
||||
.max_timeout = 0xFFFF
|
||||
};
|
||||
|
||||
static void __exit booke_wdt_exit(void)
|
||||
{
|
||||
misc_deregister(&booke_wdt_miscdev);
|
||||
watchdog_unregister_device(&booke_wdt_dev);
|
||||
}
|
||||
|
||||
static int __init booke_wdt_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
|
||||
pr_info("powerpc book-e watchdog driver loaded\n");
|
||||
ident.firmware_version = cur_cpu_spec->pvr_value;
|
||||
booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value;
|
||||
booke_wdt_set_timeout(&booke_wdt_dev,
|
||||
period_to_sec(CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT));
|
||||
watchdog_set_nowayout(&booke_wdt_dev, nowayout);
|
||||
if (booke_wdt_enabled)
|
||||
__booke_wdt_start(&booke_wdt_dev);
|
||||
|
||||
ret = misc_register(&booke_wdt_miscdev);
|
||||
if (ret) {
|
||||
pr_err("cannot register device (minor=%u, ret=%i)\n",
|
||||
WATCHDOG_MINOR, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock(&booke_wdt_lock);
|
||||
if (booke_wdt_enabled == 1) {
|
||||
pr_info("watchdog enabled (timeout = %llu sec)\n",
|
||||
period_to_sec(booke_wdt_period));
|
||||
on_each_cpu(__booke_wdt_enable, NULL, 0);
|
||||
}
|
||||
spin_unlock(&booke_wdt_lock);
|
||||
ret = watchdog_register_device(&booke_wdt_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -69,7 +69,6 @@ static unsigned long wdt_status;
|
||||
#define WDT_REGION_INITED 2
|
||||
#define WDT_DEVICE_INITED 3
|
||||
|
||||
static struct resource *wdt_mem;
|
||||
static void __iomem *wdt_base;
|
||||
struct clk *wdt_clk;
|
||||
|
||||
@ -201,10 +200,11 @@ static struct miscdevice davinci_wdt_miscdev = {
|
||||
|
||||
static int davinci_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0, size;
|
||||
int ret = 0;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *wdt_mem;
|
||||
|
||||
wdt_clk = clk_get(dev, NULL);
|
||||
wdt_clk = devm_clk_get(dev, NULL);
|
||||
if (WARN_ON(IS_ERR(wdt_clk)))
|
||||
return PTR_ERR(wdt_clk);
|
||||
|
||||
@ -221,43 +221,26 @@ static int davinci_wdt_probe(struct platform_device *pdev)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
size = resource_size(wdt_mem);
|
||||
if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
|
||||
dev_err(dev, "failed to get memory region\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
wdt_base = ioremap(wdt_mem->start, size);
|
||||
wdt_base = devm_request_and_ioremap(dev, wdt_mem);
|
||||
if (!wdt_base) {
|
||||
dev_err(dev, "failed to map memory region\n");
|
||||
release_mem_region(wdt_mem->start, size);
|
||||
wdt_mem = NULL;
|
||||
return -ENOMEM;
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
ret = misc_register(&davinci_wdt_miscdev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "cannot register misc device\n");
|
||||
release_mem_region(wdt_mem->start, size);
|
||||
wdt_mem = NULL;
|
||||
} else {
|
||||
set_bit(WDT_DEVICE_INITED, &wdt_status);
|
||||
}
|
||||
|
||||
iounmap(wdt_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int davinci_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&davinci_wdt_miscdev);
|
||||
if (wdt_mem) {
|
||||
release_mem_region(wdt_mem->start, resource_size(wdt_mem));
|
||||
wdt_mem = NULL;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(wdt_clk);
|
||||
clk_put(wdt_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -310,6 +310,7 @@ static struct platform_driver gef_wdt_driver = {
|
||||
.of_match_table = gef_wdt_ids,
|
||||
},
|
||||
.probe = gef_wdt_probe,
|
||||
.remove = gef_wdt_remove,
|
||||
};
|
||||
|
||||
static int __init gef_wdt_init(void)
|
||||
|
@ -45,6 +45,11 @@
|
||||
|
||||
#include "omap_wdt.h"
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
|
||||
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
static unsigned timer_margin;
|
||||
module_param(timer_margin, uint, 0);
|
||||
MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
|
||||
@ -201,7 +206,6 @@ static const struct watchdog_ops omap_wdt_ops = {
|
||||
static int omap_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_wd_timer_platform_data *pdata = pdev->dev.platform_data;
|
||||
bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
struct watchdog_device *omap_wdt;
|
||||
struct resource *res, *mem;
|
||||
struct omap_wdt_dev *wdev;
|
||||
|
@ -140,6 +140,7 @@ static const struct watchdog_ops orion_wdt_ops = {
|
||||
static struct watchdog_device orion_wdt = {
|
||||
.info = &orion_wdt_info,
|
||||
.ops = &orion_wdt_ops,
|
||||
.min_timeout = 1,
|
||||
};
|
||||
|
||||
static int orion_wdt_probe(struct platform_device *pdev)
|
||||
@ -164,12 +165,9 @@ static int orion_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
|
||||
|
||||
if ((heartbeat < 1) || (heartbeat > wdt_max_duration))
|
||||
heartbeat = wdt_max_duration;
|
||||
|
||||
orion_wdt.timeout = heartbeat;
|
||||
orion_wdt.min_timeout = 1;
|
||||
orion_wdt.timeout = wdt_max_duration;
|
||||
orion_wdt.max_timeout = wdt_max_duration;
|
||||
watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev);
|
||||
|
||||
watchdog_set_nowayout(&orion_wdt, nowayout);
|
||||
ret = watchdog_register_device(&orion_wdt);
|
||||
@ -179,7 +177,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
pr_info("Initial timeout %d sec%s\n",
|
||||
heartbeat, nowayout ? ", nowayout" : "");
|
||||
orion_wdt.timeout, nowayout ? ", nowayout" : "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -225,4 +223,5 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:orion_wdt");
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
||||
|
@ -142,6 +142,7 @@ static const struct watchdog_ops pnx4008_wdt_ops = {
|
||||
static struct watchdog_device pnx4008_wdd = {
|
||||
.info = &pnx4008_wdt_ident,
|
||||
.ops = &pnx4008_wdt_ops,
|
||||
.timeout = DEFAULT_HEARTBEAT,
|
||||
.min_timeout = 1,
|
||||
.max_timeout = MAX_HEARTBEAT,
|
||||
};
|
||||
@ -151,8 +152,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
|
||||
struct resource *r;
|
||||
int ret = 0;
|
||||
|
||||
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
|
||||
heartbeat = DEFAULT_HEARTBEAT;
|
||||
watchdog_init_timeout(&pnx4008_wdd, heartbeat, &pdev->dev);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
wdt_base = devm_ioremap_resource(&pdev->dev, r);
|
||||
@ -167,7 +167,6 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
pnx4008_wdd.timeout = heartbeat;
|
||||
pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
|
||||
WDIOF_CARDRESET : 0;
|
||||
watchdog_set_nowayout(&pnx4008_wdd, nowayout);
|
||||
@ -181,7 +180,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "PNX4008 Watchdog Timer: heartbeat %d sec\n",
|
||||
heartbeat);
|
||||
pnx4008_wdd.timeout);
|
||||
|
||||
return 0;
|
||||
|
||||
|
178
drivers/watchdog/retu_wdt.c
Normal file
178
drivers/watchdog/retu_wdt.c
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Retu watchdog driver
|
||||
*
|
||||
* Copyright (C) 2004, 2005 Nokia Corporation
|
||||
*
|
||||
* Based on code written by Amit Kucheria and Michael Buesch.
|
||||
* Rewritten by Aaro Koskinen.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/retu.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* Watchdog timer values in seconds */
|
||||
#define RETU_WDT_MAX_TIMER 63
|
||||
|
||||
struct retu_wdt_dev {
|
||||
struct retu_dev *rdev;
|
||||
struct device *dev;
|
||||
struct delayed_work ping_work;
|
||||
};
|
||||
|
||||
/*
|
||||
* Since Retu watchdog cannot be disabled in hardware, we must kick it
|
||||
* with a timer until userspace watchdog software takes over. If
|
||||
* CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding.
|
||||
*/
|
||||
static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
|
||||
{
|
||||
retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
|
||||
schedule_delayed_work(&wdev->ping_work,
|
||||
round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2));
|
||||
}
|
||||
|
||||
static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
|
||||
{
|
||||
retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
|
||||
cancel_delayed_work_sync(&wdev->ping_work);
|
||||
}
|
||||
|
||||
static void retu_wdt_ping_work(struct work_struct *work)
|
||||
{
|
||||
struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
|
||||
struct retu_wdt_dev, ping_work);
|
||||
retu_wdt_ping_enable(wdev);
|
||||
}
|
||||
|
||||
static int retu_wdt_start(struct watchdog_device *wdog)
|
||||
{
|
||||
struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
||||
|
||||
retu_wdt_ping_disable(wdev);
|
||||
|
||||
return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
|
||||
}
|
||||
|
||||
static int retu_wdt_stop(struct watchdog_device *wdog)
|
||||
{
|
||||
struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
||||
|
||||
retu_wdt_ping_enable(wdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int retu_wdt_ping(struct watchdog_device *wdog)
|
||||
{
|
||||
struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
||||
|
||||
return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
|
||||
}
|
||||
|
||||
static int retu_wdt_set_timeout(struct watchdog_device *wdog,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
||||
|
||||
wdog->timeout = timeout;
|
||||
return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
|
||||
}
|
||||
|
||||
static const struct watchdog_info retu_wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
|
||||
.identity = "Retu watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops retu_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = retu_wdt_start,
|
||||
.stop = retu_wdt_stop,
|
||||
.ping = retu_wdt_ping,
|
||||
.set_timeout = retu_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static int retu_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
|
||||
bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
struct watchdog_device *retu_wdt;
|
||||
struct retu_wdt_dev *wdev;
|
||||
int ret;
|
||||
|
||||
retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL);
|
||||
if (!retu_wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
|
||||
if (!wdev)
|
||||
return -ENOMEM;
|
||||
|
||||
retu_wdt->info = &retu_wdt_info;
|
||||
retu_wdt->ops = &retu_wdt_ops;
|
||||
retu_wdt->timeout = RETU_WDT_MAX_TIMER;
|
||||
retu_wdt->min_timeout = 0;
|
||||
retu_wdt->max_timeout = RETU_WDT_MAX_TIMER;
|
||||
|
||||
watchdog_set_drvdata(retu_wdt, wdev);
|
||||
watchdog_set_nowayout(retu_wdt, nowayout);
|
||||
|
||||
wdev->rdev = rdev;
|
||||
wdev->dev = &pdev->dev;
|
||||
|
||||
INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work);
|
||||
|
||||
ret = watchdog_register_device(retu_wdt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (nowayout)
|
||||
retu_wdt_ping(retu_wdt);
|
||||
else
|
||||
retu_wdt_ping_enable(wdev);
|
||||
|
||||
platform_set_drvdata(pdev, retu_wdt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int retu_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct watchdog_device *wdog = platform_get_drvdata(pdev);
|
||||
struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
||||
|
||||
watchdog_unregister_device(wdog);
|
||||
cancel_delayed_work_sync(&wdev->ping_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver retu_wdt_driver = {
|
||||
.probe = retu_wdt_probe,
|
||||
.remove = retu_wdt_remove,
|
||||
.driver = {
|
||||
.name = "retu-wdt",
|
||||
},
|
||||
};
|
||||
module_platform_driver(retu_wdt_driver);
|
||||
|
||||
MODULE_ALIAS("platform:retu-wdt");
|
||||
MODULE_DESCRIPTION("Retu watchdog");
|
||||
MODULE_AUTHOR("Amit Kucheria");
|
||||
MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
|
||||
MODULE_LICENSE("GPL");
|
@ -53,7 +53,7 @@
|
||||
#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
|
||||
static int tmr_margin;
|
||||
static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
|
||||
static int soft_noboot;
|
||||
static int debug;
|
||||
@ -226,6 +226,7 @@ static struct watchdog_ops s3c2410wdt_ops = {
|
||||
static struct watchdog_device s3c2410_wdd = {
|
||||
.info = &s3c2410_wdt_ident,
|
||||
.ops = &s3c2410wdt_ops,
|
||||
.timeout = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME,
|
||||
};
|
||||
|
||||
/* interrupt handler code */
|
||||
@ -309,7 +310,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
unsigned int wtcon;
|
||||
int started = 0;
|
||||
int ret;
|
||||
int size;
|
||||
|
||||
DBG("%s: probe=%p\n", __func__, pdev);
|
||||
|
||||
@ -330,28 +330,20 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* get the memory region for the watchdog timer */
|
||||
|
||||
size = resource_size(wdt_mem);
|
||||
if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
|
||||
dev_err(dev, "failed to get memory region\n");
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
wdt_base = ioremap(wdt_mem->start, size);
|
||||
wdt_base = devm_request_and_ioremap(dev, wdt_mem);
|
||||
if (wdt_base == NULL) {
|
||||
dev_err(dev, "failed to ioremap() region\n");
|
||||
ret = -EINVAL;
|
||||
goto err_req;
|
||||
dev_err(dev, "failed to devm_request_and_ioremap() region\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
DBG("probe: mapped wdt_base=%p\n", wdt_base);
|
||||
|
||||
wdt_clock = clk_get(&pdev->dev, "watchdog");
|
||||
wdt_clock = devm_clk_get(dev, "watchdog");
|
||||
if (IS_ERR(wdt_clock)) {
|
||||
dev_err(dev, "failed to find watchdog clock source\n");
|
||||
ret = PTR_ERR(wdt_clock);
|
||||
goto err_map;
|
||||
goto err;
|
||||
}
|
||||
|
||||
clk_prepare_enable(wdt_clock);
|
||||
@ -365,7 +357,8 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
/* see if we can actually set the requested timer margin, and if
|
||||
* not, try the default value */
|
||||
|
||||
if (s3c2410wdt_set_heartbeat(&s3c2410_wdd, tmr_margin)) {
|
||||
watchdog_init_timeout(&s3c2410_wdd, tmr_margin, &pdev->dev);
|
||||
if (s3c2410wdt_set_heartbeat(&s3c2410_wdd, s3c2410_wdd.timeout)) {
|
||||
started = s3c2410wdt_set_heartbeat(&s3c2410_wdd,
|
||||
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
|
||||
|
||||
@ -378,7 +371,8 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
"cannot start\n");
|
||||
}
|
||||
|
||||
ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
|
||||
ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
|
||||
pdev->name, pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to install irq (%d)\n", ret);
|
||||
goto err_cpufreq;
|
||||
@ -389,7 +383,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
ret = watchdog_register_device(&s3c2410_wdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot register watchdog (%d)\n", ret);
|
||||
goto err_irq;
|
||||
goto err_cpufreq;
|
||||
}
|
||||
|
||||
if (tmr_atboot && started == 0) {
|
||||
@ -414,23 +408,13 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
free_irq(wdt_irq->start, pdev);
|
||||
|
||||
err_cpufreq:
|
||||
s3c2410wdt_cpufreq_deregister();
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(wdt_clock);
|
||||
clk_put(wdt_clock);
|
||||
wdt_clock = NULL;
|
||||
|
||||
err_map:
|
||||
iounmap(wdt_base);
|
||||
|
||||
err_req:
|
||||
release_mem_region(wdt_mem->start, size);
|
||||
|
||||
err:
|
||||
wdt_irq = NULL;
|
||||
wdt_mem = NULL;
|
||||
@ -441,17 +425,11 @@ static int s3c2410wdt_remove(struct platform_device *dev)
|
||||
{
|
||||
watchdog_unregister_device(&s3c2410_wdd);
|
||||
|
||||
free_irq(wdt_irq->start, dev);
|
||||
|
||||
s3c2410wdt_cpufreq_deregister();
|
||||
|
||||
clk_disable_unprepare(wdt_clock);
|
||||
clk_put(wdt_clock);
|
||||
wdt_clock = NULL;
|
||||
|
||||
iounmap(wdt_base);
|
||||
|
||||
release_mem_region(wdt_mem->start, resource_size(wdt_mem));
|
||||
wdt_irq = NULL;
|
||||
wdt_mem = NULL;
|
||||
return 0;
|
||||
|
@ -361,7 +361,7 @@ static unsigned char sp5100_tco_setupdevice(void)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
const char *dev_name = NULL;
|
||||
u32 val;
|
||||
u32 val, tmp_val;
|
||||
u32 index_reg, data_reg, base_addr;
|
||||
|
||||
/* Match the PCI device */
|
||||
@ -497,30 +497,19 @@ static unsigned char sp5100_tco_setupdevice(void)
|
||||
pr_debug("Got 0x%04x from resource tree\n", val);
|
||||
}
|
||||
|
||||
/* Restore to the low three bits, if chipset is SB8x0(or later) */
|
||||
if (sp5100_tco_pci->revision >= 0x40) {
|
||||
u8 reserved_bit;
|
||||
reserved_bit = inb(base_addr) & 0x7;
|
||||
val |= (u32)reserved_bit;
|
||||
}
|
||||
/* Restore to the low three bits */
|
||||
outb(base_addr+0, index_reg);
|
||||
tmp_val = val | (inb(data_reg) & 0x7);
|
||||
|
||||
/* Re-programming the watchdog timer base address */
|
||||
outb(base_addr+0, index_reg);
|
||||
/* Low three bits of BASE are reserved */
|
||||
outb((val >> 0) & 0xf8, data_reg);
|
||||
outb((tmp_val >> 0) & 0xff, data_reg);
|
||||
outb(base_addr+1, index_reg);
|
||||
outb((val >> 8) & 0xff, data_reg);
|
||||
outb((tmp_val >> 8) & 0xff, data_reg);
|
||||
outb(base_addr+2, index_reg);
|
||||
outb((val >> 16) & 0xff, data_reg);
|
||||
outb((tmp_val >> 16) & 0xff, data_reg);
|
||||
outb(base_addr+3, index_reg);
|
||||
outb((val >> 24) & 0xff, data_reg);
|
||||
|
||||
/*
|
||||
* Clear unnecessary the low three bits,
|
||||
* if chipset is SB8x0(or later)
|
||||
*/
|
||||
if (sp5100_tco_pci->revision >= 0x40)
|
||||
val &= ~0x7;
|
||||
outb((tmp_val >> 24) & 0xff, data_reg);
|
||||
|
||||
if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
|
||||
dev_name)) {
|
||||
|
111
drivers/watchdog/stmp3xxx_rtc_wdt.c
Normal file
111
drivers/watchdog/stmp3xxx_rtc_wdt.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Watchdog driver for the RTC based watchdog in STMP3xxx and i.MX23/28
|
||||
*
|
||||
* Author: Wolfram Sang <w.sang@pengutronix.de>
|
||||
*
|
||||
* Copyright (C) 2011-12 Wolfram Sang, Pengutronix
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/stmp3xxx_rtc_wdt.h>
|
||||
|
||||
#define WDOG_TICK_RATE 1000 /* 1 kHz clock */
|
||||
#define STMP3XXX_DEFAULT_TIMEOUT 19
|
||||
#define STMP3XXX_MAX_TIMEOUT (UINT_MAX / WDOG_TICK_RATE)
|
||||
|
||||
static int heartbeat = STMP3XXX_DEFAULT_TIMEOUT;
|
||||
module_param(heartbeat, uint, 0);
|
||||
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat period in seconds from 1 to "
|
||||
__MODULE_STRING(STMP3XXX_MAX_TIMEOUT) ", default "
|
||||
__MODULE_STRING(STMP3XXX_DEFAULT_TIMEOUT));
|
||||
|
||||
static int wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct device *dev = watchdog_get_drvdata(wdd);
|
||||
struct stmp3xxx_wdt_pdata *pdata = dev->platform_data;
|
||||
|
||||
pdata->wdt_set_timeout(dev->parent, wdd->timeout * WDOG_TICK_RATE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct device *dev = watchdog_get_drvdata(wdd);
|
||||
struct stmp3xxx_wdt_pdata *pdata = dev->platform_data;
|
||||
|
||||
pdata->wdt_set_timeout(dev->parent, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_set_timeout(struct watchdog_device *wdd, unsigned new_timeout)
|
||||
{
|
||||
wdd->timeout = new_timeout;
|
||||
return wdt_start(wdd);
|
||||
}
|
||||
|
||||
static const struct watchdog_info stmp3xxx_wdt_ident = {
|
||||
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
|
||||
.identity = "STMP3XXX RTC Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops stmp3xxx_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = wdt_start,
|
||||
.stop = wdt_stop,
|
||||
.set_timeout = wdt_set_timeout,
|
||||
};
|
||||
|
||||
static struct watchdog_device stmp3xxx_wdd = {
|
||||
.info = &stmp3xxx_wdt_ident,
|
||||
.ops = &stmp3xxx_wdt_ops,
|
||||
.min_timeout = 1,
|
||||
.max_timeout = STMP3XXX_MAX_TIMEOUT,
|
||||
.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
|
||||
};
|
||||
|
||||
static int stmp3xxx_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
|
||||
|
||||
stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
|
||||
|
||||
ret = watchdog_register_device(&stmp3xxx_wdd);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "cannot register watchdog device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "initialized watchdog with heartbeat %ds\n",
|
||||
stmp3xxx_wdd.timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stmp3xxx_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
watchdog_unregister_device(&stmp3xxx_wdd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver stmp3xxx_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "stmp3xxx_rtc_wdt",
|
||||
},
|
||||
.probe = stmp3xxx_wdt_probe,
|
||||
.remove = stmp3xxx_wdt_remove,
|
||||
};
|
||||
module_platform_driver(stmp3xxx_wdt_driver);
|
||||
|
||||
MODULE_DESCRIPTION("STMP3XXX RTC Watchdog Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
@ -1,288 +0,0 @@
|
||||
/*
|
||||
* Watchdog driver for Freescale STMP37XX/STMP378X
|
||||
*
|
||||
* Author: Vitaly Wool <vital@embeddedalley.com>
|
||||
*
|
||||
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <mach/platform.h>
|
||||
#include <mach/regs-rtc.h>
|
||||
|
||||
#define DEFAULT_HEARTBEAT 19
|
||||
#define MAX_HEARTBEAT (0x10000000 >> 6)
|
||||
|
||||
/* missing bitmask in headers */
|
||||
#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000
|
||||
|
||||
#define WDT_IN_USE 0
|
||||
#define WDT_OK_TO_CLOSE 1
|
||||
|
||||
#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */
|
||||
|
||||
static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock);
|
||||
static unsigned long wdt_status;
|
||||
static const bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
static int heartbeat = DEFAULT_HEARTBEAT;
|
||||
static unsigned long boot_status;
|
||||
|
||||
static void wdt_enable(u32 value)
|
||||
{
|
||||
spin_lock(&stmp3xxx_wdt_io_lock);
|
||||
__raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG);
|
||||
stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
|
||||
stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
|
||||
REGS_RTC_BASE + HW_RTC_PERSISTENT1);
|
||||
spin_unlock(&stmp3xxx_wdt_io_lock);
|
||||
}
|
||||
|
||||
static void wdt_disable(void)
|
||||
{
|
||||
spin_lock(&stmp3xxx_wdt_io_lock);
|
||||
stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
|
||||
REGS_RTC_BASE + HW_RTC_PERSISTENT1);
|
||||
stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
|
||||
spin_unlock(&stmp3xxx_wdt_io_lock);
|
||||
}
|
||||
|
||||
static void wdt_ping(void)
|
||||
{
|
||||
wdt_enable(heartbeat * WDOG_COUNTER_RATE);
|
||||
}
|
||||
|
||||
static int stmp3xxx_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
|
||||
return -EBUSY;
|
||||
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
wdt_ping();
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
if (len) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
|
||||
for (i = 0; i != len; i++) {
|
||||
char c;
|
||||
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
}
|
||||
}
|
||||
wdt_ping();
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_CARDRESET |
|
||||
WDIOF_MAGICCLOSE |
|
||||
WDIOF_SETTIMEOUT |
|
||||
WDIOF_KEEPALIVEPING,
|
||||
.identity = "STMP3XXX Watchdog",
|
||||
};
|
||||
|
||||
static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
int new_heartbeat, opts;
|
||||
int ret = -ENOTTY;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
|
||||
break;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
ret = put_user(0, p);
|
||||
break;
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
ret = put_user(boot_status, p);
|
||||
break;
|
||||
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(opts, p)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (opts & WDIOS_DISABLECARD)
|
||||
wdt_disable();
|
||||
else if (opts & WDIOS_ENABLECARD)
|
||||
wdt_ping();
|
||||
else {
|
||||
pr_debug("%s: unknown option 0x%x\n", __func__, opts);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
wdt_ping();
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(new_heartbeat, p)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
heartbeat = new_heartbeat;
|
||||
wdt_ping();
|
||||
/* Fall through */
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
ret = put_user(heartbeat, p);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stmp3xxx_wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!nowayout) {
|
||||
if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
|
||||
wdt_ping();
|
||||
pr_debug("%s: Device closed unexpectedly\n", __func__);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
wdt_disable();
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
}
|
||||
}
|
||||
clear_bit(WDT_IN_USE, &wdt_status);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations stmp3xxx_wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = stmp3xxx_wdt_write,
|
||||
.unlocked_ioctl = stmp3xxx_wdt_ioctl,
|
||||
.open = stmp3xxx_wdt_open,
|
||||
.release = stmp3xxx_wdt_release,
|
||||
};
|
||||
|
||||
static struct miscdevice stmp3xxx_wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &stmp3xxx_wdt_fops,
|
||||
};
|
||||
|
||||
static int stmp3xxx_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
|
||||
heartbeat = DEFAULT_HEARTBEAT;
|
||||
|
||||
boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) &
|
||||
BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER;
|
||||
boot_status = !!boot_status;
|
||||
stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
|
||||
REGS_RTC_BASE + HW_RTC_PERSISTENT1);
|
||||
wdt_disable(); /* disable for now */
|
||||
|
||||
ret = misc_register(&stmp3xxx_wdt_miscdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "cannot register misc device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("initialized, heartbeat %d sec\n", heartbeat);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stmp3xxx_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&stmp3xxx_wdt_miscdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int wdt_suspended;
|
||||
static u32 wdt_saved_time;
|
||||
|
||||
static int stmp3xxx_wdt_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) &
|
||||
BM_RTC_CTRL_WATCHDOGEN) {
|
||||
wdt_suspended = 1;
|
||||
wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG);
|
||||
wdt_disable();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stmp3xxx_wdt_resume(struct platform_device *pdev)
|
||||
{
|
||||
if (wdt_suspended) {
|
||||
wdt_enable(wdt_saved_time);
|
||||
wdt_suspended = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define stmp3xxx_wdt_suspend NULL
|
||||
#define stmp3xxx_wdt_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver platform_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "stmp3xxx_wdt",
|
||||
},
|
||||
.probe = stmp3xxx_wdt_probe,
|
||||
.remove = stmp3xxx_wdt_remove,
|
||||
.suspend = stmp3xxx_wdt_suspend,
|
||||
.resume = stmp3xxx_wdt_resume,
|
||||
};
|
||||
|
||||
module_platform_driver(platform_wdt_driver);
|
||||
|
||||
MODULE_DESCRIPTION("STMP3XXX Watchdog Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(heartbeat, int, 0);
|
||||
MODULE_PARM_DESC(heartbeat,
|
||||
"Watchdog heartbeat period in seconds from 1 to "
|
||||
__MODULE_STRING(MAX_HEARTBEAT) ", default "
|
||||
__MODULE_STRING(DEFAULT_HEARTBEAT));
|
||||
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
@ -36,12 +36,68 @@
|
||||
#include <linux/init.h> /* For __init/__exit/... */
|
||||
#include <linux/idr.h> /* For ida_* macros */
|
||||
#include <linux/err.h> /* For IS_ERR macros */
|
||||
#include <linux/of.h> /* For of_get_timeout_sec */
|
||||
|
||||
#include "watchdog_core.h" /* For watchdog_dev_register/... */
|
||||
|
||||
static DEFINE_IDA(watchdog_ida);
|
||||
static struct class *watchdog_class;
|
||||
|
||||
static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
|
||||
{
|
||||
/*
|
||||
* Check that we have valid min and max timeout values, if
|
||||
* not reset them both to 0 (=not used or unknown)
|
||||
*/
|
||||
if (wdd->min_timeout > wdd->max_timeout) {
|
||||
pr_info("Invalid min and max timeout values, resetting to 0!\n");
|
||||
wdd->min_timeout = 0;
|
||||
wdd->max_timeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* watchdog_init_timeout() - initialize the timeout field
|
||||
* @timeout_parm: timeout module parameter
|
||||
* @dev: Device that stores the timeout-sec property
|
||||
*
|
||||
* Initialize the timeout field of the watchdog_device struct with either the
|
||||
* timeout module parameter (if it is valid value) or the timeout-sec property
|
||||
* (only if it is a valid value and the timeout_parm is out of bounds).
|
||||
* If none of them are valid then we keep the old value (which should normally
|
||||
* be the default timeout value.
|
||||
*
|
||||
* A zero is returned on success and -EINVAL for failure.
|
||||
*/
|
||||
int watchdog_init_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout_parm, struct device *dev)
|
||||
{
|
||||
unsigned int t = 0;
|
||||
int ret = 0;
|
||||
|
||||
watchdog_check_min_max_timeout(wdd);
|
||||
|
||||
/* try to get the tiemout module parameter first */
|
||||
if (!watchdog_timeout_invalid(wdd, timeout_parm)) {
|
||||
wdd->timeout = timeout_parm;
|
||||
return ret;
|
||||
}
|
||||
if (timeout_parm)
|
||||
ret = -EINVAL;
|
||||
|
||||
/* try to get the timeout_sec property */
|
||||
if (dev == NULL || dev->of_node == NULL)
|
||||
return ret;
|
||||
of_property_read_u32(dev->of_node, "timeout-sec", &t);
|
||||
if (!watchdog_timeout_invalid(wdd, t))
|
||||
wdd->timeout = t;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(watchdog_init_timeout);
|
||||
|
||||
/**
|
||||
* watchdog_register_device() - register a watchdog device
|
||||
* @wdd: watchdog device
|
||||
@ -63,15 +119,7 @@ int watchdog_register_device(struct watchdog_device *wdd)
|
||||
if (wdd->ops->start == NULL || wdd->ops->stop == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Check that we have valid min and max timeout values, if
|
||||
* not reset them both to 0 (=not used or unknown)
|
||||
*/
|
||||
if (wdd->min_timeout > wdd->max_timeout) {
|
||||
pr_info("Invalid min and max timeout values, resetting to 0!\n");
|
||||
wdd->min_timeout = 0;
|
||||
wdd->max_timeout = 0;
|
||||
}
|
||||
watchdog_check_min_max_timeout(wdd);
|
||||
|
||||
/*
|
||||
* Note: now that all watchdog_device data has been verified, we
|
||||
|
@ -200,8 +200,7 @@ static int watchdog_set_timeout(struct watchdog_device *wddev,
|
||||
!(wddev->info->options & WDIOF_SETTIMEOUT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((wddev->max_timeout != 0) &&
|
||||
(timeout < wddev->min_timeout || timeout > wddev->max_timeout))
|
||||
if (watchdog_timeout_invalid(wddev, timeout))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&wddev->lock);
|
||||
|
@ -1,7 +1,10 @@
|
||||
#ifndef LINUX_BCM47XX_WDT_H_
|
||||
#define LINUX_BCM47XX_WDT_H_
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
|
||||
struct bcm47xx_wdt {
|
||||
@ -10,6 +13,12 @@ struct bcm47xx_wdt {
|
||||
u32 max_timer_ms;
|
||||
|
||||
void *driver_data;
|
||||
|
||||
struct watchdog_device wdd;
|
||||
struct notifier_block notifier;
|
||||
|
||||
struct timer_list soft_timer;
|
||||
atomic_t soft_ticks;
|
||||
};
|
||||
|
||||
static inline void *bcm47xx_wdt_get_drvdata(struct bcm47xx_wdt *wdt)
|
||||
|
15
include/linux/stmp3xxx_rtc_wdt.h
Normal file
15
include/linux/stmp3xxx_rtc_wdt.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* stmp3xxx_rtc_wdt.h
|
||||
*
|
||||
* Copyright (C) 2011 Wolfram Sang, Pengutronix e.K.
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
#ifndef __LINUX_STMP3XXX_RTC_WDT_H
|
||||
#define __LINUX_STMP3XXX_RTC_WDT_H
|
||||
|
||||
struct stmp3xxx_wdt_pdata {
|
||||
void (*wdt_set_timeout)(struct device *dev, u32 timeout);
|
||||
};
|
||||
|
||||
#endif /* __LINUX_STMP3XXX_RTC_WDT_H */
|
@ -118,6 +118,13 @@ static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool noway
|
||||
set_bit(WDOG_NO_WAY_OUT, &wdd->status);
|
||||
}
|
||||
|
||||
/* Use the following function to check if a timeout value is invalid */
|
||||
static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t)
|
||||
{
|
||||
return ((wdd->max_timeout != 0) &&
|
||||
(t < wdd->min_timeout || t > wdd->max_timeout));
|
||||
}
|
||||
|
||||
/* Use the following functions to manipulate watchdog driver specific data */
|
||||
static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
|
||||
{
|
||||
@ -130,6 +137,8 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
|
||||
}
|
||||
|
||||
/* drivers/watchdog/watchdog_core.c */
|
||||
extern int watchdog_init_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout_parm, struct device *dev);
|
||||
extern int watchdog_register_device(struct watchdog_device *);
|
||||
extern void watchdog_unregister_device(struct watchdog_device *);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user