1/* 2 * drivers/char/watchdog/iop_wdt.c 3 * 4 * WDT driver for Intel I/O Processors 5 * Copyright (C) 2005, Intel Corporation. 6 * 7 * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc. 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms and conditions of the GNU General Public License, 11 * version 2, as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 20 * Place - Suite 330, Boston, MA 02111-1307 USA. 21 * 22 * Curt E Bruns <curt.e.bruns@intel.com> 23 * Peter Milne <peter.milne@d-tacq.com> 24 * Dan Williams <dan.j.williams@intel.com> 25 */ 26 27#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 28 29#include <linux/module.h> 30#include <linux/kernel.h> 31#include <linux/fs.h> 32#include <linux/init.h> 33#include <linux/device.h> 34#include <linux/miscdevice.h> 35#include <linux/watchdog.h> 36#include <linux/uaccess.h> 37#include <mach/hardware.h> 38 39static bool nowayout = WATCHDOG_NOWAYOUT; 40static unsigned long wdt_status; 41static unsigned long boot_status; 42static DEFINE_SPINLOCK(wdt_lock); 43 44#define WDT_IN_USE 0 45#define WDT_OK_TO_CLOSE 1 46#define WDT_ENABLED 2 47 48static unsigned long iop_watchdog_timeout(void) 49{ 50 return (0xffffffffUL / get_iop_tick_rate()); 51} 52 53/** 54 * wdt_supports_disable - determine if we are accessing a iop13xx watchdog 55 * or iop3xx by whether it has a disable command 56 */ 57static int wdt_supports_disable(void) 58{ 59 int can_disable; 60 61 if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM) 62 can_disable = 1; 63 else 64 can_disable = 0; 65 66 return can_disable; 67} 68 69static void wdt_enable(void) 70{ 71 /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF 72 * Takes approx. 10.7s to timeout 73 */ 74 spin_lock(&wdt_lock); 75 write_wdtcr(IOP_WDTCR_EN_ARM); 76 write_wdtcr(IOP_WDTCR_EN); 77 spin_unlock(&wdt_lock); 78} 79 80/* returns 0 if the timer was successfully disabled */ 81static int wdt_disable(void) 82{ 83 /* Stop Counting */ 84 if (wdt_supports_disable()) { 85 spin_lock(&wdt_lock); 86 write_wdtcr(IOP_WDTCR_DIS_ARM); 87 write_wdtcr(IOP_WDTCR_DIS); 88 clear_bit(WDT_ENABLED, &wdt_status); 89 spin_unlock(&wdt_lock); 90 pr_info("Disabled\n"); 91 return 0; 92 } else 93 return 1; 94} 95 96static int iop_wdt_open(struct inode *inode, struct file *file) 97{ 98 if (test_and_set_bit(WDT_IN_USE, &wdt_status)) 99 return -EBUSY; 100 101 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 102 wdt_enable(); 103 set_bit(WDT_ENABLED, &wdt_status); 104 return nonseekable_open(inode, file); 105} 106 107static ssize_t iop_wdt_write(struct file *file, const char *data, size_t len, 108 loff_t *ppos) 109{ 110 if (len) { 111 if (!nowayout) { 112 size_t i; 113 114 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 115 116 for (i = 0; i != len; i++) { 117 char c; 118 119 if (get_user(c, data + i)) 120 return -EFAULT; 121 if (c == 'V') 122 set_bit(WDT_OK_TO_CLOSE, &wdt_status); 123 } 124 } 125 wdt_enable(); 126 } 127 return len; 128} 129 130static const struct watchdog_info ident = { 131 .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 132 .identity = "iop watchdog", 133}; 134 135static long iop_wdt_ioctl(struct file *file, 136 unsigned int cmd, unsigned long arg) 137{ 138 int options; 139 int ret = -ENOTTY; 140 int __user *argp = (int __user *)arg; 141 142 switch (cmd) { 143 case WDIOC_GETSUPPORT: 144 if (copy_to_user(argp, &ident, sizeof(ident))) 145 ret = -EFAULT; 146 else 147 ret = 0; 148 break; 149 150 case WDIOC_GETSTATUS: 151 ret = put_user(0, argp); 152 break; 153 154 case WDIOC_GETBOOTSTATUS: 155 ret = put_user(boot_status, argp); 156 break; 157 158 case WDIOC_SETOPTIONS: 159 if (get_user(options, (int *)arg)) 160 return -EFAULT; 161 162 if (options & WDIOS_DISABLECARD) { 163 if (!nowayout) { 164 if (wdt_disable() == 0) { 165 set_bit(WDT_OK_TO_CLOSE, &wdt_status); 166 ret = 0; 167 } else 168 ret = -ENXIO; 169 } else 170 ret = 0; 171 } 172 if (options & WDIOS_ENABLECARD) { 173 wdt_enable(); 174 ret = 0; 175 } 176 break; 177 178 case WDIOC_KEEPALIVE: 179 wdt_enable(); 180 ret = 0; 181 break; 182 183 case WDIOC_GETTIMEOUT: 184 ret = put_user(iop_watchdog_timeout(), argp); 185 break; 186 } 187 return ret; 188} 189 190static int iop_wdt_release(struct inode *inode, struct file *file) 191{ 192 int state = 1; 193 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) 194 if (test_bit(WDT_ENABLED, &wdt_status)) 195 state = wdt_disable(); 196 197 /* if the timer is not disabled reload and notify that we are still 198 * going down 199 */ 200 if (state != 0) { 201 wdt_enable(); 202 pr_crit("Device closed unexpectedly - reset in %lu seconds\n", 203 iop_watchdog_timeout()); 204 } 205 206 clear_bit(WDT_IN_USE, &wdt_status); 207 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 208 209 return 0; 210} 211 212static const struct file_operations iop_wdt_fops = { 213 .owner = THIS_MODULE, 214 .llseek = no_llseek, 215 .write = iop_wdt_write, 216 .unlocked_ioctl = iop_wdt_ioctl, 217 .open = iop_wdt_open, 218 .release = iop_wdt_release, 219}; 220 221static struct miscdevice iop_wdt_miscdev = { 222 .minor = WATCHDOG_MINOR, 223 .name = "watchdog", 224 .fops = &iop_wdt_fops, 225}; 226 227static int __init iop_wdt_init(void) 228{ 229 int ret; 230 231 /* check if the reset was caused by the watchdog timer */ 232 boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0; 233 234 /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset 235 * NOTE: An IB Reset will Reset both cores in the IOP342 236 */ 237 write_wdtsr(IOP13XX_WDTCR_IB_RESET); 238 239 /* Register after we have the device set up so we cannot race 240 with an open */ 241 ret = misc_register(&iop_wdt_miscdev); 242 if (ret == 0) 243 pr_info("timeout %lu sec\n", iop_watchdog_timeout()); 244 245 return ret; 246} 247 248static void __exit iop_wdt_exit(void) 249{ 250 misc_deregister(&iop_wdt_miscdev); 251} 252 253module_init(iop_wdt_init); 254module_exit(iop_wdt_exit); 255 256module_param(nowayout, bool, 0); 257MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 258 259MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>"); 260MODULE_DESCRIPTION("iop watchdog timer driver"); 261MODULE_LICENSE("GPL"); 262