1/* 2 * drivers/char/watchdog/ixp2000_wdt.c 3 * 4 * Watchdog driver for Intel IXP2000 network processors 5 * 6 * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek. 7 * The original version carries these notices: 8 * 9 * Author: Deepak Saxena <dsaxena@plexity.net> 10 * 11 * Copyright 2004 (c) MontaVista, Software, Inc. 12 * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu> 13 * 14 * This file is licensed under the terms of the GNU General Public 15 * License version 2. This program is licensed "as is" without any 16 * warranty of any kind, whether express or implied. 17 */ 18 19#include <linux/module.h> 20#include <linux/moduleparam.h> 21#include <linux/types.h> 22#include <linux/timer.h> 23#include <linux/kernel.h> 24#include <linux/fs.h> 25#include <linux/miscdevice.h> 26#include <linux/watchdog.h> 27#include <linux/init.h> 28#include <linux/bitops.h> 29#include <linux/uaccess.h> 30#include <mach/hardware.h> 31 32static int nowayout = WATCHDOG_NOWAYOUT; 33static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */ 34static unsigned long wdt_status; 35static spinlock_t wdt_lock; 36 37#define WDT_IN_USE 0 38#define WDT_OK_TO_CLOSE 1 39 40static unsigned long wdt_tick_rate; 41 42static void wdt_enable(void) 43{ 44 spin_lock(&wdt_lock); 45 ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE); 46 ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE); 47 ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate); 48 ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE); 49 spin_unlock(&wdt_lock); 50} 51 52static void wdt_disable(void) 53{ 54 spin_lock(&wdt_lock); 55 ixp2000_reg_write(IXP2000_T4_CTL, 0); 56 spin_unlock(&wdt_lock); 57} 58 59static void wdt_keepalive(void) 60{ 61 spin_lock(&wdt_lock); 62 ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate); 63 spin_unlock(&wdt_lock); 64} 65 66static int ixp2000_wdt_open(struct inode *inode, struct file *file) 67{ 68 if (test_and_set_bit(WDT_IN_USE, &wdt_status)) 69 return -EBUSY; 70 71 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 72 73 wdt_enable(); 74 75 return nonseekable_open(inode, file); 76} 77 78static ssize_t ixp2000_wdt_write(struct file *file, const char *data, 79 size_t len, loff_t *ppos) 80{ 81 if (len) { 82 if (!nowayout) { 83 size_t i; 84 85 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 86 87 for (i = 0; i != len; i++) { 88 char c; 89 90 if (get_user(c, data + i)) 91 return -EFAULT; 92 if (c == 'V') 93 set_bit(WDT_OK_TO_CLOSE, &wdt_status); 94 } 95 } 96 wdt_keepalive(); 97 } 98 99 return len; 100} 101 102 103static const struct watchdog_info ident = { 104 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | 105 WDIOF_KEEPALIVEPING, 106 .identity = "IXP2000 Watchdog", 107}; 108 109static long ixp2000_wdt_ioctl(struct file *file, unsigned int cmd, 110 unsigned long arg) 111{ 112 int ret = -ENOTTY; 113 int time; 114 115 switch (cmd) { 116 case WDIOC_GETSUPPORT: 117 ret = copy_to_user((struct watchdog_info *)arg, &ident, 118 sizeof(ident)) ? -EFAULT : 0; 119 break; 120 121 case WDIOC_GETSTATUS: 122 ret = put_user(0, (int *)arg); 123 break; 124 125 case WDIOC_GETBOOTSTATUS: 126 ret = put_user(0, (int *)arg); 127 break; 128 129 case WDIOC_KEEPALIVE: 130 wdt_enable(); 131 ret = 0; 132 break; 133 134 case WDIOC_SETTIMEOUT: 135 ret = get_user(time, (int *)arg); 136 if (ret) 137 break; 138 139 if (time <= 0 || time > 60) { 140 ret = -EINVAL; 141 break; 142 } 143 144 heartbeat = time; 145 wdt_keepalive(); 146 /* Fall through */ 147 148 case WDIOC_GETTIMEOUT: 149 ret = put_user(heartbeat, (int *)arg); 150 break; 151 } 152 153 return ret; 154} 155 156static int ixp2000_wdt_release(struct inode *inode, struct file *file) 157{ 158 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) 159 wdt_disable(); 160 else 161 printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " 162 "timer will not stop\n"); 163 clear_bit(WDT_IN_USE, &wdt_status); 164 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 165 166 return 0; 167} 168 169 170static const struct file_operations ixp2000_wdt_fops = { 171 .owner = THIS_MODULE, 172 .llseek = no_llseek, 173 .write = ixp2000_wdt_write, 174 .unlocked_ioctl = ixp2000_wdt_ioctl, 175 .open = ixp2000_wdt_open, 176 .release = ixp2000_wdt_release, 177}; 178 179static struct miscdevice ixp2000_wdt_miscdev = { 180 .minor = WATCHDOG_MINOR, 181 .name = "watchdog", 182 .fops = &ixp2000_wdt_fops, 183}; 184 185static int __init ixp2000_wdt_init(void) 186{ 187 if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { 188 printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n"); 189 return -EIO; 190 } 191 wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256; 192 spin_lock_init(&wdt_lock); 193 return misc_register(&ixp2000_wdt_miscdev); 194} 195 196static void __exit ixp2000_wdt_exit(void) 197{ 198 misc_deregister(&ixp2000_wdt_miscdev); 199} 200 201module_init(ixp2000_wdt_init); 202module_exit(ixp2000_wdt_exit); 203 204MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); 205MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog"); 206 207module_param(heartbeat, int, 0); 208MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); 209 210module_param(nowayout, int, 0); 211MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 212 213MODULE_LICENSE("GPL"); 214MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 215