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/fs.h> 19#include <linux/init.h> 20#include <linux/kernel.h> 21#include <linux/miscdevice.h> 22#include <linux/platform_device.h> 23#include <linux/module.h> 24#include <linux/watchdog.h> 25#include <asm/io.h> 26#include <asm/uaccess.h> 27 28struct mpc83xx_wdt { 29 __be32 res0; 30 __be32 swcrr; /* System watchdog control register */ 31#define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */ 32#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */ 33#define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/ 34#define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */ 35 __be32 swcnr; /* System watchdog count register */ 36 u8 res1[2]; 37 __be16 swsrr; /* System watchdog service register */ 38 u8 res2[0xF0]; 39}; 40 41static struct mpc83xx_wdt __iomem *wd_base; 42 43static u16 timeout = 0xffff; 44module_param(timeout, ushort, 0); 45MODULE_PARM_DESC(timeout, "Watchdog timeout in ticks. (0<timeout<65536, default=65535"); 46 47static int reset = 1; 48module_param(reset, bool, 0); 49MODULE_PARM_DESC(reset, "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset"); 50 51/* 52 * We always prescale, but if someone really doesn't want to they can set this 53 * to 0 54 */ 55static int prescale = 1; 56static unsigned int timeout_sec; 57 58static unsigned long wdt_is_open; 59static spinlock_t wdt_spinlock; 60 61static void mpc83xx_wdt_keepalive(void) 62{ 63 /* Ping the WDT */ 64 spin_lock(&wdt_spinlock); 65 out_be16(&wd_base->swsrr, 0x556c); 66 out_be16(&wd_base->swsrr, 0xaa39); 67 spin_unlock(&wdt_spinlock); 68} 69 70static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf, 71 size_t count, loff_t *ppos) 72{ 73 if (count) 74 mpc83xx_wdt_keepalive(); 75 return count; 76} 77 78static int mpc83xx_wdt_open(struct inode *inode, struct file *file) 79{ 80 u32 tmp = SWCRR_SWEN; 81 if (test_and_set_bit(0, &wdt_is_open)) 82 return -EBUSY; 83 84 /* Once we start the watchdog we can't stop it */ 85 __module_get(THIS_MODULE); 86 87 /* Good, fire up the show */ 88 if (prescale) 89 tmp |= SWCRR_SWPR; 90 if (reset) 91 tmp |= SWCRR_SWRI; 92 93 tmp |= timeout << 16; 94 95 out_be32(&wd_base->swcrr, tmp); 96 97 return nonseekable_open(inode, file); 98} 99 100static int mpc83xx_wdt_release(struct inode *inode, struct file *file) 101{ 102 printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n"); 103 mpc83xx_wdt_keepalive(); 104 clear_bit(0, &wdt_is_open); 105 return 0; 106} 107 108static int mpc83xx_wdt_ioctl(struct inode *inode, struct file *file, 109 unsigned int cmd, unsigned long arg) 110{ 111 void __user *argp = (void __user *)arg; 112 int __user *p = argp; 113 static struct watchdog_info ident = { 114 .options = WDIOF_KEEPALIVEPING, 115 .firmware_version = 1, 116 .identity = "MPC83xx", 117 }; 118 119 switch (cmd) { 120 case WDIOC_GETSUPPORT: 121 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; 122 case WDIOC_KEEPALIVE: 123 mpc83xx_wdt_keepalive(); 124 return 0; 125 case WDIOC_GETTIMEOUT: 126 return put_user(timeout_sec, p); 127 default: 128 return -ENOTTY; 129 } 130} 131 132static const struct file_operations mpc83xx_wdt_fops = { 133 .owner = THIS_MODULE, 134 .llseek = no_llseek, 135 .write = mpc83xx_wdt_write, 136 .ioctl = mpc83xx_wdt_ioctl, 137 .open = mpc83xx_wdt_open, 138 .release = mpc83xx_wdt_release, 139}; 140 141static struct miscdevice mpc83xx_wdt_miscdev = { 142 .minor = WATCHDOG_MINOR, 143 .name = "watchdog", 144 .fops = &mpc83xx_wdt_fops, 145}; 146 147static int __devinit mpc83xx_wdt_probe(struct platform_device *dev) 148{ 149 struct resource *r; 150 int ret; 151 unsigned int *freq = dev->dev.platform_data; 152 153 /* get a pointer to the register memory */ 154 r = platform_get_resource(dev, IORESOURCE_MEM, 0); 155 156 if (!r) { 157 ret = -ENODEV; 158 goto err_out; 159 } 160 161 wd_base = ioremap(r->start, sizeof (struct mpc83xx_wdt)); 162 163 if (wd_base == NULL) { 164 ret = -ENOMEM; 165 goto err_out; 166 } 167 168 ret = misc_register(&mpc83xx_wdt_miscdev); 169 if (ret) { 170 printk(KERN_ERR "cannot register miscdev on minor=%d " 171 "(err=%d)\n", 172 WATCHDOG_MINOR, ret); 173 goto err_unmap; 174 } 175 176 /* Calculate the timeout in seconds */ 177 if (prescale) 178 timeout_sec = (timeout * 0x10000) / (*freq); 179 else 180 timeout_sec = timeout / (*freq); 181 182 printk(KERN_INFO "WDT driver for MPC83xx initialized. " 183 "mode:%s timeout=%d (%d seconds)\n", 184 reset ? "reset":"interrupt", timeout, timeout_sec); 185 186 spin_lock_init(&wdt_spinlock); 187 188 return 0; 189 190err_unmap: 191 iounmap(wd_base); 192err_out: 193 return ret; 194} 195 196static int __devexit mpc83xx_wdt_remove(struct platform_device *dev) 197{ 198 misc_deregister(&mpc83xx_wdt_miscdev); 199 iounmap(wd_base); 200 201 return 0; 202} 203 204static struct platform_driver mpc83xx_wdt_driver = { 205 .probe = mpc83xx_wdt_probe, 206 .remove = __devexit_p(mpc83xx_wdt_remove), 207 .driver = { 208 .name = "mpc83xx_wdt", 209 }, 210}; 211 212static int __init mpc83xx_wdt_init(void) 213{ 214 return platform_driver_register(&mpc83xx_wdt_driver); 215} 216 217static void __exit mpc83xx_wdt_exit(void) 218{ 219 platform_driver_unregister(&mpc83xx_wdt_driver); 220} 221 222module_init(mpc83xx_wdt_init); 223module_exit(mpc83xx_wdt_exit); 224 225MODULE_AUTHOR("Dave Updegraff, Kumar Gala"); 226MODULE_DESCRIPTION("Driver for watchdog timer in MPC83xx uProcessor"); 227MODULE_LICENSE("GPL"); 228MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 229