Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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