1 /* 2 * RDC321x watchdog driver 3 * 4 * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> 5 * 6 * This driver is highly inspired from the cpu5_wdt driver 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 * 22 */ 23 24 #include <linux/module.h> 25 #include <linux/moduleparam.h> 26 #include <linux/types.h> 27 #include <linux/errno.h> 28 #include <linux/miscdevice.h> 29 #include <linux/fs.h> 30 #include <linux/init.h> 31 #include <linux/ioport.h> 32 #include <linux/timer.h> 33 #include <linux/completion.h> 34 #include <linux/jiffies.h> 35 #include <linux/platform_device.h> 36 #include <linux/watchdog.h> 37 #include <linux/io.h> 38 #include <linux/uaccess.h> 39 #include <linux/mfd/rdc321x.h> 40 41 #define RDC_WDT_MASK 0x80000000 /* Mask */ 42 #define RDC_WDT_EN 0x00800000 /* Enable bit */ 43 #define RDC_WDT_WTI 0x00200000 /* Generate CPU reset/NMI/WDT on timeout */ 44 #define RDC_WDT_RST 0x00100000 /* Reset bit */ 45 #define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */ 46 #define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */ 47 #define RDC_WDT_CNT 0x00000001 /* WDT count */ 48 49 #define RDC_CLS_TMR 0x80003844 /* Clear timer */ 50 51 #define RDC_WDT_INTERVAL (HZ/10+1) 52 53 static int ticks = 1000; 54 55 /* some device data */ 56 57 static struct { 58 struct completion stop; 59 int running; 60 struct timer_list timer; 61 int queue; 62 int default_ticks; 63 unsigned long inuse; 64 spinlock_t lock; 65 struct pci_dev *sb_pdev; 66 int base_reg; 67 } rdc321x_wdt_device; 68 69 /* generic helper functions */ 70 71 static void rdc321x_wdt_trigger(unsigned long unused) 72 { 73 unsigned long flags; 74 u32 val; 75 76 if (rdc321x_wdt_device.running) 77 ticks--; 78 79 /* keep watchdog alive */ 80 spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); 81 pci_read_config_dword(rdc321x_wdt_device.sb_pdev, 82 rdc321x_wdt_device.base_reg, &val); 83 val |= RDC_WDT_EN; 84 pci_write_config_dword(rdc321x_wdt_device.sb_pdev, 85 rdc321x_wdt_device.base_reg, val); 86 spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); 87 88 /* requeue?? */ 89 if (rdc321x_wdt_device.queue && ticks) 90 mod_timer(&rdc321x_wdt_device.timer, 91 jiffies + RDC_WDT_INTERVAL); 92 else { 93 /* ticks doesn't matter anyway */ 94 complete(&rdc321x_wdt_device.stop); 95 } 96 97 } 98 99 static void rdc321x_wdt_reset(void) 100 { 101 ticks = rdc321x_wdt_device.default_ticks; 102 } 103 104 static void rdc321x_wdt_start(void) 105 { 106 unsigned long flags; 107 108 if (!rdc321x_wdt_device.queue) { 109 rdc321x_wdt_device.queue = 1; 110 111 /* Clear the timer */ 112 spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); 113 pci_write_config_dword(rdc321x_wdt_device.sb_pdev, 114 rdc321x_wdt_device.base_reg, RDC_CLS_TMR); 115 116 /* Enable watchdog and set the timeout to 81.92 us */ 117 pci_write_config_dword(rdc321x_wdt_device.sb_pdev, 118 rdc321x_wdt_device.base_reg, 119 RDC_WDT_EN | RDC_WDT_CNT); 120 spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); 121 122 mod_timer(&rdc321x_wdt_device.timer, 123 jiffies + RDC_WDT_INTERVAL); 124 } 125 126 /* if process dies, counter is not decremented */ 127 rdc321x_wdt_device.running++; 128 } 129 130 static int rdc321x_wdt_stop(void) 131 { 132 if (rdc321x_wdt_device.running) 133 rdc321x_wdt_device.running = 0; 134 135 ticks = rdc321x_wdt_device.default_ticks; 136 137 return -EIO; 138 } 139 140 /* filesystem operations */ 141 static int rdc321x_wdt_open(struct inode *inode, struct file *file) 142 { 143 if (test_and_set_bit(0, &rdc321x_wdt_device.inuse)) 144 return -EBUSY; 145 146 return nonseekable_open(inode, file); 147 } 148 149 static int rdc321x_wdt_release(struct inode *inode, struct file *file) 150 { 151 clear_bit(0, &rdc321x_wdt_device.inuse); 152 return 0; 153 } 154 155 static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd, 156 unsigned long arg) 157 { 158 void __user *argp = (void __user *)arg; 159 u32 value; 160 static const struct watchdog_info ident = { 161 .options = WDIOF_CARDRESET, 162 .identity = "RDC321x WDT", 163 }; 164 unsigned long flags; 165 166 switch (cmd) { 167 case WDIOC_KEEPALIVE: 168 rdc321x_wdt_reset(); 169 break; 170 case WDIOC_GETSTATUS: 171 /* Read the value from the DATA register */ 172 spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); 173 pci_read_config_dword(rdc321x_wdt_device.sb_pdev, 174 rdc321x_wdt_device.base_reg, &value); 175 spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); 176 if (copy_to_user(argp, &value, sizeof(u32))) 177 return -EFAULT; 178 break; 179 case WDIOC_GETSUPPORT: 180 if (copy_to_user(argp, &ident, sizeof(ident))) 181 return -EFAULT; 182 break; 183 case WDIOC_SETOPTIONS: 184 if (copy_from_user(&value, argp, sizeof(int))) 185 return -EFAULT; 186 switch (value) { 187 case WDIOS_ENABLECARD: 188 rdc321x_wdt_start(); 189 break; 190 case WDIOS_DISABLECARD: 191 return rdc321x_wdt_stop(); 192 default: 193 return -EINVAL; 194 } 195 break; 196 default: 197 return -ENOTTY; 198 } 199 return 0; 200 } 201 202 static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf, 203 size_t count, loff_t *ppos) 204 { 205 if (!count) 206 return -EIO; 207 208 rdc321x_wdt_reset(); 209 210 return count; 211 } 212 213 static const struct file_operations rdc321x_wdt_fops = { 214 .owner = THIS_MODULE, 215 .llseek = no_llseek, 216 .unlocked_ioctl = rdc321x_wdt_ioctl, 217 .open = rdc321x_wdt_open, 218 .write = rdc321x_wdt_write, 219 .release = rdc321x_wdt_release, 220 }; 221 222 static struct miscdevice rdc321x_wdt_misc = { 223 .minor = WATCHDOG_MINOR, 224 .name = "watchdog", 225 .fops = &rdc321x_wdt_fops, 226 }; 227 228 static int rdc321x_wdt_probe(struct platform_device *pdev) 229 { 230 int err; 231 struct resource *r; 232 struct rdc321x_wdt_pdata *pdata; 233 234 pdata = pdev->dev.platform_data; 235 if (!pdata) { 236 dev_err(&pdev->dev, "no platform data supplied\n"); 237 return -ENODEV; 238 } 239 240 r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg"); 241 if (!r) { 242 dev_err(&pdev->dev, "failed to get wdt-reg resource\n"); 243 return -ENODEV; 244 } 245 246 rdc321x_wdt_device.sb_pdev = pdata->sb_pdev; 247 rdc321x_wdt_device.base_reg = r->start; 248 249 err = misc_register(&rdc321x_wdt_misc); 250 if (err < 0) { 251 dev_err(&pdev->dev, "misc_register failed\n"); 252 return err; 253 } 254 255 spin_lock_init(&rdc321x_wdt_device.lock); 256 257 /* Reset the watchdog */ 258 pci_write_config_dword(rdc321x_wdt_device.sb_pdev, 259 rdc321x_wdt_device.base_reg, RDC_WDT_RST); 260 261 init_completion(&rdc321x_wdt_device.stop); 262 rdc321x_wdt_device.queue = 0; 263 264 clear_bit(0, &rdc321x_wdt_device.inuse); 265 266 setup_timer(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0); 267 268 rdc321x_wdt_device.default_ticks = ticks; 269 270 dev_info(&pdev->dev, "watchdog init success\n"); 271 272 return 0; 273 } 274 275 static int rdc321x_wdt_remove(struct platform_device *pdev) 276 { 277 if (rdc321x_wdt_device.queue) { 278 rdc321x_wdt_device.queue = 0; 279 wait_for_completion(&rdc321x_wdt_device.stop); 280 } 281 282 misc_deregister(&rdc321x_wdt_misc); 283 284 return 0; 285 } 286 287 static struct platform_driver rdc321x_wdt_driver = { 288 .probe = rdc321x_wdt_probe, 289 .remove = rdc321x_wdt_remove, 290 .driver = { 291 .owner = THIS_MODULE, 292 .name = "rdc321x-wdt", 293 }, 294 }; 295 296 module_platform_driver(rdc321x_wdt_driver); 297 298 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 299 MODULE_DESCRIPTION("RDC321x watchdog driver"); 300 MODULE_LICENSE("GPL"); 301 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 302