Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: Add Xilinx watchdog timer driver

Watchdog timer device driver for Xilinx xps_timebase_wdt compatible ip cores.
It takes watchdog timer configuration from device tree and it needs that its
parent has defined the property "clock-frecuency".
It is compatible with watchdog timer kernel API, so user apps like watchdogd
may talk with it.

Signed-off-by: Alejandro Cabrera <aldaya@gmail.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Alejandro Cabrera and committed by
Wim Van Sebroeck
e9659e69 2fc5d52b

+450
+14
drivers/watchdog/Kconfig
··· 888 888 To compile this driver as a module, choose M here: the 889 889 module will be called m54xx_wdt. 890 890 891 + # MicroBlaze Architecture 892 + 893 + config XILINX_WATCHDOG 894 + tristate "Xilinx Watchdog timer" 895 + depends on MICROBLAZE 896 + ---help--- 897 + Watchdog driver for the xps_timebase_wdt ip core. 898 + 899 + IMPORTANT: The xps_timebase_wdt parent must have the property 900 + "clock-frequency" at device tree. 901 + 902 + To compile this driver as a module, choose M here: the 903 + module will be called of_xilinx_wdt. 904 + 891 905 # MIPS Architecture 892 906 893 907 config ATH79_WDT
+3
drivers/watchdog/Makefile
··· 110 110 # M68K Architecture 111 111 obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o 112 112 113 + # MicroBlaze Architecture 114 + obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o 115 + 113 116 # MIPS Architecture 114 117 obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o 115 118 obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
+433
drivers/watchdog/of_xilinx_wdt.c
··· 1 + /* 2 + * of_xilinx_wdt.c 1.01 A Watchdog Device Driver for Xilinx xps_timebase_wdt 3 + * 4 + * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>) 5 + * 6 + * ----------------------- 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License 10 + * as published by the Free Software Foundation; either version 11 + * 2 of the License, or (at your option) any later version. 12 + * 13 + * ----------------------- 14 + * 30-May-2011 Alejandro Cabrera <aldaya@gmail.com> 15 + * - If "xlnx,wdt-enable-once" wasn't found on device tree the 16 + * module will use CONFIG_WATCHDOG_NOWAYOUT 17 + * - If the device tree parameters ("clock-frequency" and 18 + * "xlnx,wdt-interval") wasn't found the driver won't 19 + * know the wdt reset interval 20 + */ 21 + 22 + #include <linux/module.h> 23 + #include <linux/types.h> 24 + #include <linux/kernel.h> 25 + #include <linux/fs.h> 26 + #include <linux/miscdevice.h> 27 + #include <linux/init.h> 28 + #include <linux/ioport.h> 29 + #include <linux/watchdog.h> 30 + #include <linux/io.h> 31 + #include <linux/uaccess.h> 32 + #include <linux/of.h> 33 + #include <linux/of_device.h> 34 + #include <linux/of_address.h> 35 + 36 + /* Register offsets for the Wdt device */ 37 + #define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */ 38 + #define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */ 39 + #define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */ 40 + 41 + /* Control/Status Register Masks */ 42 + #define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */ 43 + #define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */ 44 + #define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */ 45 + 46 + /* Control/Status Register 0/1 bits */ 47 + #define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */ 48 + 49 + /* SelfTest constants */ 50 + #define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000 51 + #define XWT_TIMER_FAILED 0xFFFFFFFF 52 + 53 + #define WATCHDOG_NAME "Xilinx Watchdog" 54 + #define PFX WATCHDOG_NAME ": " 55 + 56 + struct xwdt_device { 57 + struct resource res; 58 + void __iomem *base; 59 + u32 nowayout; 60 + u32 wdt_interval; 61 + u32 boot_status; 62 + }; 63 + 64 + static struct xwdt_device xdev; 65 + 66 + static u32 timeout; 67 + static u32 control_status_reg; 68 + static u8 expect_close; 69 + static u8 no_timeout; 70 + static unsigned long driver_open; 71 + 72 + static DEFINE_SPINLOCK(spinlock); 73 + 74 + static void xwdt_start(void) 75 + { 76 + spin_lock(&spinlock); 77 + 78 + /* Clean previous status and enable the watchdog timer */ 79 + control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); 80 + control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); 81 + 82 + iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK), 83 + xdev.base + XWT_TWCSR0_OFFSET); 84 + 85 + iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET); 86 + 87 + spin_unlock(&spinlock); 88 + } 89 + 90 + static void xwdt_stop(void) 91 + { 92 + spin_lock(&spinlock); 93 + 94 + control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); 95 + 96 + iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK), 97 + xdev.base + XWT_TWCSR0_OFFSET); 98 + 99 + iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET); 100 + 101 + spin_unlock(&spinlock); 102 + printk(KERN_INFO PFX "Stopped!\n"); 103 + } 104 + 105 + static void xwdt_keepalive(void) 106 + { 107 + spin_lock(&spinlock); 108 + 109 + control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); 110 + control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); 111 + iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET); 112 + 113 + spin_unlock(&spinlock); 114 + } 115 + 116 + static void xwdt_get_status(int *status) 117 + { 118 + int new_status; 119 + 120 + spin_lock(&spinlock); 121 + 122 + control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); 123 + new_status = ((control_status_reg & 124 + (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0); 125 + spin_unlock(&spinlock); 126 + 127 + *status = 0; 128 + if (new_status & 1) 129 + *status |= WDIOF_CARDRESET; 130 + } 131 + 132 + static u32 xwdt_selftest(void) 133 + { 134 + int i; 135 + u32 timer_value1; 136 + u32 timer_value2; 137 + 138 + spin_lock(&spinlock); 139 + 140 + timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET); 141 + timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); 142 + 143 + for (i = 0; 144 + ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) && 145 + (timer_value2 == timer_value1)); i++) { 146 + timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); 147 + } 148 + 149 + spin_unlock(&spinlock); 150 + 151 + if (timer_value2 != timer_value1) 152 + return ~XWT_TIMER_FAILED; 153 + else 154 + return XWT_TIMER_FAILED; 155 + } 156 + 157 + static int xwdt_open(struct inode *inode, struct file *file) 158 + { 159 + /* Only one process can handle the wdt at a time */ 160 + if (test_and_set_bit(0, &driver_open)) 161 + return -EBUSY; 162 + 163 + /* Make sure that the module are always loaded...*/ 164 + if (xdev.nowayout) 165 + __module_get(THIS_MODULE); 166 + 167 + xwdt_start(); 168 + printk(KERN_INFO PFX "Started...\n"); 169 + 170 + return nonseekable_open(inode, file); 171 + } 172 + 173 + static int xwdt_release(struct inode *inode, struct file *file) 174 + { 175 + if (expect_close == 42) { 176 + xwdt_stop(); 177 + } else { 178 + printk(KERN_CRIT PFX 179 + "Unexpected close, not stopping watchdog!\n"); 180 + xwdt_keepalive(); 181 + } 182 + 183 + clear_bit(0, &driver_open); 184 + expect_close = 0; 185 + return 0; 186 + } 187 + 188 + /* 189 + * xwdt_write: 190 + * @file: file handle to the watchdog 191 + * @buf: buffer to write (unused as data does not matter here 192 + * @count: count of bytes 193 + * @ppos: pointer to the position to write. No seeks allowed 194 + * 195 + * A write to a watchdog device is defined as a keepalive signal. Any 196 + * write of data will do, as we don't define content meaning. 197 + */ 198 + static ssize_t xwdt_write(struct file *file, const char __user *buf, 199 + size_t len, loff_t *ppos) 200 + { 201 + if (len) { 202 + if (!xdev.nowayout) { 203 + size_t i; 204 + 205 + /* In case it was set long ago */ 206 + expect_close = 0; 207 + 208 + for (i = 0; i != len; i++) { 209 + char c; 210 + 211 + if (get_user(c, buf + i)) 212 + return -EFAULT; 213 + if (c == 'V') 214 + expect_close = 42; 215 + } 216 + } 217 + xwdt_keepalive(); 218 + } 219 + return len; 220 + } 221 + 222 + static const struct watchdog_info ident = { 223 + .options = WDIOF_MAGICCLOSE | 224 + WDIOF_KEEPALIVEPING, 225 + .firmware_version = 1, 226 + .identity = WATCHDOG_NAME, 227 + }; 228 + 229 + /* 230 + * xwdt_ioctl: 231 + * @file: file handle to the device 232 + * @cmd: watchdog command 233 + * @arg: argument pointer 234 + * 235 + * The watchdog API defines a common set of functions for all watchdogs 236 + * according to their available features. 237 + */ 238 + static long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 239 + { 240 + int status; 241 + 242 + union { 243 + struct watchdog_info __user *ident; 244 + int __user *i; 245 + } uarg; 246 + 247 + uarg.i = (int __user *)arg; 248 + 249 + switch (cmd) { 250 + case WDIOC_GETSUPPORT: 251 + return copy_to_user(uarg.ident, &ident, 252 + sizeof(ident)) ? -EFAULT : 0; 253 + 254 + case WDIOC_GETBOOTSTATUS: 255 + return put_user(xdev.boot_status, uarg.i); 256 + 257 + case WDIOC_GETSTATUS: 258 + xwdt_get_status(&status); 259 + return put_user(status, uarg.i); 260 + 261 + case WDIOC_KEEPALIVE: 262 + xwdt_keepalive(); 263 + return 0; 264 + 265 + case WDIOC_GETTIMEOUT: 266 + if (no_timeout) 267 + return -ENOTTY; 268 + else 269 + return put_user(timeout, uarg.i); 270 + 271 + default: 272 + return -ENOTTY; 273 + } 274 + } 275 + 276 + static const struct file_operations xwdt_fops = { 277 + .owner = THIS_MODULE, 278 + .llseek = no_llseek, 279 + .write = xwdt_write, 280 + .open = xwdt_open, 281 + .release = xwdt_release, 282 + .unlocked_ioctl = xwdt_ioctl, 283 + }; 284 + 285 + static struct miscdevice xwdt_miscdev = { 286 + .minor = WATCHDOG_MINOR, 287 + .name = "watchdog", 288 + .fops = &xwdt_fops, 289 + }; 290 + 291 + static int __devinit xwdt_probe(struct platform_device *pdev) 292 + { 293 + int rc; 294 + u32 *tmptr; 295 + u32 *pfreq; 296 + 297 + no_timeout = 0; 298 + 299 + pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent, 300 + "clock-frequency", NULL); 301 + 302 + if (pfreq == NULL) { 303 + printk(KERN_WARNING PFX 304 + "The watchdog clock frequency cannot be obtained!\n"); 305 + no_timeout = 1; 306 + } 307 + 308 + rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res); 309 + if (rc) { 310 + printk(KERN_WARNING PFX "invalid address!\n"); 311 + return rc; 312 + } 313 + 314 + tmptr = (u32 *)of_get_property(pdev->dev.of_node, 315 + "xlnx,wdt-interval", NULL); 316 + if (tmptr == NULL) { 317 + printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-interval\"" 318 + " not found in device tree!\n"); 319 + no_timeout = 1; 320 + } else { 321 + xdev.wdt_interval = *tmptr; 322 + } 323 + 324 + tmptr = (u32 *)of_get_property(pdev->dev.of_node, 325 + "xlnx,wdt-enable-once", NULL); 326 + if (tmptr == NULL) { 327 + printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-enable-once\"" 328 + " not found in device tree!\n"); 329 + xdev.nowayout = WATCHDOG_NOWAYOUT; 330 + } 331 + 332 + /* 333 + * Twice of the 2^wdt_interval / freq because the first wdt overflow is 334 + * ignored (interrupt), reset is only generated at second wdt overflow 335 + */ 336 + if (!no_timeout) 337 + timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq); 338 + 339 + if (!request_mem_region(xdev.res.start, 340 + xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) { 341 + rc = -ENXIO; 342 + printk(KERN_ERR PFX "memory request failure!\n"); 343 + goto err_out; 344 + } 345 + 346 + xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1); 347 + if (xdev.base == NULL) { 348 + rc = -ENOMEM; 349 + printk(KERN_ERR PFX "ioremap failure!\n"); 350 + goto release_mem; 351 + } 352 + 353 + rc = xwdt_selftest(); 354 + if (rc == XWT_TIMER_FAILED) { 355 + printk(KERN_ERR PFX "SelfTest routine error!\n"); 356 + goto unmap_io; 357 + } 358 + 359 + xwdt_get_status(&xdev.boot_status); 360 + 361 + rc = misc_register(&xwdt_miscdev); 362 + if (rc) { 363 + printk(KERN_ERR PFX 364 + "cannot register miscdev on minor=%d (err=%d)\n", 365 + xwdt_miscdev.minor, rc); 366 + goto unmap_io; 367 + } 368 + 369 + if (no_timeout) 370 + printk(KERN_INFO PFX 371 + "driver loaded (timeout=? sec, nowayout=%d)\n", 372 + xdev.nowayout); 373 + else 374 + printk(KERN_INFO PFX 375 + "driver loaded (timeout=%d sec, nowayout=%d)\n", 376 + timeout, xdev.nowayout); 377 + 378 + expect_close = 0; 379 + clear_bit(0, &driver_open); 380 + 381 + return 0; 382 + 383 + unmap_io: 384 + iounmap(xdev.base); 385 + release_mem: 386 + release_mem_region(xdev.res.start, resource_size(&xdev.res)); 387 + err_out: 388 + return rc; 389 + } 390 + 391 + static int __devexit xwdt_remove(struct platform_device *dev) 392 + { 393 + misc_deregister(&xwdt_miscdev); 394 + iounmap(xdev.base); 395 + release_mem_region(xdev.res.start, resource_size(&xdev.res)); 396 + 397 + return 0; 398 + } 399 + 400 + /* Match table for of_platform binding */ 401 + static struct of_device_id __devinitdata xwdt_of_match[] = { 402 + { .compatible = "xlnx,xps-timebase-wdt-1.01.a", }, 403 + {}, 404 + }; 405 + MODULE_DEVICE_TABLE(of, xwdt_of_match); 406 + 407 + static struct platform_driver xwdt_driver = { 408 + .probe = xwdt_probe, 409 + .remove = __devexit_p(xwdt_remove), 410 + .driver = { 411 + .owner = THIS_MODULE, 412 + .name = WATCHDOG_NAME, 413 + .of_match_table = xwdt_of_match, 414 + }, 415 + }; 416 + 417 + static int __init xwdt_init(void) 418 + { 419 + return platform_driver_register(&xwdt_driver); 420 + } 421 + 422 + static void __exit xwdt_exit(void) 423 + { 424 + platform_driver_unregister(&xwdt_driver); 425 + } 426 + 427 + module_init(xwdt_init); 428 + module_exit(xwdt_exit); 429 + 430 + MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>"); 431 + MODULE_DESCRIPTION("Xilinx Watchdog driver"); 432 + MODULE_LICENSE("GPL"); 433 + MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);