aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/ep93xx_wdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/ep93xx_wdt.c')
-rw-r--r--drivers/watchdog/ep93xx_wdt.c251
1 files changed, 89 insertions, 162 deletions
diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
index 726b7df61fd0..77050037597a 100644
--- a/drivers/watchdog/ep93xx_wdt.c
+++ b/drivers/watchdog/ep93xx_wdt.c
@@ -8,6 +8,9 @@
8 * Authors: Ray Lehtiniemi <rayl@mail.com>, 8 * Authors: Ray Lehtiniemi <rayl@mail.com>,
9 * Alessandro Zummo <a.zummo@towertech.it> 9 * Alessandro Zummo <a.zummo@towertech.it>
10 * 10 *
11 * Copyright (c) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
12 * Convert to a platform device and use the watchdog framework API
13 *
11 * This file is licensed under the terms of the GNU General Public 14 * This file is licensed under the terms of the GNU General Public
12 * License version 2. This program is licensed "as is" without any 15 * License version 2. This program is licensed "as is" without any
13 * warranty of any kind, whether express or implied. 16 * warranty of any kind, whether express or implied.
@@ -23,232 +26,156 @@
23 * - Add a few missing ioctls 26 * - Add a few missing ioctls
24 */ 27 */
25 28
29#include <linux/platform_device.h>
26#include <linux/module.h> 30#include <linux/module.h>
27#include <linux/fs.h>
28#include <linux/miscdevice.h> 31#include <linux/miscdevice.h>
29#include <linux/watchdog.h> 32#include <linux/watchdog.h>
30#include <linux/timer.h> 33#include <linux/timer.h>
31#include <linux/uaccess.h>
32#include <linux/io.h> 34#include <linux/io.h>
33#include <mach/hardware.h>
34 35
35#define WDT_VERSION "0.3" 36#define WDT_VERSION "0.4"
36#define PFX "ep93xx_wdt: "
37 37
38/* default timeout (secs) */ 38/* default timeout (secs) */
39#define WDT_TIMEOUT 30 39#define WDT_TIMEOUT 30
40 40
41static int nowayout = WATCHDOG_NOWAYOUT; 41static bool nowayout = WATCHDOG_NOWAYOUT;
42static int timeout = WDT_TIMEOUT; 42module_param(nowayout, bool, 0);
43MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
43 44
45static unsigned int timeout = WDT_TIMEOUT;
46module_param(timeout, uint, 0);
47MODULE_PARM_DESC(timeout,
48 "Watchdog timeout in seconds. (1<=timeout<=3600, default="
49 __MODULE_STRING(WDT_TIMEOUT) ")");
50
51static void __iomem *mmio_base;
44static struct timer_list timer; 52static struct timer_list timer;
45static unsigned long next_heartbeat; 53static unsigned long next_heartbeat;
46static unsigned long wdt_status;
47static unsigned long boot_status;
48
49#define WDT_IN_USE 0
50#define WDT_OK_TO_CLOSE 1
51 54
52#define EP93XX_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x)) 55#define EP93XX_WATCHDOG 0x00
53#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00) 56#define EP93XX_WDSTATUS 0x04
54#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04)
55 57
56/* reset the wdt every ~200ms */ 58/* reset the wdt every ~200ms - the heartbeat of the device is 0.250 seconds*/
57#define WDT_INTERVAL (HZ/5) 59#define WDT_INTERVAL (HZ/5)
58 60
59static void wdt_enable(void) 61static void ep93xx_wdt_timer_ping(unsigned long data)
60{ 62{
61 __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG); 63 if (time_before(jiffies, next_heartbeat))
62} 64 writel(0x5555, mmio_base + EP93XX_WATCHDOG);
63
64static void wdt_disable(void)
65{
66 __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG);
67}
68 65
69static inline void wdt_ping(void) 66 /* Re-set the timer interval */
70{ 67 mod_timer(&timer, jiffies + WDT_INTERVAL);
71 __raw_writew(0x5555, EP93XX_WDT_WATCHDOG);
72} 68}
73 69
74static void wdt_startup(void) 70static int ep93xx_wdt_start(struct watchdog_device *wdd)
75{ 71{
76 next_heartbeat = jiffies + (timeout * HZ); 72 next_heartbeat = jiffies + (timeout * HZ);
77 73
78 wdt_enable(); 74 writel(0xaaaa, mmio_base + EP93XX_WATCHDOG);
79 mod_timer(&timer, jiffies + WDT_INTERVAL); 75 mod_timer(&timer, jiffies + WDT_INTERVAL);
76
77 return 0;
80} 78}
81 79
82static void wdt_shutdown(void) 80static int ep93xx_wdt_stop(struct watchdog_device *wdd)
83{ 81{
84 del_timer_sync(&timer); 82 del_timer_sync(&timer);
85 wdt_disable(); 83 writel(0xaa55, mmio_base + EP93XX_WATCHDOG);
84
85 return 0;
86} 86}
87 87
88static void wdt_keepalive(void) 88static int ep93xx_wdt_keepalive(struct watchdog_device *wdd)
89{ 89{
90 /* user land ping */ 90 /* user land ping */
91 next_heartbeat = jiffies + (timeout * HZ); 91 next_heartbeat = jiffies + (timeout * HZ);
92}
93
94static int ep93xx_wdt_open(struct inode *inode, struct file *file)
95{
96 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
97 return -EBUSY;
98
99 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
100
101 wdt_startup();
102
103 return nonseekable_open(inode, file);
104}
105
106static ssize_t
107ep93xx_wdt_write(struct file *file, const char __user *data, size_t len,
108 loff_t *ppos)
109{
110 if (len) {
111 if (!nowayout) {
112 size_t i;
113
114 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
115
116 for (i = 0; i != len; i++) {
117 char c;
118
119 if (get_user(c, data + i))
120 return -EFAULT;
121
122 if (c == 'V')
123 set_bit(WDT_OK_TO_CLOSE, &wdt_status);
124 else
125 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
126 }
127 }
128 wdt_keepalive();
129 }
130 92
131 return len; 93 return 0;
132} 94}
133 95
134static const struct watchdog_info ident = { 96static const struct watchdog_info ep93xx_wdt_ident = {
135 .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE, 97 .options = WDIOF_CARDRESET |
136 .identity = "EP93xx Watchdog", 98 WDIOF_MAGICCLOSE |
99 WDIOF_KEEPALIVEPING,
100 .identity = "EP93xx Watchdog",
137}; 101};
138 102
139static long ep93xx_wdt_ioctl(struct file *file, 103static struct watchdog_ops ep93xx_wdt_ops = {
140 unsigned int cmd, unsigned long arg)
141{
142 int ret = -ENOTTY;
143
144 switch (cmd) {
145 case WDIOC_GETSUPPORT:
146 ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
147 sizeof(ident)) ? -EFAULT : 0;
148 break;
149
150 case WDIOC_GETSTATUS:
151 ret = put_user(0, (int __user *)arg);
152 break;
153
154 case WDIOC_GETBOOTSTATUS:
155 ret = put_user(boot_status, (int __user *)arg);
156 break;
157
158 case WDIOC_KEEPALIVE:
159 wdt_keepalive();
160 ret = 0;
161 break;
162
163 case WDIOC_GETTIMEOUT:
164 /* actually, it is 0.250 seconds.... */
165 ret = put_user(1, (int __user *)arg);
166 break;
167 }
168 return ret;
169}
170
171static int ep93xx_wdt_release(struct inode *inode, struct file *file)
172{
173 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
174 wdt_shutdown();
175 else
176 printk(KERN_CRIT PFX
177 "Device closed unexpectedly - timer will not stop\n");
178
179 clear_bit(WDT_IN_USE, &wdt_status);
180 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
181
182 return 0;
183}
184
185static const struct file_operations ep93xx_wdt_fops = {
186 .owner = THIS_MODULE, 104 .owner = THIS_MODULE,
187 .write = ep93xx_wdt_write, 105 .start = ep93xx_wdt_start,
188 .unlocked_ioctl = ep93xx_wdt_ioctl, 106 .stop = ep93xx_wdt_stop,
189 .open = ep93xx_wdt_open, 107 .ping = ep93xx_wdt_keepalive,
190 .release = ep93xx_wdt_release,
191 .llseek = no_llseek,
192}; 108};
193 109
194static struct miscdevice ep93xx_wdt_miscdev = { 110static struct watchdog_device ep93xx_wdt_wdd = {
195 .minor = WATCHDOG_MINOR, 111 .info = &ep93xx_wdt_ident,
196 .name = "watchdog", 112 .ops = &ep93xx_wdt_ops,
197 .fops = &ep93xx_wdt_fops,
198}; 113};
199 114
200static void ep93xx_timer_ping(unsigned long data) 115static int __devinit ep93xx_wdt_probe(struct platform_device *pdev)
201{
202 if (time_before(jiffies, next_heartbeat))
203 wdt_ping();
204
205 /* Re-set the timer interval */
206 mod_timer(&timer, jiffies + WDT_INTERVAL);
207}
208
209static int __init ep93xx_wdt_init(void)
210{ 116{
117 struct resource *res;
118 unsigned long val;
211 int err; 119 int err;
212 120
213 err = misc_register(&ep93xx_wdt_miscdev); 121 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
122 if (!res)
123 return -ENXIO;
214 124
215 boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0; 125 if (!devm_request_mem_region(&pdev->dev, res->start,
126 resource_size(res), pdev->name))
127 return -EBUSY;
216 128
217 printk(KERN_INFO PFX "EP93XX watchdog, driver version " 129 mmio_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
218 WDT_VERSION "%s\n", 130 if (!mmio_base)
219 (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08) 131 return -ENXIO;
220 ? " (nCS1 disable detected)" : "");
221 132
222 if (timeout < 1 || timeout > 3600) { 133 if (timeout < 1 || timeout > 3600) {
223 timeout = WDT_TIMEOUT; 134 timeout = WDT_TIMEOUT;
224 printk(KERN_INFO PFX 135 dev_warn(&pdev->dev,
225 "timeout value must be 1<=x<=3600, using %d\n", 136 "timeout value must be 1<=x<=3600, using %d\n",
226 timeout); 137 timeout);
227 } 138 }
228 139
229 setup_timer(&timer, ep93xx_timer_ping, 1); 140 val = readl(mmio_base + EP93XX_WATCHDOG);
230 return err; 141 ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
142 ep93xx_wdt_wdd.timeout = timeout;
143
144 watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
145
146 setup_timer(&timer, ep93xx_wdt_timer_ping, 1);
147
148 err = watchdog_register_device(&ep93xx_wdt_wdd);
149 if (err)
150 return err;
151
152 dev_info(&pdev->dev,
153 "EP93XX watchdog, driver version " WDT_VERSION "%s\n",
154 (val & 0x08) ? " (nCS1 disable detected)" : "");
155
156 return 0;
231} 157}
232 158
233static void __exit ep93xx_wdt_exit(void) 159static int __devexit ep93xx_wdt_remove(struct platform_device *pdev)
234{ 160{
235 wdt_shutdown(); 161 watchdog_unregister_device(&ep93xx_wdt_wdd);
236 misc_deregister(&ep93xx_wdt_miscdev); 162 return 0;
237} 163}
238 164
239module_init(ep93xx_wdt_init); 165static struct platform_driver ep93xx_wdt_driver = {
240module_exit(ep93xx_wdt_exit); 166 .driver = {
241 167 .owner = THIS_MODULE,
242module_param(nowayout, int, 0); 168 .name = "ep93xx-wdt",
243MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 169 },
170 .probe = ep93xx_wdt_probe,
171 .remove = __devexit_p(ep93xx_wdt_remove),
172};
244 173
245module_param(timeout, int, 0); 174module_platform_driver(ep93xx_wdt_driver);
246MODULE_PARM_DESC(timeout,
247 "Watchdog timeout in seconds. (1<=timeout<=3600, default="
248 __MODULE_STRING(WDT_TIMEOUT) ")");
249 175
250MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>," 176MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>,"
251 "Alessandro Zummo <a.zummo@towertech.it>"); 177 "Alessandro Zummo <a.zummo@towertech.it>,"
178 "H Hartley Sweeten <hsweeten@visionengravers.com>");
252MODULE_DESCRIPTION("EP93xx Watchdog"); 179MODULE_DESCRIPTION("EP93xx Watchdog");
253MODULE_LICENSE("GPL"); 180MODULE_LICENSE("GPL");
254MODULE_VERSION(WDT_VERSION); 181MODULE_VERSION(WDT_VERSION);