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-rc7 315 lines 7.6 kB view raw
1/* 2 * Watchdog driver for Kendin/Micrel KS8695. 3 * 4 * (C) 2007 Andrew Victor 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13#include <linux/bitops.h> 14#include <linux/errno.h> 15#include <linux/fs.h> 16#include <linux/init.h> 17#include <linux/kernel.h> 18#include <linux/miscdevice.h> 19#include <linux/module.h> 20#include <linux/moduleparam.h> 21#include <linux/platform_device.h> 22#include <linux/types.h> 23#include <linux/watchdog.h> 24#include <linux/io.h> 25#include <linux/uaccess.h> 26#include <mach/hardware.h> 27#include <mach/regs-timer.h> 28 29#define WDT_DEFAULT_TIME 5 /* seconds */ 30#define WDT_MAX_TIME 171 /* seconds */ 31 32static int wdt_time = WDT_DEFAULT_TIME; 33static bool nowayout = WATCHDOG_NOWAYOUT; 34 35module_param(wdt_time, int, 0); 36MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" 37 __MODULE_STRING(WDT_DEFAULT_TIME) ")"); 38 39#ifdef CONFIG_WATCHDOG_NOWAYOUT 40module_param(nowayout, bool, 0); 41MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 42 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 43#endif 44 45 46static unsigned long ks8695wdt_busy; 47static DEFINE_SPINLOCK(ks8695_lock); 48 49/* ......................................................................... */ 50 51/* 52 * Disable the watchdog. 53 */ 54static inline void ks8695_wdt_stop(void) 55{ 56 unsigned long tmcon; 57 58 spin_lock(&ks8695_lock); 59 /* disable timer0 */ 60 tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); 61 __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); 62 spin_unlock(&ks8695_lock); 63} 64 65/* 66 * Enable and reset the watchdog. 67 */ 68static inline void ks8695_wdt_start(void) 69{ 70 unsigned long tmcon; 71 unsigned long tval = wdt_time * KS8695_CLOCK_RATE; 72 73 spin_lock(&ks8695_lock); 74 /* disable timer0 */ 75 tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); 76 __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); 77 78 /* program timer0 */ 79 __raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC); 80 81 /* re-enable timer0 */ 82 tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); 83 __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); 84 spin_unlock(&ks8695_lock); 85} 86 87/* 88 * Reload the watchdog timer. (ie, pat the watchdog) 89 */ 90static inline void ks8695_wdt_reload(void) 91{ 92 unsigned long tmcon; 93 94 spin_lock(&ks8695_lock); 95 /* disable, then re-enable timer0 */ 96 tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); 97 __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); 98 __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); 99 spin_unlock(&ks8695_lock); 100} 101 102/* 103 * Change the watchdog time interval. 104 */ 105static int ks8695_wdt_settimeout(int new_time) 106{ 107 /* 108 * All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz 109 * 110 * Since WDV is a 16-bit counter, the maximum period is 111 * 65536 / 0.256 = 256 seconds. 112 */ 113 if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) 114 return -EINVAL; 115 116 /* Set new watchdog time. It will be used when 117 ks8695_wdt_start() is called. */ 118 wdt_time = new_time; 119 return 0; 120} 121 122/* ......................................................................... */ 123 124/* 125 * Watchdog device is opened, and watchdog starts running. 126 */ 127static int ks8695_wdt_open(struct inode *inode, struct file *file) 128{ 129 if (test_and_set_bit(0, &ks8695wdt_busy)) 130 return -EBUSY; 131 132 ks8695_wdt_start(); 133 return nonseekable_open(inode, file); 134} 135 136/* 137 * Close the watchdog device. 138 * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also 139 * disabled. 140 */ 141static int ks8695_wdt_close(struct inode *inode, struct file *file) 142{ 143 /* Disable the watchdog when file is closed */ 144 if (!nowayout) 145 ks8695_wdt_stop(); 146 clear_bit(0, &ks8695wdt_busy); 147 return 0; 148} 149 150static const struct watchdog_info ks8695_wdt_info = { 151 .identity = "ks8695 watchdog", 152 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 153}; 154 155/* 156 * Handle commands from user-space. 157 */ 158static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd, 159 unsigned long arg) 160{ 161 void __user *argp = (void __user *)arg; 162 int __user *p = argp; 163 int new_value; 164 165 switch (cmd) { 166 case WDIOC_GETSUPPORT: 167 return copy_to_user(argp, &ks8695_wdt_info, 168 sizeof(ks8695_wdt_info)) ? -EFAULT : 0; 169 case WDIOC_GETSTATUS: 170 case WDIOC_GETBOOTSTATUS: 171 return put_user(0, p); 172 case WDIOC_SETOPTIONS: 173 if (get_user(new_value, p)) 174 return -EFAULT; 175 if (new_value & WDIOS_DISABLECARD) 176 ks8695_wdt_stop(); 177 if (new_value & WDIOS_ENABLECARD) 178 ks8695_wdt_start(); 179 return 0; 180 case WDIOC_KEEPALIVE: 181 ks8695_wdt_reload(); /* pat the watchdog */ 182 return 0; 183 case WDIOC_SETTIMEOUT: 184 if (get_user(new_value, p)) 185 return -EFAULT; 186 if (ks8695_wdt_settimeout(new_value)) 187 return -EINVAL; 188 /* Enable new time value */ 189 ks8695_wdt_start(); 190 /* Return current value */ 191 return put_user(wdt_time, p); 192 case WDIOC_GETTIMEOUT: 193 return put_user(wdt_time, p); 194 default: 195 return -ENOTTY; 196 } 197} 198 199/* 200 * Pat the watchdog whenever device is written to. 201 */ 202static ssize_t ks8695_wdt_write(struct file *file, const char *data, 203 size_t len, loff_t *ppos) 204{ 205 ks8695_wdt_reload(); /* pat the watchdog */ 206 return len; 207} 208 209/* ......................................................................... */ 210 211static const struct file_operations ks8695wdt_fops = { 212 .owner = THIS_MODULE, 213 .llseek = no_llseek, 214 .unlocked_ioctl = ks8695_wdt_ioctl, 215 .open = ks8695_wdt_open, 216 .release = ks8695_wdt_close, 217 .write = ks8695_wdt_write, 218}; 219 220static struct miscdevice ks8695wdt_miscdev = { 221 .minor = WATCHDOG_MINOR, 222 .name = "watchdog", 223 .fops = &ks8695wdt_fops, 224}; 225 226static int __devinit ks8695wdt_probe(struct platform_device *pdev) 227{ 228 int res; 229 230 if (ks8695wdt_miscdev.parent) 231 return -EBUSY; 232 ks8695wdt_miscdev.parent = &pdev->dev; 233 234 res = misc_register(&ks8695wdt_miscdev); 235 if (res) 236 return res; 237 238 pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n", 239 wdt_time, nowayout ? ", nowayout" : ""); 240 return 0; 241} 242 243static int __devexit ks8695wdt_remove(struct platform_device *pdev) 244{ 245 int res; 246 247 res = misc_deregister(&ks8695wdt_miscdev); 248 if (!res) 249 ks8695wdt_miscdev.parent = NULL; 250 251 return res; 252} 253 254static void ks8695wdt_shutdown(struct platform_device *pdev) 255{ 256 ks8695_wdt_stop(); 257} 258 259#ifdef CONFIG_PM 260 261static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message) 262{ 263 ks8695_wdt_stop(); 264 return 0; 265} 266 267static int ks8695wdt_resume(struct platform_device *pdev) 268{ 269 if (ks8695wdt_busy) 270 ks8695_wdt_start(); 271 return 0; 272} 273 274#else 275#define ks8695wdt_suspend NULL 276#define ks8695wdt_resume NULL 277#endif 278 279static struct platform_driver ks8695wdt_driver = { 280 .probe = ks8695wdt_probe, 281 .remove = __devexit_p(ks8695wdt_remove), 282 .shutdown = ks8695wdt_shutdown, 283 .suspend = ks8695wdt_suspend, 284 .resume = ks8695wdt_resume, 285 .driver = { 286 .name = "ks8695_wdt", 287 .owner = THIS_MODULE, 288 }, 289}; 290 291static int __init ks8695_wdt_init(void) 292{ 293 /* Check that the heartbeat value is within range; 294 if not reset to the default */ 295 if (ks8695_wdt_settimeout(wdt_time)) { 296 ks8695_wdt_settimeout(WDT_DEFAULT_TIME); 297 pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i" 298 ", using %d\n", wdt_time, WDT_MAX_TIME); 299 } 300 return platform_driver_register(&ks8695wdt_driver); 301} 302 303static void __exit ks8695_wdt_exit(void) 304{ 305 platform_driver_unregister(&ks8695wdt_driver); 306} 307 308module_init(ks8695_wdt_init); 309module_exit(ks8695_wdt_exit); 310 311MODULE_AUTHOR("Andrew Victor"); 312MODULE_DESCRIPTION("Watchdog driver for KS8695"); 313MODULE_LICENSE("GPL"); 314MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 315MODULE_ALIAS("platform:ks8695_wdt");