diff options
Diffstat (limited to 'drivers/watchdog/ep93xx_wdt.c')
-rw-r--r-- | drivers/watchdog/ep93xx_wdt.c | 251 |
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 | ||
41 | static int nowayout = WATCHDOG_NOWAYOUT; | 41 | static bool nowayout = WATCHDOG_NOWAYOUT; |
42 | static int timeout = WDT_TIMEOUT; | 42 | module_param(nowayout, bool, 0); |
43 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); | ||
43 | 44 | ||
45 | static unsigned int timeout = WDT_TIMEOUT; | ||
46 | module_param(timeout, uint, 0); | ||
47 | MODULE_PARM_DESC(timeout, | ||
48 | "Watchdog timeout in seconds. (1<=timeout<=3600, default=" | ||
49 | __MODULE_STRING(WDT_TIMEOUT) ")"); | ||
50 | |||
51 | static void __iomem *mmio_base; | ||
44 | static struct timer_list timer; | 52 | static struct timer_list timer; |
45 | static unsigned long next_heartbeat; | 53 | static unsigned long next_heartbeat; |
46 | static unsigned long wdt_status; | ||
47 | static 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 | ||
59 | static void wdt_enable(void) | 61 | static 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 | |||
64 | static void wdt_disable(void) | ||
65 | { | ||
66 | __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG); | ||
67 | } | ||
68 | 65 | ||
69 | static 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 | ||
74 | static void wdt_startup(void) | 70 | static 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 | ||
82 | static void wdt_shutdown(void) | 80 | static 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 | ||
88 | static void wdt_keepalive(void) | 88 | static 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 | |||
94 | static 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 | |||
106 | static ssize_t | ||
107 | ep93xx_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 | ||
134 | static const struct watchdog_info ident = { | 96 | static 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 | ||
139 | static long ep93xx_wdt_ioctl(struct file *file, | 103 | static 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 | |||
171 | static 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 | |||
185 | static 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 | ||
194 | static struct miscdevice ep93xx_wdt_miscdev = { | 110 | static 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 | ||
200 | static void ep93xx_timer_ping(unsigned long data) | 115 | static 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 | |||
209 | static 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 | ||
233 | static void __exit ep93xx_wdt_exit(void) | 159 | static 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 | ||
239 | module_init(ep93xx_wdt_init); | 165 | static struct platform_driver ep93xx_wdt_driver = { |
240 | module_exit(ep93xx_wdt_exit); | 166 | .driver = { |
241 | 167 | .owner = THIS_MODULE, | |
242 | module_param(nowayout, int, 0); | 168 | .name = "ep93xx-wdt", |
243 | MODULE_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 | ||
245 | module_param(timeout, int, 0); | 174 | module_platform_driver(ep93xx_wdt_driver); |
246 | MODULE_PARM_DESC(timeout, | ||
247 | "Watchdog timeout in seconds. (1<=timeout<=3600, default=" | ||
248 | __MODULE_STRING(WDT_TIMEOUT) ")"); | ||
249 | 175 | ||
250 | MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>," | 176 | MODULE_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>"); | ||
252 | MODULE_DESCRIPTION("EP93xx Watchdog"); | 179 | MODULE_DESCRIPTION("EP93xx Watchdog"); |
253 | MODULE_LICENSE("GPL"); | 180 | MODULE_LICENSE("GPL"); |
254 | MODULE_VERSION(WDT_VERSION); | 181 | MODULE_VERSION(WDT_VERSION); |