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 288 lines 6.5 kB view raw
1/* 2 * Watchdog driver for Freescale STMP37XX/STMP378X 3 * 4 * Author: Vitaly Wool <vital@embeddedalley.com> 5 * 6 * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. 7 * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. 8 */ 9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 12#include <linux/init.h> 13#include <linux/kernel.h> 14#include <linux/fs.h> 15#include <linux/miscdevice.h> 16#include <linux/watchdog.h> 17#include <linux/platform_device.h> 18#include <linux/spinlock.h> 19#include <linux/uaccess.h> 20#include <linux/module.h> 21 22#include <mach/platform.h> 23#include <mach/regs-rtc.h> 24 25#define DEFAULT_HEARTBEAT 19 26#define MAX_HEARTBEAT (0x10000000 >> 6) 27 28/* missing bitmask in headers */ 29#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000 30 31#define WDT_IN_USE 0 32#define WDT_OK_TO_CLOSE 1 33 34#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */ 35 36static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock); 37static unsigned long wdt_status; 38static const bool nowayout = WATCHDOG_NOWAYOUT; 39static int heartbeat = DEFAULT_HEARTBEAT; 40static unsigned long boot_status; 41 42static void wdt_enable(u32 value) 43{ 44 spin_lock(&stmp3xxx_wdt_io_lock); 45 __raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG); 46 stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL); 47 stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, 48 REGS_RTC_BASE + HW_RTC_PERSISTENT1); 49 spin_unlock(&stmp3xxx_wdt_io_lock); 50} 51 52static void wdt_disable(void) 53{ 54 spin_lock(&stmp3xxx_wdt_io_lock); 55 stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, 56 REGS_RTC_BASE + HW_RTC_PERSISTENT1); 57 stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL); 58 spin_unlock(&stmp3xxx_wdt_io_lock); 59} 60 61static void wdt_ping(void) 62{ 63 wdt_enable(heartbeat * WDOG_COUNTER_RATE); 64} 65 66static int stmp3xxx_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 wdt_ping(); 73 74 return nonseekable_open(inode, file); 75} 76 77static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *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_ping(); 96 } 97 98 return len; 99} 100 101static const struct watchdog_info ident = { 102 .options = WDIOF_CARDRESET | 103 WDIOF_MAGICCLOSE | 104 WDIOF_SETTIMEOUT | 105 WDIOF_KEEPALIVEPING, 106 .identity = "STMP3XXX Watchdog", 107}; 108 109static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd, 110 unsigned long arg) 111{ 112 void __user *argp = (void __user *)arg; 113 int __user *p = argp; 114 int new_heartbeat, opts; 115 int ret = -ENOTTY; 116 117 switch (cmd) { 118 case WDIOC_GETSUPPORT: 119 ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; 120 break; 121 122 case WDIOC_GETSTATUS: 123 ret = put_user(0, p); 124 break; 125 126 case WDIOC_GETBOOTSTATUS: 127 ret = put_user(boot_status, p); 128 break; 129 130 case WDIOC_SETOPTIONS: 131 if (get_user(opts, p)) { 132 ret = -EFAULT; 133 break; 134 } 135 if (opts & WDIOS_DISABLECARD) 136 wdt_disable(); 137 else if (opts & WDIOS_ENABLECARD) 138 wdt_ping(); 139 else { 140 pr_debug("%s: unknown option 0x%x\n", __func__, opts); 141 ret = -EINVAL; 142 break; 143 } 144 ret = 0; 145 break; 146 147 case WDIOC_KEEPALIVE: 148 wdt_ping(); 149 ret = 0; 150 break; 151 152 case WDIOC_SETTIMEOUT: 153 if (get_user(new_heartbeat, p)) { 154 ret = -EFAULT; 155 break; 156 } 157 if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) { 158 ret = -EINVAL; 159 break; 160 } 161 162 heartbeat = new_heartbeat; 163 wdt_ping(); 164 /* Fall through */ 165 166 case WDIOC_GETTIMEOUT: 167 ret = put_user(heartbeat, p); 168 break; 169 } 170 return ret; 171} 172 173static int stmp3xxx_wdt_release(struct inode *inode, struct file *file) 174{ 175 int ret = 0; 176 177 if (!nowayout) { 178 if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { 179 wdt_ping(); 180 pr_debug("%s: Device closed unexpectedly\n", __func__); 181 ret = -EINVAL; 182 } else { 183 wdt_disable(); 184 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 185 } 186 } 187 clear_bit(WDT_IN_USE, &wdt_status); 188 189 return ret; 190} 191 192static const struct file_operations stmp3xxx_wdt_fops = { 193 .owner = THIS_MODULE, 194 .llseek = no_llseek, 195 .write = stmp3xxx_wdt_write, 196 .unlocked_ioctl = stmp3xxx_wdt_ioctl, 197 .open = stmp3xxx_wdt_open, 198 .release = stmp3xxx_wdt_release, 199}; 200 201static struct miscdevice stmp3xxx_wdt_miscdev = { 202 .minor = WATCHDOG_MINOR, 203 .name = "watchdog", 204 .fops = &stmp3xxx_wdt_fops, 205}; 206 207static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev) 208{ 209 int ret = 0; 210 211 if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) 212 heartbeat = DEFAULT_HEARTBEAT; 213 214 boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) & 215 BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER; 216 boot_status = !!boot_status; 217 stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, 218 REGS_RTC_BASE + HW_RTC_PERSISTENT1); 219 wdt_disable(); /* disable for now */ 220 221 ret = misc_register(&stmp3xxx_wdt_miscdev); 222 if (ret < 0) { 223 dev_err(&pdev->dev, "cannot register misc device\n"); 224 return ret; 225 } 226 227 pr_info("initialized, heartbeat %d sec\n", heartbeat); 228 229 return ret; 230} 231 232static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev) 233{ 234 misc_deregister(&stmp3xxx_wdt_miscdev); 235 return 0; 236} 237 238#ifdef CONFIG_PM 239static int wdt_suspended; 240static u32 wdt_saved_time; 241 242static int stmp3xxx_wdt_suspend(struct platform_device *pdev, 243 pm_message_t state) 244{ 245 if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) & 246 BM_RTC_CTRL_WATCHDOGEN) { 247 wdt_suspended = 1; 248 wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG); 249 wdt_disable(); 250 } 251 return 0; 252} 253 254static int stmp3xxx_wdt_resume(struct platform_device *pdev) 255{ 256 if (wdt_suspended) { 257 wdt_enable(wdt_saved_time); 258 wdt_suspended = 0; 259 } 260 return 0; 261} 262#else 263#define stmp3xxx_wdt_suspend NULL 264#define stmp3xxx_wdt_resume NULL 265#endif 266 267static struct platform_driver platform_wdt_driver = { 268 .driver = { 269 .name = "stmp3xxx_wdt", 270 }, 271 .probe = stmp3xxx_wdt_probe, 272 .remove = __devexit_p(stmp3xxx_wdt_remove), 273 .suspend = stmp3xxx_wdt_suspend, 274 .resume = stmp3xxx_wdt_resume, 275}; 276 277module_platform_driver(platform_wdt_driver); 278 279MODULE_DESCRIPTION("STMP3XXX Watchdog Driver"); 280MODULE_LICENSE("GPL"); 281 282module_param(heartbeat, int, 0); 283MODULE_PARM_DESC(heartbeat, 284 "Watchdog heartbeat period in seconds from 1 to " 285 __MODULE_STRING(MAX_HEARTBEAT) ", default " 286 __MODULE_STRING(DEFAULT_HEARTBEAT)); 287 288MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);