1/* 2 * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface 3 * 4 * Author: James Chapman <jchapman@katalix.com> 5 * 6 * Platform-specific setup code should configure the dog to generate 7 * interrupt or reset as required. This code only enables/disables 8 * and services the watchdog. 9 * 10 * Derived from mpc8xx_wdt.c, with the following copyright. 11 * 12 * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under 13 * the terms of the GNU General Public License version 2. This program 14 * is licensed "as is" without any warranty of any kind, whether express 15 * or implied. 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/module.h> 23#include <linux/watchdog.h> 24#include <linux/platform_device.h> 25 26#include <asm/mv64x60.h> 27#include <asm/uaccess.h> 28#include <asm/io.h> 29 30/* MV64x60 WDC (config) register access definitions */ 31#define MV64x60_WDC_CTL1_MASK (3 << 24) 32#define MV64x60_WDC_CTL1(val) ((val & 3) << 24) 33#define MV64x60_WDC_CTL2_MASK (3 << 26) 34#define MV64x60_WDC_CTL2(val) ((val & 3) << 26) 35 36/* Flags bits */ 37#define MV64x60_WDOG_FLAG_OPENED 0 38#define MV64x60_WDOG_FLAG_ENABLED 1 39 40static unsigned long wdt_flags; 41static int wdt_status; 42static void __iomem *mv64x60_regs; 43static int mv64x60_wdt_timeout; 44 45static void mv64x60_wdt_reg_write(u32 val) 46{ 47 /* Allow write only to CTL1 / CTL2 fields, retaining values in 48 * other fields. 49 */ 50 u32 data = readl(mv64x60_regs + MV64x60_WDT_WDC); 51 data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK); 52 data |= val; 53 writel(data, mv64x60_regs + MV64x60_WDT_WDC); 54} 55 56static void mv64x60_wdt_service(void) 57{ 58 /* Write 01 followed by 10 to CTL2 */ 59 mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x01)); 60 mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x02)); 61} 62 63static void mv64x60_wdt_handler_disable(void) 64{ 65 if (test_and_clear_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) { 66 /* Write 01 followed by 10 to CTL1 */ 67 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01)); 68 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02)); 69 printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n"); 70 } 71} 72 73static void mv64x60_wdt_handler_enable(void) 74{ 75 if (!test_and_set_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) { 76 /* Write 01 followed by 10 to CTL1 */ 77 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01)); 78 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02)); 79 printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n"); 80 } 81} 82 83static int mv64x60_wdt_open(struct inode *inode, struct file *file) 84{ 85 if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags)) 86 return -EBUSY; 87 88 mv64x60_wdt_service(); 89 mv64x60_wdt_handler_enable(); 90 91 nonseekable_open(inode, file); 92 93 return 0; 94} 95 96static int mv64x60_wdt_release(struct inode *inode, struct file *file) 97{ 98 mv64x60_wdt_service(); 99 100#if !defined(CONFIG_WATCHDOG_NOWAYOUT) 101 mv64x60_wdt_handler_disable(); 102#endif 103 104 clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags); 105 106 return 0; 107} 108 109static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data, 110 size_t len, loff_t * ppos) 111{ 112 if (len) 113 mv64x60_wdt_service(); 114 115 return len; 116} 117 118static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file, 119 unsigned int cmd, unsigned long arg) 120{ 121 int timeout; 122 void __user *argp = (void __user *)arg; 123 static struct watchdog_info info = { 124 .options = WDIOF_KEEPALIVEPING, 125 .firmware_version = 0, 126 .identity = "MV64x60 watchdog", 127 }; 128 129 switch (cmd) { 130 case WDIOC_GETSUPPORT: 131 if (copy_to_user(argp, &info, sizeof(info))) 132 return -EFAULT; 133 break; 134 135 case WDIOC_GETSTATUS: 136 case WDIOC_GETBOOTSTATUS: 137 if (put_user(wdt_status, (int __user *)argp)) 138 return -EFAULT; 139 wdt_status &= ~WDIOF_KEEPALIVEPING; 140 break; 141 142 case WDIOC_GETTEMP: 143 return -EOPNOTSUPP; 144 145 case WDIOC_SETOPTIONS: 146 return -EOPNOTSUPP; 147 148 case WDIOC_KEEPALIVE: 149 mv64x60_wdt_service(); 150 wdt_status |= WDIOF_KEEPALIVEPING; 151 break; 152 153 case WDIOC_SETTIMEOUT: 154 return -EOPNOTSUPP; 155 156 case WDIOC_GETTIMEOUT: 157 timeout = mv64x60_wdt_timeout * HZ; 158 if (put_user(timeout, (int __user *)argp)) 159 return -EFAULT; 160 break; 161 162 default: 163 return -ENOTTY; 164 } 165 166 return 0; 167} 168 169static const struct file_operations mv64x60_wdt_fops = { 170 .owner = THIS_MODULE, 171 .llseek = no_llseek, 172 .write = mv64x60_wdt_write, 173 .ioctl = mv64x60_wdt_ioctl, 174 .open = mv64x60_wdt_open, 175 .release = mv64x60_wdt_release, 176}; 177 178static struct miscdevice mv64x60_wdt_miscdev = { 179 .minor = WATCHDOG_MINOR, 180 .name = "watchdog", 181 .fops = &mv64x60_wdt_fops, 182}; 183 184static int __devinit mv64x60_wdt_probe(struct platform_device *dev) 185{ 186 struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data; 187 int bus_clk = 133; 188 189 mv64x60_wdt_timeout = 10; 190 if (pdata) { 191 mv64x60_wdt_timeout = pdata->timeout; 192 bus_clk = pdata->bus_clk; 193 } 194 195 mv64x60_regs = mv64x60_get_bridge_vbase(); 196 197 writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8, 198 mv64x60_regs + MV64x60_WDT_WDC); 199 200 return misc_register(&mv64x60_wdt_miscdev); 201} 202 203static int __devexit mv64x60_wdt_remove(struct platform_device *dev) 204{ 205 misc_deregister(&mv64x60_wdt_miscdev); 206 207 mv64x60_wdt_service(); 208 mv64x60_wdt_handler_disable(); 209 210 return 0; 211} 212 213static struct platform_driver mv64x60_wdt_driver = { 214 .probe = mv64x60_wdt_probe, 215 .remove = __devexit_p(mv64x60_wdt_remove), 216 .driver = { 217 .owner = THIS_MODULE, 218 .name = MV64x60_WDT_NAME, 219 }, 220}; 221 222static struct platform_device *mv64x60_wdt_dev; 223 224static int __init mv64x60_wdt_init(void) 225{ 226 int ret; 227 228 printk(KERN_INFO "MV64x60 watchdog driver\n"); 229 230 mv64x60_wdt_dev = platform_device_alloc(MV64x60_WDT_NAME, -1); 231 if (!mv64x60_wdt_dev) { 232 ret = -ENOMEM; 233 goto out; 234 } 235 236 ret = platform_device_add(mv64x60_wdt_dev); 237 if (ret) { 238 platform_device_put(mv64x60_wdt_dev); 239 goto out; 240 } 241 242 ret = platform_driver_register(&mv64x60_wdt_driver); 243 if (ret) { 244 platform_device_unregister(mv64x60_wdt_dev); 245 goto out; 246 } 247 248 out: 249 return ret; 250} 251 252static void __exit mv64x60_wdt_exit(void) 253{ 254 platform_driver_unregister(&mv64x60_wdt_driver); 255 platform_device_unregister(mv64x60_wdt_dev); 256} 257 258module_init(mv64x60_wdt_init); 259module_exit(mv64x60_wdt_exit); 260 261MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); 262MODULE_DESCRIPTION("MV64x60 watchdog driver"); 263MODULE_LICENSE("GPL"); 264MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 265