Convert driver to use watchdog subsystem.

RFC/RFT: Completely untested. Also see FIXME comments in code.

Notes:
- The driver no longer uses platform data.
  Using resources to pass PCI configuration register addresses
  does not make much sense, and the PCI device can be obtained
  with to_pci_dev(dev->parent).
- WDIOC_GETSTATUS no longer returns the raw watchdog register
  value.
- WDIOS_DISABLECARD no longer returns -EIO but just stops
  the watchdog.

Cc: Florian Fainelli <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
---
 drivers/watchdog/rdc321x_wdt.c | 274 +++++++++------------------------
 1 file changed, 69 insertions(+), 205 deletions(-)

diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c
index 57187efeb86f..5a3eaca6b9f8 100644
--- a/drivers/watchdog/rdc321x_wdt.c
+++ b/drivers/watchdog/rdc321x_wdt.c
@@ -8,19 +8,10 @@
  */
 
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/types.h>
 #include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/fs.h>
-#include <linux/ioport.h>
-#include <linux/timer.h>
-#include <linux/completion.h>
-#include <linux/jiffies.h>
 #include <linux/platform_device.h>
 #include <linux/watchdog.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
 #include <linux/mfd/rdc321x.h>
 
 #define RDC_WDT_MASK   0x80000000 /* Mask */
@@ -32,247 +23,120 @@
 #define RDC_WDT_CNT    0x00000001 /* WDT count */
 
 #define RDC_CLS_TMR    0x80003844 /* Clear timer */
+                               // xxxx44 -> 2.34s or 2.68s timeout
+                               // (or maybe 2.5s / 2.85s)
+                               // xxx8xx -> route to irq[1]
+                               // xx3xxx -> reserved
+                               // (should that have been 3xxxxx ?)
+                               // 8xxxxxxx -> not defined in datasheet
 
-#define RDC_WDT_INTERVAL       (HZ/10+1)
+#define DEFAULT_TIMEOUT                30      /* seconds */
 
-static int ticks = 1000;
-
-/* some device data */
-
-static struct {
-       struct completion stop;
-       int running;
-       struct timer_list timer;
-       int queue;
-       int default_ticks;
-       unsigned long inuse;
+struct rdc321x_wdt_device {
+       struct watchdog_device wdd;
        spinlock_t lock;
        struct pci_dev *sb_pdev;
-       int base_reg;
-} rdc321x_wdt_device;
-
-/* generic helper functions */
+};
 
-static void rdc321x_wdt_trigger(struct timer_list *unused)
+static int rdc321x_wdt_ping(struct watchdog_device *wdd)
 {
+       struct rdc321x_wdt_device *wdt = watchdog_get_drvdata(wdd);
        unsigned long flags;
        u32 val;
 
-       if (rdc321x_wdt_device.running)
-               ticks--;
-
-       /* keep watchdog alive */
-       spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
-       pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
-                                       rdc321x_wdt_device.base_reg, &val);
+       spin_lock_irqsave(&wdt->lock, flags);
+       /* FIXME it appears that writing RDC_WDT_EN restarts the timer */
+       pci_read_config_dword(wdt->sb_pdev, RDC321X_WDT_CTRL, &val);
        val |= RDC_WDT_EN;
-       pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
-                                       rdc321x_wdt_device.base_reg, val);
-       spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
-
-       /* requeue?? */
-       if (rdc321x_wdt_device.queue && ticks)
-               mod_timer(&rdc321x_wdt_device.timer,
-                               jiffies + RDC_WDT_INTERVAL);
-       else {
-               /* ticks doesn't matter anyway */
-               complete(&rdc321x_wdt_device.stop);
-       }
-
-}
-
-static void rdc321x_wdt_reset(void)
-{
-       ticks = rdc321x_wdt_device.default_ticks;
-}
-
-static void rdc321x_wdt_start(void)
-{
-       unsigned long flags;
-
-       if (!rdc321x_wdt_device.queue) {
-               rdc321x_wdt_device.queue = 1;
-
-               /* Clear the timer */
-               spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
-               pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
-                               rdc321x_wdt_device.base_reg, RDC_CLS_TMR);
-
-               /* Enable watchdog and set the timeout to 81.92 us */
-               pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
-                                       rdc321x_wdt_device.base_reg,
-                                       RDC_WDT_EN | RDC_WDT_CNT);
-               spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
-
-               mod_timer(&rdc321x_wdt_device.timer,
-                               jiffies + RDC_WDT_INTERVAL);
-       }
+       pci_write_config_dword(wdt->sb_pdev, RDC321X_WDT_CTRL, val);
+       spin_unlock_irqrestore(&wdt->lock, flags);
 
