[ARM SMP] Add MPCore watchdog driver

Add platform independent parts of the ARM MPCore watchdog driver.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by Russell King and committed by Russell King b9d36b85 357d596b

+444
+9
drivers/char/watchdog/Kconfig
··· 139 139 To compile this driver as a module, choose M here: the 140 140 module will be called sa1100_wdt. 141 141 142 + config MPCORE_WATCHDOG 143 + tristate "MPcore watchdog" 144 + depends on WATCHDOG && ARM_MPCORE_PLATFORM && LOCAL_TIMERS 145 + help 146 + Watchdog timer embedded into the MPcore system. 147 + 148 + To compile this driver as a module, choose M here: the 149 + module will be called mpcore_wdt. 150 + 142 151 # X86 (i386 + ia64 + x86_64) Architecture 143 152 144 153 config ACQUIRE_WDT
+1
drivers/char/watchdog/Makefile
··· 29 29 obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o 30 30 obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o 31 31 obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o 32 + obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o 32 33 33 34 # X86 (i386 + ia64 + x86_64) Architecture 34 35 obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
+434
drivers/char/watchdog/mpcore_wdt.c
··· 1 + /* 2 + * Watchdog driver for the mpcore watchdog timer 3 + * 4 + * (c) Copyright 2004 ARM Limited 5 + * 6 + * Based on the SoftDog driver: 7 + * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. 8 + * http://www.redhat.com 9 + * 10 + * This program is free software; you can redistribute it and/or 11 + * modify it under the terms of the GNU General Public License 12 + * as published by the Free Software Foundation; either version 13 + * 2 of the License, or (at your option) any later version. 14 + * 15 + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 16 + * warranty for any of this software. This material is provided 17 + * "AS-IS" and at no charge. 18 + * 19 + * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> 20 + * 21 + */ 22 + #include <linux/module.h> 23 + #include <linux/moduleparam.h> 24 + #include <linux/config.h> 25 + #include <linux/types.h> 26 + #include <linux/miscdevice.h> 27 + #include <linux/watchdog.h> 28 + #include <linux/fs.h> 29 + #include <linux/reboot.h> 30 + #include <linux/init.h> 31 + #include <linux/interrupt.h> 32 + #include <linux/device.h> 33 + #include <asm/uaccess.h> 34 + 35 + struct mpcore_wdt { 36 + unsigned long timer_alive; 37 + struct device *dev; 38 + void __iomem *base; 39 + int irq; 40 + unsigned int perturb; 41 + char expect_close; 42 + }; 43 + 44 + static struct platform_device *mpcore_wdt_dev; 45 + 46 + extern unsigned int mpcore_timer_rate; 47 + 48 + #define TIMER_MARGIN 60 49 + static int mpcore_margin = TIMER_MARGIN; 50 + module_param(mpcore_margin, int, 0); 51 + MODULE_PARM_DESC(mpcore_margin, "MPcore timer margin in seconds. (0<mpcore_margin<65536, default=" __MODULE_STRING(TIMER_MARGIN) ")"); 52 + 53 + static int nowayout = WATCHDOG_NOWAYOUT; 54 + module_param(nowayout, int, 0); 55 + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 56 + 57 + #define ONLY_TESTING 0 58 + static int mpcore_noboot = ONLY_TESTING; 59 + module_param(mpcore_noboot, int, 0); 60 + MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, set to 1 to ignore reboots, 0 to reboot (default=" __MODULE_STRING(ONLY_TESTING) ")"); 61 + 62 + /* 63 + * This is the interrupt handler. Note that we only use this 64 + * in testing mode, so don't actually do a reboot here. 65 + */ 66 + static irqreturn_t mpcore_wdt_fire(int irq, void *arg, struct pt_regs *regs) 67 + { 68 + struct mpcore_wdt *wdt = arg; 69 + 70 + /* Check it really was our interrupt */ 71 + if (readl(wdt->base + TWD_WDOG_INTSTAT)) { 72 + dev_printk(KERN_CRIT, wdt->dev, "Triggered - Reboot ignored.\n"); 73 + 74 + /* Clear the interrupt on the watchdog */ 75 + writel(1, wdt->base + TWD_WDOG_INTSTAT); 76 + 77 + return IRQ_HANDLED; 78 + } 79 + 80 + return IRQ_NONE; 81 + } 82 + 83 + /* 84 + * mpcore_wdt_keepalive - reload the timer 85 + * 86 + * Note that the spec says a DIFFERENT value must be written to the reload 87 + * register each time. The "perturb" variable deals with this by adding 1 88 + * to the count every other time the function is called. 89 + */ 90 + static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt) 91 + { 92 + unsigned int count; 93 + 94 + /* Assume prescale is set to 256 */ 95 + count = (mpcore_timer_rate / 256) * mpcore_margin; 96 + 97 + /* Reload the counter */ 98 + writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD); 99 + 100 + wdt->perturb = wdt->perturb ? 0 : 1; 101 + } 102 + 103 + static void mpcore_wdt_stop(struct mpcore_wdt *wdt) 104 + { 105 + writel(0x12345678, wdt->base + TWD_WDOG_DISABLE); 106 + writel(0x87654321, wdt->base + TWD_WDOG_DISABLE); 107 + writel(0x0, wdt->base + TWD_WDOG_CONTROL); 108 + } 109 + 110 + static void mpcore_wdt_start(struct mpcore_wdt *wdt) 111 + { 112 + dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n"); 113 + 114 + /* This loads the count register but does NOT start the count yet */ 115 + mpcore_wdt_keepalive(wdt); 116 + 117 + if (mpcore_noboot) { 118 + /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */ 119 + writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL); 120 + } else { 121 + /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */ 122 + writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL); 123 + } 124 + } 125 + 126 + static int mpcore_wdt_set_heartbeat(int t) 127 + { 128 + if (t < 0x0001 || t > 0xFFFF) 129 + return -EINVAL; 130 + 131 + mpcore_margin = t; 132 + return 0; 133 + } 134 + 135 + /* 136 + * /dev/watchdog handling 137 + */ 138 + static int mpcore_wdt_open(struct inode *inode, struct file *file) 139 + { 140 + struct mpcore_wdt *wdt = dev_get_drvdata(&mpcore_wdt_dev->dev); 141 + 142 + if (test_and_set_bit(0, &wdt->timer_alive)) 143 + return -EBUSY; 144 + 145 + if (nowayout) 146 + __module_get(THIS_MODULE); 147 + 148 + file->private_data = wdt; 149 + 150 + /* 151 + * Activate timer 152 + */ 153 + mpcore_wdt_start(wdt); 154 + 155 + return nonseekable_open(inode, file); 156 + } 157 + 158 + static int mpcore_wdt_release(struct inode *inode, struct file *file) 159 + { 160 + struct mpcore_wdt *wdt = file->private_data; 161 + 162 + /* 163 + * Shut off the timer. 164 + * Lock it in if it's a module and we set nowayout 165 + */ 166 + if (wdt->expect_close == 42) { 167 + mpcore_wdt_stop(wdt); 168 + } else { 169 + dev_printk(KERN_CRIT, wdt->dev, "unexpected close, not stopping watchdog!\n"); 170 + mpcore_wdt_keepalive(wdt); 171 + } 172 + clear_bit(0, &wdt->timer_alive); 173 + wdt->expect_close = 0; 174 + return 0; 175 + } 176 + 177 + static ssize_t mpcore_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) 178 + { 179 + struct mpcore_wdt *wdt = file->private_data; 180 + 181 + /* Can't seek (pwrite) on this device */ 182 + if (ppos != &file->f_pos) 183 + return -ESPIPE; 184 + 185 + /* 186 + * Refresh the timer. 187 + */ 188 + if (len) { 189 + if (!nowayout) { 190 + size_t i; 191 + 192 + /* In case it was set long ago */ 193 + wdt->expect_close = 0; 194 + 195 + for (i = 0; i != len; i++) { 196 + char c; 197 + 198 + if (get_user(c, data + i)) 199 + return -EFAULT; 200 + if (c == 'V') 201 + wdt->expect_close = 42; 202 + } 203 + } 204 + mpcore_wdt_keepalive(wdt); 205 + } 206 + return len; 207 + } 208 + 209 + static struct watchdog_info ident = { 210 + .options = WDIOF_SETTIMEOUT | 211 + WDIOF_KEEPALIVEPING | 212 + WDIOF_MAGICCLOSE, 213 + .identity = "MPcore Watchdog", 214 + }; 215 + 216 + static int mpcore_wdt_ioctl(struct inode *inode, struct file *file, 217 + unsigned int cmd, unsigned long arg) 218 + { 219 + struct mpcore_wdt *wdt = file->private_data; 220 + int ret; 221 + union { 222 + struct watchdog_info ident; 223 + int i; 224 + } uarg; 225 + 226 + if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg)) 227 + return -ENOIOCTLCMD; 228 + 229 + if (_IOC_DIR(cmd) & _IOC_WRITE) { 230 + ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd)); 231 + if (ret) 232 + return -EFAULT; 233 + } 234 + 235 + switch (cmd) { 236 + case WDIOC_GETSUPPORT: 237 + uarg.ident = ident; 238 + ret = 0; 239 + break; 240 + 241 + case WDIOC_SETOPTIONS: 242 + ret = -EINVAL; 243 + if (uarg.i & WDIOS_DISABLECARD) { 244 + mpcore_wdt_stop(wdt); 245 + ret = 0; 246 + } 247 + if (uarg.i & WDIOS_ENABLECARD) { 248 + mpcore_wdt_start(wdt); 249 + ret = 0; 250 + } 251 + break; 252 + 253 + case WDIOC_GETSTATUS: 254 + case WDIOC_GETBOOTSTATUS: 255 + uarg.i = 0; 256 + ret = 0; 257 + break; 258 + 259 + case WDIOC_KEEPALIVE: 260 + mpcore_wdt_keepalive(wdt); 261 + ret = 0; 262 + break; 263 + 264 + case WDIOC_SETTIMEOUT: 265 + ret = mpcore_wdt_set_heartbeat(uarg.i); 266 + if (ret) 267 + break; 268 + 269 + mpcore_wdt_keepalive(wdt); 270 + /* Fall */ 271 + case WDIOC_GETTIMEOUT: 272 + uarg.i = mpcore_margin; 273 + ret = 0; 274 + break; 275 + 276 + default: 277 + return -ENOIOCTLCMD; 278 + } 279 + 280 + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { 281 + ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd)); 282 + if (ret) 283 + ret = -EFAULT; 284 + } 285 + return ret; 286 + } 287 + 288 + /* 289 + * System shutdown handler. Turn off the watchdog if we're 290 + * restarting or halting the system. 291 + */ 292 + static void mpcore_wdt_shutdown(struct device *_dev) 293 + { 294 + struct mpcore_wdt *wdt = dev_get_drvdata(_dev); 295 + 296 + if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT) 297 + mpcore_wdt_stop(wdt); 298 + } 299 + 300 + /* 301 + * Kernel Interfaces 302 + */ 303 + static struct file_operations mpcore_wdt_fops = { 304 + .owner = THIS_MODULE, 305 + .llseek = no_llseek, 306 + .write = mpcore_wdt_write, 307 + .ioctl = mpcore_wdt_ioctl, 308 + .open = mpcore_wdt_open, 309 + .release = mpcore_wdt_release, 310 + }; 311 + 312 + static struct miscdevice mpcore_wdt_miscdev = { 313 + .minor = WATCHDOG_MINOR, 314 + .name = "watchdog", 315 + .fops = &mpcore_wdt_fops, 316 + }; 317 + 318 + static int __devinit mpcore_wdt_probe(struct device *_dev) 319 + { 320 + struct platform_device *dev = to_platform_device(_dev); 321 + struct mpcore_wdt *wdt; 322 + struct resource *res; 323 + int ret; 324 + 325 + /* We only accept one device, and it must have an id of -1 */ 326 + if (dev->id != -1) 327 + return -ENODEV; 328 + 329 + res = platform_get_resource(dev, IORESOURCE_MEM, 0); 330 + if (!res) { 331 + ret = -ENODEV; 332 + goto err_out; 333 + } 334 + 335 + wdt = kmalloc(sizeof(struct mpcore_wdt), GFP_KERNEL); 336 + if (!wdt) { 337 + ret = -ENOMEM; 338 + goto err_out; 339 + } 340 + memset(wdt, 0, sizeof(struct mpcore_wdt)); 341 + 342 + wdt->dev = &dev->dev; 343 + wdt->irq = platform_get_irq(dev, 0); 344 + wdt->base = ioremap(res->start, res->end - res->start + 1); 345 + if (!wdt->base) { 346 + ret = -ENOMEM; 347 + goto err_free; 348 + } 349 + 350 + mpcore_wdt_miscdev.dev = &dev->dev; 351 + ret = misc_register(&mpcore_wdt_miscdev); 352 + if (ret) { 353 + dev_printk(KERN_ERR, _dev, "cannot register miscdev on minor=%d (err=%d)\n", 354 + WATCHDOG_MINOR, ret); 355 + goto err_misc; 356 + } 357 + 358 + ret = request_irq(wdt->irq, mpcore_wdt_fire, SA_INTERRUPT, "mpcore_wdt", wdt); 359 + if (ret) { 360 + dev_printk(KERN_ERR, _dev, "cannot register IRQ%d for watchdog\n", wdt->irq); 361 + goto err_irq; 362 + } 363 + 364 + mpcore_wdt_stop(wdt); 365 + dev_set_drvdata(&dev->dev, wdt); 366 + mpcore_wdt_dev = dev; 367 + 368 + return 0; 369 + 370 + err_irq: 371 + misc_deregister(&mpcore_wdt_miscdev); 372 + err_misc: 373 + iounmap(wdt->base); 374 + err_free: 375 + kfree(wdt); 376 + err_out: 377 + return ret; 378 + } 379 + 380 + static int __devexit mpcore_wdt_remove(struct device *dev) 381 + { 382 + struct mpcore_wdt *wdt = dev_get_drvdata(dev); 383 + 384 + dev_set_drvdata(dev, NULL); 385 + 386 + misc_deregister(&mpcore_wdt_miscdev); 387 + 388 + mpcore_wdt_dev = NULL; 389 + 390 + free_irq(wdt->irq, wdt); 391 + iounmap(wdt->base); 392 + kfree(wdt); 393 + return 0; 394 + } 395 + 396 + static struct device_driver mpcore_wdt_driver = { 397 + .name = "mpcore_wdt", 398 + .bus = &platform_bus_type, 399 + .probe = mpcore_wdt_probe, 400 + .remove = __devexit_p(mpcore_wdt_remove), 401 + .shutdown = mpcore_wdt_shutdown, 402 + }; 403 + 404 + static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n"; 405 + 406 + static int __init mpcore_wdt_init(void) 407 + { 408 + /* 409 + * Check that the margin value is within it's range; 410 + * if not reset to the default 411 + */ 412 + if (mpcore_wdt_set_heartbeat(mpcore_margin)) { 413 + mpcore_wdt_set_heartbeat(TIMER_MARGIN); 414 + printk(KERN_INFO "mpcore_margin value must be 0<mpcore_margin<65536, using %d\n", 415 + TIMER_MARGIN); 416 + } 417 + 418 + printk(banner, mpcore_noboot, mpcore_margin, nowayout); 419 + 420 + return driver_register(&mpcore_wdt_driver); 421 + } 422 + 423 + static void __exit mpcore_wdt_exit(void) 424 + { 425 + driver_unregister(&mpcore_wdt_driver); 426 + } 427 + 428 + module_init(mpcore_wdt_init); 429 + module_exit(mpcore_wdt_exit); 430 + 431 + MODULE_AUTHOR("ARM Limited"); 432 + MODULE_DESCRIPTION("MPcore Watchdog Device Driver"); 433 + MODULE_LICENSE("GPL"); 434 + MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);