Commit 68b86a25 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:
   - 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
  ...
parents 527c680f 41e9f3f7
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>;
};
* 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.
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;
/* calculate when the next userspace timeout will be */
at91wdt_private.next_heartbeat = jiffies + wdd->timeout * HZ;
return 0;
}
at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
static int at91_wdt_start(struct watchdog_device *wdd)
{
/* calculate the next userspace timeout and modify the timer */
at91_wdt_ping(wdd);
mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
return nonseekable_open(inode, file);
return 0;
}
/*
* Close the watchdog device.
*/
static int at91_wdt_close(struct inode *inode, struct file *file)
static int at91_wdt_stop(struct watchdog_device *wdd)
{
clear_bit(0, &at91wdt_private.open);
/* stop internal ping */
if (!at91wdt_private.expect_close)
del_timer(&at91wdt_private.timer);
/* 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);