aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/sp805_wdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/sp805_wdt.c')
-rw-r--r--drivers/watchdog/sp805_wdt.c249
1 files changed, 90 insertions, 159 deletions
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index bbb170e50055..afcd13676542 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -16,20 +16,17 @@
16#include <linux/amba/bus.h> 16#include <linux/amba/bus.h>
17#include <linux/bitops.h> 17#include <linux/bitops.h>
18#include <linux/clk.h> 18#include <linux/clk.h>
19#include <linux/fs.h>
20#include <linux/init.h> 19#include <linux/init.h>
21#include <linux/io.h> 20#include <linux/io.h>
22#include <linux/ioport.h> 21#include <linux/ioport.h>
23#include <linux/kernel.h> 22#include <linux/kernel.h>
24#include <linux/math64.h> 23#include <linux/math64.h>
25#include <linux/miscdevice.h>
26#include <linux/module.h> 24#include <linux/module.h>
27#include <linux/moduleparam.h> 25#include <linux/moduleparam.h>
28#include <linux/pm.h> 26#include <linux/pm.h>
29#include <linux/slab.h> 27#include <linux/slab.h>
30#include <linux/spinlock.h> 28#include <linux/spinlock.h>
31#include <linux/types.h> 29#include <linux/types.h>
32#include <linux/uaccess.h>
33#include <linux/watchdog.h> 30#include <linux/watchdog.h>
34 31
35/* default timeout in seconds */ 32/* default timeout in seconds */
@@ -56,6 +53,7 @@
56 53
57/** 54/**
58 * struct sp805_wdt: sp805 wdt device structure 55 * struct sp805_wdt: sp805 wdt device structure
56 * @wdd: instance of struct watchdog_device
59 * @lock: spin lock protecting dev structure and io access 57 * @lock: spin lock protecting dev structure and io access
60 * @base: base address of wdt 58 * @base: base address of wdt
61 * @clk: clock structure of wdt 59 * @clk: clock structure of wdt
@@ -65,24 +63,24 @@
65 * @timeout: current programmed timeout 63 * @timeout: current programmed timeout
66 */ 64 */
67struct sp805_wdt { 65struct sp805_wdt {
66 struct watchdog_device wdd;
68 spinlock_t lock; 67 spinlock_t lock;
69 void __iomem *base; 68 void __iomem *base;
70 struct clk *clk; 69 struct clk *clk;
71 struct amba_device *adev; 70 struct amba_device *adev;
72 unsigned long status;
73 #define WDT_BUSY 0
74 #define WDT_CAN_BE_CLOSED 1
75 unsigned int load_val; 71 unsigned int load_val;
76 unsigned int timeout; 72 unsigned int timeout;
77}; 73};
78 74
79/* local variables */
80static struct sp805_wdt *wdt;
81static bool nowayout = WATCHDOG_NOWAYOUT; 75static bool nowayout = WATCHDOG_NOWAYOUT;
76module_param(nowayout, bool, 0);
77MODULE_PARM_DESC(nowayout,
78 "Set to 1 to keep watchdog running after device release");
82 79
83/* This routine finds load value that will reset system in required timout */ 80/* This routine finds load value that will reset system in required timout */
84static void wdt_setload(unsigned int timeout) 81static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout)
85{ 82{
83 struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
86 u64 load, rate; 84 u64 load, rate;
87 85
88 rate = clk_get_rate(wdt->clk); 86 rate = clk_get_rate(wdt->clk);
@@ -103,11 +101,14 @@ static void wdt_setload(unsigned int timeout)
103 /* roundup timeout to closest positive integer value */ 101 /* roundup timeout to closest positive integer value */
104 wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate); 102 wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
105 spin_unlock(&wdt->lock); 103 spin_unlock(&wdt->lock);
104
105 return 0;
106} 106}
107 107
108/* returns number of seconds left for reset to occur */ 108/* returns number of seconds left for reset to occur */
109static u32 wdt_timeleft(void) 109static unsigned int wdt_timeleft(struct watchdog_device *wdd)
110{ 110{
111 struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
111 u64 load, rate; 112 u64 load, rate;
112 113
113 rate = clk_get_rate(wdt->clk); 114 rate = clk_get_rate(wdt->clk);
@@ -123,166 +124,96 @@ static u32 wdt_timeleft(void)
123 return div_u64(load, rate); 124 return div_u64(load, rate);
124} 125}
125 126
126/* enables watchdog timers reset */ 127static int wdt_config(struct watchdog_device *wdd, bool ping)
127static void wdt_enable(void)
128{ 128{
129 spin_lock(&wdt->lock); 129 struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
130 int ret;
130 131
131 writel_relaxed(UNLOCK, wdt->base + WDTLOCK); 132 if (!ping) {
132 writel_relaxed(wdt->load_val, wdt->base + WDTLOAD); 133 ret = clk_prepare(wdt->clk);
133 writel_relaxed(INT_MASK, wdt->base + WDTINTCLR); 134 if (ret) {
134 writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); 135 dev_err(&wdt->adev->dev, "clock prepare fail");
135 writel_relaxed(LOCK, wdt->base + WDTLOCK); 136 return ret;
137 }
136 138
137 /* Flush posted writes. */ 139 ret = clk_enable(wdt->clk);
138 readl_relaxed(wdt->base + WDTLOCK); 140 if (ret) {
139 spin_unlock(&wdt->lock); 141 dev_err(&wdt->adev->dev, "clock enable fail");
140} 142 clk_unprepare(wdt->clk);
143 return ret;
144 }
145 }
141 146
142/* disables watchdog timers reset */
143static void wdt_disable(void)
144{
145 spin_lock(&wdt->lock); 147 spin_lock(&wdt->lock);
146 148
147 writel_relaxed(UNLOCK, wdt->base + WDTLOCK); 149 writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
148 writel_relaxed(0, wdt->base + WDTCONTROL); 150 writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
151
152 if (!ping) {
153 writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
154 writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
155 WDTCONTROL);
156 }
157
149 writel_relaxed(LOCK, wdt->base + WDTLOCK); 158 writel_relaxed(LOCK, wdt->base + WDTLOCK);
150 159
151 /* Flush posted writes. */ 160 /* Flush posted writes. */
152 readl_relaxed(wdt->base + WDTLOCK); 161 readl_relaxed(wdt->base + WDTLOCK);
153 spin_unlock(&wdt->lock); 162 spin_unlock(&wdt->lock);
163
164 return 0;
154} 165}
155 166
156static ssize_t sp805_wdt_write(struct file *file, const char *data, 167static int wdt_ping(struct watchdog_device *wdd)
157 size_t len, loff_t *ppos)
158{ 168{
159 if (len) { 169 return wdt_config(wdd, true);
160 if (!nowayout) {
161 size_t i;
162
163 clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
164
165 for (i = 0; i != len; i++) {
166 char c;
167
168 if (get_user(c, data + i))
169 return -EFAULT;
170 /* Check for Magic Close character */
171 if (c == 'V') {
172 set_bit(WDT_CAN_BE_CLOSED,
173 &wdt->status);
174 break;
175 }
176 }
177 }
178 wdt_enable();
179 }
180 return len;
181} 170}
182 171
183static const struct watchdog_info ident = { 172/* enables watchdog timers reset */
184 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 173static int wdt_enable(struct watchdog_device *wdd)
185 .identity = MODULE_NAME,
186};
187
188static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
189 unsigned long arg)
190{ 174{
191 int ret = -ENOTTY; 175 return wdt_config(wdd, false);
192 unsigned int timeout;
193
194 switch (cmd) {
195 case WDIOC_GETSUPPORT:
196 ret = copy_to_user((struct watchdog_info *)arg, &ident,
197 sizeof(ident)) ? -EFAULT : 0;
198 break;
199
200 case WDIOC_GETSTATUS:
201 ret = put_user(0, (int *)arg);
202 break;
203
204 case WDIOC_KEEPALIVE:
205 wdt_enable();
206 ret = 0;
207 break;
208
209 case WDIOC_SETTIMEOUT:
210 ret = get_user(timeout, (unsigned int *)arg);
211 if (ret)
212 break;
213
214 wdt_setload(timeout);
215
216 wdt_enable();
217 /* Fall through */
218
219 case WDIOC_GETTIMEOUT:
220 ret = put_user(wdt->timeout, (unsigned int *)arg);
221 break;
222 case WDIOC_GETTIMELEFT:
223 ret = put_user(wdt_timeleft(), (unsigned int *)arg);
224 break;
225 }
226 return ret;
227} 176}
228 177
229static int sp805_wdt_open(struct inode *inode, struct file *file) 178/* disables watchdog timers reset */
179static int wdt_disable(struct watchdog_device *wdd)
230{ 180{
231 int ret = 0; 181 struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
232
233 if (test_and_set_bit(WDT_BUSY, &wdt->status))
234 return -EBUSY;
235
236 ret = clk_enable(wdt->clk);
237 if (ret) {
238 dev_err(&wdt->adev->dev, "clock enable fail");
239 goto err;
240 }
241
242 wdt_enable();
243 182
244 /* can not be closed, once enabled */ 183 spin_lock(&wdt->lock);
245 clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
246 return nonseekable_open(inode, file);
247 184
248err: 185 writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
249 clear_bit(WDT_BUSY, &wdt->status); 186 writel_relaxed(0, wdt->base + WDTCONTROL);
250 return ret; 187 writel_relaxed(LOCK, wdt->base + WDTLOCK);
251}
252 188
253static int sp805_wdt_release(struct inode *inode, struct file *file) 189 /* Flush posted writes. */
254{ 190 readl_relaxed(wdt->base + WDTLOCK);
255 if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) { 191 spin_unlock(&wdt->lock);
256 clear_bit(WDT_BUSY, &wdt->status);
257 dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
258 return 0;
259 }
260 192
261 wdt_disable();
262 clk_disable(wdt->clk); 193 clk_disable(wdt->clk);
263 clear_bit(WDT_BUSY, &wdt->status); 194 clk_unprepare(wdt->clk);
264 195
265 return 0; 196 return 0;
266} 197}
267 198
268static const struct file_operations sp805_wdt_fops = { 199static const struct watchdog_info wdt_info = {
269 .owner = THIS_MODULE, 200 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
270 .llseek = no_llseek, 201 .identity = MODULE_NAME,
271 .write = sp805_wdt_write,
272 .unlocked_ioctl = sp805_wdt_ioctl,
273 .open = sp805_wdt_open,
274 .release = sp805_wdt_release,
275}; 202};
276 203
277static struct miscdevice sp805_wdt_miscdev = { 204static const struct watchdog_ops wdt_ops = {
278 .minor = WATCHDOG_MINOR, 205 .owner = THIS_MODULE,
279 .name = "watchdog", 206 .start = wdt_enable,
280 .fops = &sp805_wdt_fops, 207 .stop = wdt_disable,
208 .ping = wdt_ping,
209 .set_timeout = wdt_setload,
210 .get_timeleft = wdt_timeleft,
281}; 211};
282 212
283static int __devinit 213static int __devinit
284sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) 214sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
285{ 215{
216 struct sp805_wdt *wdt;
286 int ret = 0; 217 int ret = 0;
287 218
288 if (!devm_request_mem_region(&adev->dev, adev->res.start, 219 if (!devm_request_mem_region(&adev->dev, adev->res.start,
@@ -315,19 +246,26 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
315 } 246 }
316 247
317 wdt->adev = adev; 248 wdt->adev = adev;
249 wdt->wdd.info = &wdt_info;
250 wdt->wdd.ops = &wdt_ops;
251
318 spin_lock_init(&wdt->lock); 252 spin_lock_init(&wdt->lock);
319 wdt_setload(DEFAULT_TIMEOUT); 253 watchdog_set_nowayout(&wdt->wdd, nowayout);
254 watchdog_set_drvdata(&wdt->wdd, wdt);
255 wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT);
320 256
321 ret = misc_register(&sp805_wdt_miscdev); 257 ret = watchdog_register_device(&wdt->wdd);
322 if (ret < 0) { 258 if (ret) {
323 dev_warn(&adev->dev, "cannot register misc device\n"); 259 dev_err(&adev->dev, "watchdog_register_device() failed: %d\n",
324 goto err_misc_register; 260 ret);
261 goto err_register;
325 } 262 }
263 amba_set_drvdata(adev, wdt);
326 264
327 dev_info(&adev->dev, "registration successful\n"); 265 dev_info(&adev->dev, "registration successful\n");
328 return 0; 266 return 0;
329 267
330err_misc_register: 268err_register:
331 clk_put(wdt->clk); 269 clk_put(wdt->clk);
332err: 270err:
333 dev_err(&adev->dev, "Probe Failed!!!\n"); 271 dev_err(&adev->dev, "Probe Failed!!!\n");
@@ -336,7 +274,11 @@ err:
336 274
337static int __devexit sp805_wdt_remove(struct amba_device *adev) 275static int __devexit sp805_wdt_remove(struct amba_device *adev)
338{ 276{
339 misc_deregister(&sp805_wdt_miscdev); 277 struct sp805_wdt *wdt = amba_get_drvdata(adev);
278
279 watchdog_unregister_device(&wdt->wdd);
280 amba_set_drvdata(adev, NULL);
281 watchdog_set_drvdata(&wdt->wdd, NULL);
340 clk_put(wdt->clk); 282 clk_put(wdt->clk);
341 283
342 return 0; 284 return 0;
@@ -345,28 +287,22 @@ static int __devexit sp805_wdt_remove(struct amba_device *adev)
345#ifdef CONFIG_PM 287#ifdef CONFIG_PM
346static int sp805_wdt_suspend(struct device *dev) 288static int sp805_wdt_suspend(struct device *dev)
347{ 289{
348 if (test_bit(WDT_BUSY, &wdt->status)) { 290 struct sp805_wdt *wdt = dev_get_drvdata(dev);
349 wdt_disable(); 291
350 clk_disable(wdt->clk); 292 if (watchdog_active(&wdt->wdd))
351 } 293 return wdt_disable(&wdt->wdd);
352 294
353 return 0; 295 return 0;
354} 296}
355 297
356static int sp805_wdt_resume(struct device *dev) 298static int sp805_wdt_resume(struct device *dev)
357{ 299{
358 int ret = 0; 300 struct sp805_wdt *wdt = dev_get_drvdata(dev);
359 301
360 if (test_bit(WDT_BUSY, &wdt->status)) { 302 if (watchdog_active(&wdt->wdd))
361 ret = clk_enable(wdt->clk); 303 return wdt_enable(&wdt->wdd);
362 if (ret) {
363 dev_err(dev, "clock enable fail");
364 return ret;
365 }
366 wdt_enable();
367 }
368 304
369 return ret; 305 return 0;
370} 306}
371#endif /* CONFIG_PM */ 307#endif /* CONFIG_PM */
372 308
@@ -395,11 +331,6 @@ static struct amba_driver sp805_wdt_driver = {
395 331
396module_amba_driver(sp805_wdt_driver); 332module_amba_driver(sp805_wdt_driver);
397 333
398module_param(nowayout, bool, 0);
399MODULE_PARM_DESC(nowayout,
400 "Set to 1 to keep watchdog running after device release");
401
402MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); 334MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
403MODULE_DESCRIPTION("ARM SP805 Watchdog Driver"); 335MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
404MODULE_LICENSE("GPL"); 336MODULE_LICENSE("GPL");
405MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);