0001
0002
0003
0004
0005
0006
0007
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
0027 #define RDC_WDT_EN 0x00800000
0028 #define RDC_WDT_WTI 0x00200000
0029 #define RDC_WDT_RST 0x00100000
0030 #define RDC_WDT_WIF 0x00040000
0031 #define RDC_WDT_IRT 0x00000100
0032 #define RDC_WDT_CNT 0x00000001
0033
0034 #define RDC_CLS_TMR 0x80003844
0035
0036 #define RDC_WDT_INTERVAL (HZ/10+1)
0037
0038 static int ticks = 1000;
0039
0040
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
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
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
0074 if (rdc321x_wdt_device.queue && ticks)
0075 mod_timer(&rdc321x_wdt_device.timer,
0076 jiffies + RDC_WDT_INTERVAL);
0077 else {
0078
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
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
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
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
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
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
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");