[PATCH] powerpc: Add support for the MPC83xx watchdog

Add support for the PowerPC MPC83xx watchdog. The MPC83xx has a simple
watchdog that once enabled it can not be stopped, has some simple timeout
range selection, and the ability to either reset the processor or take a
machine check.

Signed-off-by: Dave Updegraff <dave@cray.org>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Cc: Wim Van Sebroeck <wim@iguana.be>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Kumar Gala and committed by Linus Torvalds fabbfb9e 7339ff83

+234
+4
drivers/char/watchdog/Kconfig
··· 423 423 tristate "MPC8xx Watchdog Timer" 424 424 depends on WATCHDOG && 8xx 425 425 426 + config 83xx_WDT 427 + tristate "MPC83xx Watchdog Timer" 428 + depends on WATCHDOG && PPC_83xx 429 + 426 430 config MV64X60_WDT 427 431 tristate "MV64X60 (Marvell Discovery) Watchdog Timer" 428 432 depends on WATCHDOG && MV64X60
+1
drivers/char/watchdog/Makefile
··· 56 56 57 57 # PowerPC Architecture 58 58 obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o 59 + obj-$(CONFIG_83xx_WDT) += mpc83xx_wdt.o 59 60 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o 60 61 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o 61 62
+229
drivers/char/watchdog/mpc83xx_wdt.c
··· 1 + /* 2 + * mpc83xx_wdt.c - MPC83xx watchdog userspace interface 3 + * 4 + * Authors: Dave Updegraff <dave@cray.org> 5 + * Kumar Gala <galak@kernel.crashing.org> 6 + * Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org> 7 + * ..and from sc520_wdt 8 + * 9 + * Note: it appears that you can only actually ENABLE or DISABLE the thing 10 + * once after POR. Once enabled, you cannot disable, and vice versa. 11 + * 12 + * This program is free software; you can redistribute it and/or modify it 13 + * under the terms of the GNU General Public License as published by the 14 + * Free Software Foundation; either version 2 of the License, or (at your 15 + * option) any later version. 16 + */ 17 + 18 + #include <linux/config.h> 19 + #include <linux/fs.h> 20 + #include <linux/init.h> 21 + #include <linux/kernel.h> 22 + #include <linux/miscdevice.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/module.h> 25 + #include <linux/watchdog.h> 26 + #include <asm/io.h> 27 + #include <asm/uaccess.h> 28 + 29 + struct mpc83xx_wdt { 30 + __be32 res0; 31 + __be32 swcrr; /* System watchdog control register */ 32 + #define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */ 33 + #define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */ 34 + #define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/ 35 + #define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */ 36 + __be32 swcnr; /* System watchdog count register */ 37 + u8 res1[2]; 38 + __be16 swsrr; /* System watchdog service register */ 39 + u8 res2[0xF0]; 40 + }; 41 + 42 + static struct mpc83xx_wdt __iomem *wd_base; 43 + 44 + static u16 timeout = 0xffff; 45 + module_param(timeout, ushort, 0); 46 + MODULE_PARM_DESC(timeout, "Watchdog timeout in ticks. (0<timeout<65536, default=65535"); 47 + 48 + static int reset = 1; 49 + module_param(reset, bool, 0); 50 + MODULE_PARM_DESC(reset, "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset"); 51 + 52 + /* 53 + * We always prescale, but if someone really doesn't want to they can set this 54 + * to 0 55 + */ 56 + static int prescale = 1; 57 + static unsigned int timeout_sec; 58 + 59 + static unsigned long wdt_is_open; 60 + static spinlock_t wdt_spinlock; 61 + 62 + static void mpc83xx_wdt_keepalive(void) 63 + { 64 + /* Ping the WDT */ 65 + spin_lock(&wdt_spinlock); 66 + out_be16(&wd_base->swsrr, 0x556c); 67 + out_be16(&wd_base->swsrr, 0xaa39); 68 + spin_unlock(&wdt_spinlock); 69 + } 70 + 71 + static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf, 72 + size_t count, loff_t *ppos) 73 + { 74 + if (count) 75 + mpc83xx_wdt_keepalive(); 76 + return count; 77 + } 78 + 79 + static int mpc83xx_wdt_open(struct inode *inode, struct file *file) 80 + { 81 + u32 tmp = SWCRR_SWEN; 82 + if (test_and_set_bit(0, &wdt_is_open)) 83 + return -EBUSY; 84 + 85 + /* Once we start the watchdog we can't stop it */ 86 + __module_get(THIS_MODULE); 87 + 88 + /* Good, fire up the show */ 89 + if (prescale) 90 + tmp |= SWCRR_SWPR; 91 + if (reset) 92 + tmp |= SWCRR_SWRI; 93 + 94 + tmp |= timeout << 16; 95 + 96 + out_be32(&wd_base->swcrr, tmp); 97 + 98 + return nonseekable_open(inode, file); 99 + } 100 + 101 + static int mpc83xx_wdt_release(struct inode *inode, struct file *file) 102 + { 103 + printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n"); 104 + mpc83xx_wdt_keepalive(); 105 + clear_bit(0, &wdt_is_open); 106 + return 0; 107 + } 108 + 109 + static int mpc83xx_wdt_ioctl(struct inode *inode, struct file *file, 110 + unsigned int cmd, unsigned long arg) 111 + { 112 + void __user *argp = (void __user *)arg; 113 + int __user *p = argp; 114 + static struct watchdog_info ident = { 115 + .options = WDIOF_KEEPALIVEPING, 116 + .firmware_version = 1, 117 + .identity = "MPC83xx", 118 + }; 119 + 120 + switch (cmd) { 121 + case WDIOC_GETSUPPORT: 122 + return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; 123 + case WDIOC_KEEPALIVE: 124 + mpc83xx_wdt_keepalive(); 125 + return 0; 126 + case WDIOC_GETTIMEOUT: 127 + return put_user(timeout_sec, p); 128 + default: 129 + return -ENOIOCTLCMD; 130 + } 131 + } 132 + 133 + static struct file_operations mpc83xx_wdt_fops = { 134 + .owner = THIS_MODULE, 135 + .llseek = no_llseek, 136 + .write = mpc83xx_wdt_write, 137 + .ioctl = mpc83xx_wdt_ioctl, 138 + .open = mpc83xx_wdt_open, 139 + .release = mpc83xx_wdt_release, 140 + }; 141 + 142 + static struct miscdevice mpc83xx_wdt_miscdev = { 143 + .minor = WATCHDOG_MINOR, 144 + .name = "watchdog", 145 + .fops = &mpc83xx_wdt_fops, 146 + }; 147 + 148 + static int __devinit mpc83xx_wdt_probe(struct platform_device *dev) 149 + { 150 + struct resource *r; 151 + int ret; 152 + unsigned int *freq = dev->dev.platform_data; 153 + 154 + /* get a pointer to the register memory */ 155 + r = platform_get_resource(dev, IORESOURCE_MEM, 0); 156 + 157 + if (!r) { 158 + ret = -ENODEV; 159 + goto err_out; 160 + } 161 + 162 + wd_base = ioremap(r->start, sizeof (struct mpc83xx_wdt)); 163 + 164 + if (wd_base == NULL) { 165 + ret = -ENOMEM; 166 + goto err_out; 167 + } 168 + 169 + ret = misc_register(&mpc83xx_wdt_miscdev); 170 + if (ret) { 171 + printk(KERN_ERR "cannot register miscdev on minor=%d " 172 + "(err=%d)\n", 173 + WATCHDOG_MINOR, ret); 174 + goto err_unmap; 175 + } 176 + 177 + /* Calculate the timeout in seconds */ 178 + if (prescale) 179 + timeout_sec = (timeout * 0x10000) / (*freq); 180 + else 181 + timeout_sec = timeout / (*freq); 182 + 183 + printk(KERN_INFO "WDT driver for MPC83xx initialized. " 184 + "mode:%s timeout=%d (%d seconds)\n", 185 + reset ? "reset":"interrupt", timeout, timeout_sec); 186 + 187 + spin_lock_init(&wdt_spinlock); 188 + 189 + return 0; 190 + 191 + err_unmap: 192 + iounmap(wd_base); 193 + err_out: 194 + return ret; 195 + } 196 + 197 + static int __devexit mpc83xx_wdt_remove(struct platform_device *dev) 198 + { 199 + misc_deregister(&mpc83xx_wdt_miscdev); 200 + iounmap(wd_base); 201 + 202 + return 0; 203 + } 204 + 205 + static struct platform_driver mpc83xx_wdt_driver = { 206 + .probe = mpc83xx_wdt_probe, 207 + .remove = __devexit_p(mpc83xx_wdt_remove), 208 + .driver = { 209 + .name = "mpc83xx_wdt", 210 + }, 211 + }; 212 + 213 + static int __init mpc83xx_wdt_init(void) 214 + { 215 + return platform_driver_register(&mpc83xx_wdt_driver); 216 + } 217 + 218 + static void __exit mpc83xx_wdt_exit(void) 219 + { 220 + platform_driver_unregister(&mpc83xx_wdt_driver); 221 + } 222 + 223 + module_init(mpc83xx_wdt_init); 224 + module_exit(mpc83xx_wdt_exit); 225 + 226 + MODULE_AUTHOR("Dave Updegraff, Kumar Gala"); 227 + MODULE_DESCRIPTION("Driver for watchdog timer in MPC83xx uProcessor"); 228 + MODULE_LICENSE("GPL"); 229 + MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);