-       /* if process dies, counter is not decremented */
-       rdc321x_wdt_device.running++;
-}
-
-static int rdc321x_wdt_stop(void)
-{
-       if (rdc321x_wdt_device.running)
-               rdc321x_wdt_device.running = 0;
-
-       ticks = rdc321x_wdt_device.default_ticks;
-
-       return -EIO;
-}
-
-/* filesystem operations */
-static int rdc321x_wdt_open(struct inode *inode, struct file *file)
-{
-       if (test_and_set_bit(0, &rdc321x_wdt_device.inuse))
-               return -EBUSY;
-
-       return stream_open(inode, file);
-}
-
-static int rdc321x_wdt_release(struct inode *inode, struct file *file)
-{
-       clear_bit(0, &rdc321x_wdt_device.inuse);
        return 0;
 }
 
-static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
-                               unsigned long arg)
+static int rdc321x_wdt_start(struct watchdog_device *wdd)
 {
-       void __user *argp = (void __user *)arg;
-       u32 value;
-       static const struct watchdog_info ident = {
-               .options = WDIOF_CARDRESET,
-               .identity = "RDC321x WDT",
-       };
+       struct rdc321x_wdt_device *wdt = watchdog_get_drvdata(wdd);
        unsigned long flags;
 
-       switch (cmd) {
-       case WDIOC_KEEPALIVE:
-               rdc321x_wdt_reset();
-               break;
-       case WDIOC_GETSTATUS:
-               /* Read the value from the DATA register */
-               spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
-               pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
-                                       rdc321x_wdt_device.base_reg, &value);
-               spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
-               if (copy_to_user(argp, &value, sizeof(u32)))
-                       return -EFAULT;
-               break;
-       case WDIOC_GETSUPPORT:
-               if (copy_to_user(argp, &ident, sizeof(ident)))
-                       return -EFAULT;
-               break;
-       case WDIOC_SETOPTIONS:
-               if (copy_from_user(&value, argp, sizeof(int)))
-                       return -EFAULT;
-               switch (value) {
-               case WDIOS_ENABLECARD:
-                       rdc321x_wdt_start();
-                       break;
-               case WDIOS_DISABLECARD:
-                       return rdc321x_wdt_stop();
-               default:
-                       return -EINVAL;
-               }
-               break;
-       default:
-               return -ENOTTY;
-       }
+       spin_lock_irqsave(&wdt->lock, flags);
+       /* Clear the timer */
+       pci_write_config_dword(wdt->sb_pdev, RDC321X_WDT_CTRL, RDC_CLS_TMR);
+       /* Enable watchdog and set the timeout to 81.92 us */
+       // FIXME The above comment doesn't really make sense.
+       // The kernel would be unable to handle a timeout of 81.92 us.
+       // Also, the code used to generate a heartbeat every ~100 ms.
+       pci_write_config_dword(wdt->sb_pdev, RDC321X_WDT_CTRL, RDC_WDT_EN | 
RDC_WDT_CNT);
+       spin_unlock_irqrestore(&wdt->lock, flags);
+
        return 0;
 }
 
-static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf,
-                               size_t count, loff_t *ppos)
+static int rdc321x_wdt_stop(struct watchdog_device *wdd)
 {
-       if (!count)
-               return -EIO;
+       struct rdc321x_wdt_device *wdt = watchdog_get_drvdata(wdd);
 
-       rdc321x_wdt_reset();
+       pci_write_config_dword(wdt->sb_pdev, RDC321X_WDT_CTRL, 0);
 
-       return count;
+       return 0;
 }
 
