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