1/* 2 * Watchdog driver for the SA11x0/PXA2xx 3 * 4 * (c) Copyright 2000 Oleg Drokin <green@crimea.edu> 5 * Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 * 12 * Neither Oleg Drokin nor iXcelerator.com admit liability nor provide 13 * warranty for any of this software. This material is provided 14 * "AS-IS" and at no charge. 15 * 16 * (c) Copyright 2000 Oleg Drokin <green@crimea.edu> 17 * 18 * 27/11/2000 Initial release 19 */ 20 21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 22 23#include <linux/module.h> 24#include <linux/moduleparam.h> 25#include <linux/types.h> 26#include <linux/kernel.h> 27#include <linux/fs.h> 28#include <linux/miscdevice.h> 29#include <linux/watchdog.h> 30#include <linux/init.h> 31#include <linux/io.h> 32#include <linux/bitops.h> 33#include <linux/uaccess.h> 34#include <linux/timex.h> 35 36#ifdef CONFIG_ARCH_PXA 37#include <mach/regs-ost.h> 38#endif 39 40#include <mach/reset.h> 41#include <mach/hardware.h> 42 43static unsigned long oscr_freq; 44static unsigned long sa1100wdt_users; 45static unsigned int pre_margin; 46static int boot_status; 47 48/* 49 * Allow only one person to hold it open 50 */ 51static int sa1100dog_open(struct inode *inode, struct file *file) 52{ 53 if (test_and_set_bit(1, &sa1100wdt_users)) 54 return -EBUSY; 55 56 /* Activate SA1100 Watchdog timer */ 57 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); 58 writel_relaxed(OSSR_M3, OSSR); 59 writel_relaxed(OWER_WME, OWER); 60 writel_relaxed(readl_relaxed(OIER) | OIER_E3, OIER); 61 return nonseekable_open(inode, file); 62} 63 64/* 65 * The watchdog cannot be disabled. 66 * 67 * Previous comments suggested that turning off the interrupt by 68 * clearing OIER[E3] would prevent the watchdog timing out but this 69 * does not appear to be true (at least on the PXA255). 70 */ 71static int sa1100dog_release(struct inode *inode, struct file *file) 72{ 73 pr_crit("Device closed - timer will not stop\n"); 74 clear_bit(1, &sa1100wdt_users); 75 return 0; 76} 77 78static ssize_t sa1100dog_write(struct file *file, const char __user *data, 79 size_t len, loff_t *ppos) 80{ 81 if (len) 82 /* Refresh OSMR3 timer. */ 83 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); 84 return len; 85} 86 87static const struct watchdog_info ident = { 88 .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT 89 | WDIOF_KEEPALIVEPING, 90 .identity = "SA1100/PXA255 Watchdog", 91 .firmware_version = 1, 92}; 93 94static long sa1100dog_ioctl(struct file *file, unsigned int cmd, 95 unsigned long arg) 96{ 97 int ret = -ENOTTY; 98 int time; 99 void __user *argp = (void __user *)arg; 100 int __user *p = argp; 101 102 switch (cmd) { 103 case WDIOC_GETSUPPORT: 104 ret = copy_to_user(argp, &ident, 105 sizeof(ident)) ? -EFAULT : 0; 106 break; 107 108 case WDIOC_GETSTATUS: 109 ret = put_user(0, p); 110 break; 111 112 case WDIOC_GETBOOTSTATUS: 113 ret = put_user(boot_status, p); 114 break; 115 116 case WDIOC_KEEPALIVE: 117 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); 118 ret = 0; 119 break; 120 121 case WDIOC_SETTIMEOUT: 122 ret = get_user(time, p); 123 if (ret) 124 break; 125 126 if (time <= 0 || (oscr_freq * (long long)time >= 0xffffffff)) { 127 ret = -EINVAL; 128 break; 129 } 130 131 pre_margin = oscr_freq * time; 132 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); 133 /*fall through*/ 134 135 case WDIOC_GETTIMEOUT: 136 ret = put_user(pre_margin / oscr_freq, p); 137 break; 138 } 139 return ret; 140} 141 142static const struct file_operations sa1100dog_fops = { 143 .owner = THIS_MODULE, 144 .llseek = no_llseek, 145 .write = sa1100dog_write, 146 .unlocked_ioctl = sa1100dog_ioctl, 147 .open = sa1100dog_open, 148 .release = sa1100dog_release, 149}; 150 151static struct miscdevice sa1100dog_miscdev = { 152 .minor = WATCHDOG_MINOR, 153 .name = "watchdog", 154 .fops = &sa1100dog_fops, 155}; 156 157static int margin __initdata = 60; /* (secs) Default is 1 minute */ 158 159static int __init sa1100dog_init(void) 160{ 161 int ret; 162 163 oscr_freq = get_clock_tick_rate(); 164 165 /* 166 * Read the reset status, and save it for later. If 167 * we suspend, RCSR will be cleared, and the watchdog 168 * reset reason will be lost. 169 */ 170 boot_status = (reset_status & RESET_STATUS_WATCHDOG) ? 171 WDIOF_CARDRESET : 0; 172 pre_margin = oscr_freq * margin; 173 174 ret = misc_register(&sa1100dog_miscdev); 175 if (ret == 0) 176 pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", 177 margin); 178 return ret; 179} 180 181static void __exit sa1100dog_exit(void) 182{ 183 misc_deregister(&sa1100dog_miscdev); 184} 185 186module_init(sa1100dog_init); 187module_exit(sa1100dog_exit); 188 189MODULE_AUTHOR("Oleg Drokin <green@crimea.edu>"); 190MODULE_DESCRIPTION("SA1100/PXA2xx Watchdog"); 191 192module_param(margin, int, 0); 193MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); 194 195MODULE_LICENSE("GPL"); 196