-static const struct file_operations rdc321x_wdt_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .unlocked_ioctl = rdc321x_wdt_ioctl,
-       .compat_ioctl   = compat_ptr_ioctl,
-       .open           = rdc321x_wdt_open,
-       .write          = rdc321x_wdt_write,
-       .release        = rdc321x_wdt_release,
+static const struct watchdog_ops rdc321x_wdt_ops = {
+       .start = rdc321x_wdt_start,
+       .stop = rdc321x_wdt_stop,
+       .ping = rdc321x_wdt_ping,
+       // FIXME We can't set the timeout since we don't know
+       // how to write the counter register bits.
+       // .set_timeout = rdc321x_wdt_set_timeout,
 };
 
-static struct miscdevice rdc321x_wdt_misc = {
-       .minor  = WATCHDOG_MINOR,
-       .name   = "watchdog",
-       .fops   = &rdc321x_wdt_fops,
+static struct watchdog_info rdc321x_wdt_info = {
+       .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
+       .identity = "RDC321x WDT",
 };
 
 static int rdc321x_wdt_probe(struct platform_device *pdev)
 {
-       int err;
-       struct resource *r;
-       struct rdc321x_wdt_pdata *pdata;
+       struct device *dev = &pdev->dev;
+       struct rdc321x_wdt_device *wdt;
+       struct watchdog_device *wdd;
+       u32 val;
 
-       pdata = dev_get_platdata(&pdev->dev);
-       if (!pdata) {
-               dev_err(&pdev->dev, "no platform data supplied\n");
+       if (!dev->parent) {
+               dev_err(dev, "no parent device\n");
                return -ENODEV;
        }
 
-       r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg");
-       if (!r) {
-               dev_err(&pdev->dev, "failed to get wdt-reg resource\n");
-               return -ENODEV;
-       }
+       wdt = devm_kzalloc(dev, sizeof(*wdd), GFP_KERNEL);
+       if (!wdt)
+               return -ENOMEM;
 
-       rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
-       rdc321x_wdt_device.base_reg = r->start;
+       wdt->sb_pdev = to_pci_dev(dev->parent);
 
-       err = misc_register(&rdc321x_wdt_misc);
-       if (err < 0) {
-               dev_err(&pdev->dev, "misc_register failed\n");
-               return err;
-       }
+       spin_lock_init(&wdt->lock);
 
-       spin_lock_init(&rdc321x_wdt_device.lock);
+       wdd = &wdt->wdd;
+       wdd->parent = dev;
+       wdd->info = &rdc321x_wdt_info,
+       wdd->min_timeout = 1,
+       // FIXME
+       // wdd->max_hw_heartbeat_ms = 4690,     /* Assumes external clock */
+       wdd->max_hw_heartbeat_ms = 2340,        /* guess */
+       wdd->timeout = DEFAULT_TIMEOUT,
+       wdd->ops = &rdc321x_wdt_ops,
+       watchdog_set_drvdata(wdd, wdt);
 
-       /* Reset the watchdog */
-       pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
-                               rdc321x_wdt_device.base_reg, RDC_WDT_RST);
+       pci_read_config_dword(wdt->sb_pdev, RDC321X_WDT_CTRL, &val);
+       if (val & RDC_WDT_RST)
+               wdd->bootstatus = WDIOF_CARDRESET;
 
-       init_completion(&rdc321x_wdt_device.stop);
-       rdc321x_wdt_device.queue = 0;
+       pci_write_config_dword(wdt->sb_pdev, RDC321X_WDT_CTRL, RDC_WDT_RST);
 
-       clear_bit(0, &rdc321x_wdt_device.inuse);
-
-       timer_setup(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0);
-
-       rdc321x_wdt_device.default_ticks = ticks;
-
-       dev_info(&pdev->dev, "watchdog init success\n");
-
-       return 0;
-}
-
-static int rdc321x_wdt_remove(struct platform_device *pdev)
-{
-       if (rdc321x_wdt_device.queue) {
-               rdc321x_wdt_device.queue = 0;
-               wait_for_completion(&rdc321x_wdt_device.stop);
-       }
-
-       misc_deregister(&rdc321x_wdt_misc);
-
-       return 0;
+       return devm_watchdog_register_device(dev, wdd);
 }
 
 static struct platform_driver rdc321x_wdt_driver = {
        .probe = rdc321x_wdt_probe,
-       .remove = rdc321x_wdt_remove,
        .driver = {
                .name = "rdc321x-wdt",
        },
-- 
2.17.1

Reply via email to