@@ -13,16 +13,17 @@
* The Watchdog Timer Mode Register can be only written to once. If the
* timeout need to be set from Linux, be sure that the bootstrap or the
* bootloader doesn't write to this register.
+ * The Watchdog Timer default is running with maximum counter value
+ * (WDV=0xfff) at reset, i.e., at power-up. It MUST be either disabled
+ * or be reprogrammed within the maxinum margin(16s).
*/
#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>
@@ -31,7 +32,6 @@
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/bitops.h>
-#include <linux/uaccess.h>
#include <linux/of.h>
#include "at91sam9_wdt.h"
@@ -65,8 +65,6 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-static void at91_ping(unsigned long data);
-
struct at91wdt_drvdata {
void __iomem *phybase;
bool is_enable; /* indicate if the watchdog is eabled */
@@ -99,7 +97,7 @@ static inline void at91_wdt_reset(struct at91wdt_drvdata *driver_data)
/*
* Timer tick
*/
-static void at91_ping(unsigned long data)
+static void at91wdt_timer_tick(unsigned long data)
{
struct watchdog_device *wddev = (struct watchdog_device *)data;
struct at91wdt_drvdata *driver_data = watchdog_get_drvdata(wddev);
@@ -107,45 +105,31 @@ static void at91_ping(unsigned long data)
if (time_before(jiffies, driver_data->next_heartbeat)) {
at91_wdt_reset(driver_data);
mod_timer(&driver_data->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)
-{
- driver_data->next_heartbeat = jiffies + heartbeat * HZ;
- mod_timer(&driver_data->timer, jiffies + WDT_TIMEOUT);
- return nonseekable_open(inode, file);
-}
-
-/*
- * Close the watchdog device.
- */
-static int at91_wdt_close(struct inode *inode, struct file *file)
-{
- del_timer(&driver_data->timer);
-
- return 0;
+ if (!watchdog_is_open(wddev))
+ driver_data->next_heartbeat = jiffies
+ + wddev->timeout * HZ;
+ } else {
+ pr_crit("I will reset your machine !\n");
+ }
}
-/*
- * Set the watchdog time interval in 1/256Hz (write-once)
- * Counter is 12 bit.
- */
-static int at91_wdt_settimeout(unsigned int timeout)
+static int at91wdt_enable(struct watchdog_device *wddev, unsigned int timeout)
{
+ struct at91wdt_drvdata *driver_data = watchdog_get_drvdata(wddev);
unsigned int reg;
- unsigned int mr;
- /* Check if disabled */
- mr = wdt_read(AT91_WDT_MR);
- if (mr & AT91_WDT_WDDIS) {
- pr_err("sorry, watchdog is disabled\n");
- return -EIO;
+ /*
+ * Check if the watchdog is disabled,
+ * if disabled, the reason is the bootstrap or the bootloader has
+ * written the Watchdog Timer Mode Register to disable the
+ * watchdog timer
+ */
+ reg = wdt_read(driver_data, AT91_WDT_MR);
+ if (reg & AT91_WDT_WDDIS) {
+ driver_data->is_enable = false;
+ pr_info("sorry, watchdog is disabled\n");
+ return -1;
}
/*
@@ -159,7 +143,9 @@ static int at91_wdt_settimeout(unsigned int timeout)
| AT91_WDT_WDDBGHLT /* disabled in debug mode */
| AT91_WDT_WDD /* restart at any time */
| (timeout & AT91_WDT_WDV); /* timer value */
- wdt_write(AT91_WDT_MR, reg);
+ wdt_write(driver_data, AT91_WDT_MR, reg);
+
+ driver_data->is_enable = true;
return 0;
}
@@ -170,99 +156,63 @@ static const struct watchdog_info at91_wdt_info = {
WDIOF_MAGICCLOSE,
};
-/*
- * Handle commands from user-space.
- */
-static long at91_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static int at91wdt_start(struct watchdog_device *wddev)
{
- 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);
+ struct at91wdt_drvdata *driver_data = watchdog_get_drvdata(wddev);
- case WDIOC_KEEPALIVE:
- driver_data->next_heartbeat = jiffies + heartbeat * HZ;
+ if (driver_data->is_enable) {
+ driver_data->next_heartbeat = jiffies + wddev->timeout * HZ;
+ mod_timer(&driver_data->timer, jiffies + WDT_TIMEOUT);
return 0;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_value, p))
- return -EFAULT;
-
- heartbeat = new_value;
- driver_data->next_heartbeat = jiffies + heartbeat * HZ;
-
- return put_user(new_value, p); /* return current value */
-
- case WDIOC_GETTIMEOUT:
- return put_user(heartbeat, p);
+ } else {
+ return -EIO;
}
- 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)
+static int at91wdt_stop(struct watchdog_device *wddev)
{
- if (!len)
- return 0;
+ struct at91wdt_drvdata *driver_data = watchdog_get_drvdata(wddev);
- /* Scan for magic character */
- if (!nowayout) {
- size_t i;
+ if (driver_data->is_enable)
+ return -EIO;
+ else
+ return 0;
+}
+static int at91wdt_ping(struct watchdog_device *wddev)
+{
+ struct at91wdt_drvdata *driver_data = watchdog_get_drvdata(wddev);
- for (i = 0; i < len; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- }
- }
+ if (driver_data->is_enable) {
+ driver_data->next_heartbeat = jiffies + wddev->timeout * HZ;
+ mod_timer(&driver_data->timer, jiffies + WDT_TIMEOUT);
+ return 0;
+ } else {
+ return -EIO;
}
-
- driver_data->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 struct miscdevice at91wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &at91wdt_fops,
+static struct watchdog_ops at91wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = at91wdt_start,
+ .stop = at91wdt_stop,
+ .ping = at91wdt_ping,
};
static struct watchdog_device at91wdt_wdd __initdata = {
.timeout = WDT_HEARTBEAT,
.min_timeout = MIN_HEARTBEAT,
.max_timeout = MAX_HEARTBEAT,
+ .info = &at91_wdt_info,
+ .ops = &at91wdt_ops,
};
static int __init at91wdt_probe(struct platform_device *pdev)
{
struct at91wdt_drvdata *driver_data;
struct resource *r;
- int res;
+ int ret;
driver_data = devm_kzalloc(&pdev->dev,
sizeof(*driver_data), GFP_KERNEL);
@@ -273,32 +223,32 @@ static int __init at91wdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(&at91wdt_wdd, driver_data);
- if (at91wdt_miscdev.parent)
- return -EBUSY;
- at91wdt_miscdev.parent = &pdev->dev;
-
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENODEV;
+
driver_data->phybase = ioremap(r->start, resource_size(r));
if (!driver_data->phybase) {
dev_err(&pdev->dev, "failed to map registers, aborting.\n");
return -ENOMEM;
}
- watchdog_init_timeout(&at91wdt_wdd, heartbeat, pdev->dev.of_node);
+ ret = watchdog_register_device(&at91wdt_wdd);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot register watchdog (%d)\n", ret);
+ return ret;
+ }
- /* Set watchdog */
- res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000));
- if (res)
- return res;
+ watchdog_init_timeout(&at91wdt_wdd, heartbeat, pdev->dev.of_node);
- res = misc_register(&at91wdt_miscdev);
- if (res)
- return res;
+ ret = at91wdt_enable(&at91wdt_wdd, ms_to_ticks(WDT_HW_TIMEOUT * 1000));
+ if (ret) {
+ pr_info("the watchdog has been disabled\n");
+ return 0;
+ }
driver_data->next_heartbeat = jiffies + at91wdt_wdd.timeout * HZ;
- setup_timer(&driver_data->timer, at91_ping,
+ setup_timer(&driver_data->timer, at91wdt_timer_tick,
(unsigned long)&at91wdt_wdd);
mod_timer(&driver_data->timer, jiffies + WDT_TIMEOUT);
@@ -310,13 +260,9 @@ static int __init at91wdt_probe(struct platform_device *pdev)
static int __exit at91wdt_remove(struct platform_device *pdev)
{
- int res;
-
- res = misc_deregister(&at91wdt_miscdev);
- if (!res)
- at91wdt_miscdev.parent = NULL;
+ watchdog_unregister_device(&at91wdt_wdd);
- return res;
+ return 0;
}
#if defined(CONFIG_OF)
@@ -353,4 +299,3 @@ module_exit(at91sam_wdt_exit);
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);
According to Documentation/watchdog/convert_drivers_to_kernel_api.txt, remove the file_operations struct, miscdevice, and obsolete includes Since the at91sam watchdog inherent characteristics, add the watchdog operations: at91wdt_start, at91wdt_stop and at91wdt_ping. Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com> Cc: wim@iguana.be Cc: linux-watchdog@vger.kernel.org Cc: linux-kernel@vger.kernel.org --- drivers/watchdog/at91sam9_wdt.c | 199 ++++++++++++++------------------------- 1 file changed, 72 insertions(+), 127 deletions(-)