Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * RDC321x watchdog driver
0004  *
0005  * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
0006  *
0007  * This driver is highly inspired from the cpu5_wdt driver
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/moduleparam.h>
0012 #include <linux/types.h>
0013 #include <linux/errno.h>
0014 #include <linux/miscdevice.h>
0015 #include <linux/fs.h>
0016 #include <linux/ioport.h>
0017 #include <linux/timer.h>
0018 #include <linux/completion.h>
0019 #include <linux/jiffies.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/watchdog.h>
0022 #include <linux/io.h>
0023 #include <linux/uaccess.h>
0024 #include <linux/mfd/rdc321x.h>
0025 
0026 #define RDC_WDT_MASK    0x80000000 /* Mask */
0027 #define RDC_WDT_EN  0x00800000 /* Enable bit */
0028 #define RDC_WDT_WTI 0x00200000 /* Generate CPU reset/NMI/WDT on timeout */
0029 #define RDC_WDT_RST 0x00100000 /* Reset bit */
0030 #define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */
0031 #define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */
0032 #define RDC_WDT_CNT 0x00000001 /* WDT count */
0033 
0034 #define RDC_CLS_TMR 0x80003844 /* Clear timer */
0035 
0036 #define RDC_WDT_INTERVAL    (HZ/10+1)
0037 
0038 static int ticks = 1000;
0039 
0040 /* some device data */
0041 
0042 static struct {
0043     struct completion stop;
0044     int running;
0045     struct timer_list timer;
0046     int queue;
0047     int default_ticks;
0048     unsigned long inuse;
0049     spinlock_t lock;
0050     struct pci_dev *sb_pdev;
0051     int base_reg;
0052 } rdc321x_wdt_device;
0053 
0054 /* generic helper functions */
0055 
0056 static void rdc321x_wdt_trigger(struct timer_list *unused)
0057 {
0058     unsigned long flags;
0059     u32 val;
0060 
0061     if (rdc321x_wdt_device.running)
0062         ticks--;
0063 
0064     /* keep watchdog alive */
0065     spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
0066     pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
0067                     rdc321x_wdt_device.base_reg, &val);
0068     val |= RDC_WDT_EN;
0069     pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
0070                     rdc321x_wdt_device.base_reg, val);
0071     spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
0072 
0073     /* requeue?? */
0074     if (rdc321x_wdt_device.queue && ticks)
0075         mod_timer(&rdc321x_wdt_device.timer,
0076                 jiffies + RDC_WDT_INTERVAL);
0077     else {
0078         /* ticks doesn't matter anyway */
0079         complete(&rdc321x_wdt_device.stop);
0080     }
0081 
0082 }
0083 
0084 static void rdc321x_wdt_reset(void)
0085 {
0086     ticks = rdc321x_wdt_device.default_ticks;
0087 }
0088 
0089 static void rdc321x_wdt_start(void)
0090 {
0091     unsigned long flags;
0092 
0093     if (!rdc321x_wdt_device.queue) {
0094         rdc321x_wdt_device.queue = 1;
0095 
0096         /* Clear the timer */
0097         spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
0098         pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
0099                 rdc321x_wdt_device.base_reg, RDC_CLS_TMR);
0100 
0101         /* Enable watchdog and set the timeout to 81.92 us */
0102         pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
0103                     rdc321x_wdt_device.base_reg,
0104                     RDC_WDT_EN | RDC_WDT_CNT);
0105         spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
0106 
0107         mod_timer(&rdc321x_wdt_device.timer,
0108                 jiffies + RDC_WDT_INTERVAL);
0109     }
0110 
0111     /* if process dies, counter is not decremented */
0112     rdc321x_wdt_device.running++;
0113 }
0114 
0115 static int rdc321x_wdt_stop(void)
0116 {
0117     if (rdc321x_wdt_device.running)
0118         rdc321x_wdt_device.running = 0;
0119 
0120     ticks = rdc321x_wdt_device.default_ticks;
0121 
0122     return -EIO;
0123 }
0124 
0125 /* filesystem operations */
0126 static int rdc321x_wdt_open(struct inode *inode, struct file *file)
0127 {
0128     if (test_and_set_bit(0, &rdc321x_wdt_device.inuse))
0129         return -EBUSY;
0130 
0131     return stream_open(inode, file);
0132 }
0133 
0134 static int rdc321x_wdt_release(struct inode *inode, struct file *file)
0135 {
0136     clear_bit(0, &rdc321x_wdt_device.inuse);
0137     return 0;
0138 }
0139 
0140 static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
0141                 unsigned long arg)
0142 {
0143     void __user *argp = (void __user *)arg;
0144     u32 value;
0145     static const struct watchdog_info ident = {
0146         .options = WDIOF_CARDRESET,
0147         .identity = "RDC321x WDT",
0148     };
0149     unsigned long flags;
0150 
0151     switch (cmd) {
0152     case WDIOC_KEEPALIVE:
0153         rdc321x_wdt_reset();
0154         break;
0155     case WDIOC_GETSTATUS:
0156         /* Read the value from the DATA register */
0157         spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
0158         pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
0159                     rdc321x_wdt_device.base_reg, &value);
0160         spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
0161         if (copy_to_user(argp, &value, sizeof(u32)))
0162             return -EFAULT;
0163         break;
0164     case WDIOC_GETSUPPORT:
0165         if (copy_to_user(argp, &ident, sizeof(ident)))
0166             return -EFAULT;
0167         break;
0168     case WDIOC_SETOPTIONS:
0169         if (copy_from_user(&value, argp, sizeof(int)))
0170             return -EFAULT;
0171         switch (value) {
0172         case WDIOS_ENABLECARD:
0173             rdc321x_wdt_start();
0174             break;
0175         case WDIOS_DISABLECARD:
0176             return rdc321x_wdt_stop();
0177         default:
0178             return -EINVAL;
0179         }
0180         break;
0181     default:
0182         return -ENOTTY;
0183     }
0184     return 0;
0185 }
0186 
0187 static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf,
0188                 size_t count, loff_t *ppos)
0189 {
0190     if (!count)
0191         return -EIO;
0192 
0193     rdc321x_wdt_reset();
0194 
0195     return count;
0196 }
0197 
0198 static const struct file_operations rdc321x_wdt_fops = {
0199     .owner      = THIS_MODULE,
0200     .llseek     = no_llseek,
0201     .unlocked_ioctl = rdc321x_wdt_ioctl,
0202     .compat_ioctl   = compat_ptr_ioctl,
0203     .open       = rdc321x_wdt_open,
0204     .write      = rdc321x_wdt_write,
0205     .release    = rdc321x_wdt_release,
0206 };
0207 
0208 static struct miscdevice rdc321x_wdt_misc = {
0209     .minor  = WATCHDOG_MINOR,
0210     .name   = "watchdog",
0211     .fops   = &rdc321x_wdt_fops,
0212 };
0213 
0214 static int rdc321x_wdt_probe(struct platform_device *pdev)
0215 {
0216     int err;
0217     struct resource *r;
0218     struct rdc321x_wdt_pdata *pdata;
0219 
0220     pdata = dev_get_platdata(&pdev->dev);
0221     if (!pdata) {
0222         dev_err(&pdev->dev, "no platform data supplied\n");
0223         return -ENODEV;
0224     }
0225 
0226     r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg");
0227     if (!r) {
0228         dev_err(&pdev->dev, "failed to get wdt-reg resource\n");
0229         return -ENODEV;
0230     }
0231 
0232     rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
0233     rdc321x_wdt_device.base_reg = r->start;
0234     rdc321x_wdt_device.queue = 0;
0235     rdc321x_wdt_device.default_ticks = ticks;
0236 
0237     err = misc_register(&rdc321x_wdt_misc);
0238     if (err < 0) {
0239         dev_err(&pdev->dev, "misc_register failed\n");
0240         return err;
0241     }
0242 
0243     spin_lock_init(&rdc321x_wdt_device.lock);
0244 
0245     /* Reset the watchdog */
0246     pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
0247                 rdc321x_wdt_device.base_reg, RDC_WDT_RST);
0248 
0249     init_completion(&rdc321x_wdt_device.stop);
0250 
0251     clear_bit(0, &rdc321x_wdt_device.inuse);
0252 
0253     timer_setup(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0);
0254 
0255     dev_info(&pdev->dev, "watchdog init success\n");
0256 
0257     return 0;
0258 }
0259 
0260 static int rdc321x_wdt_remove(struct platform_device *pdev)
0261 {
0262     if (rdc321x_wdt_device.queue) {
0263         rdc321x_wdt_device.queue = 0;
0264         wait_for_completion(&rdc321x_wdt_device.stop);
0265     }
0266 
0267     misc_deregister(&rdc321x_wdt_misc);
0268 
0269     return 0;
0270 }
0271 
0272 static struct platform_driver rdc321x_wdt_driver = {
0273     .probe = rdc321x_wdt_probe,
0274     .remove = rdc321x_wdt_remove,
0275     .driver = {
0276         .name = "rdc321x-wdt",
0277     },
0278 };
0279 
0280 module_platform_driver(rdc321x_wdt_driver);
0281 
0282 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
0283 MODULE_DESCRIPTION("RDC321x watchdog driver");
0284 MODULE_LICENSE("GPL");