diff options
Diffstat (limited to 'drivers/watchdog/sp805_wdt.c')
-rw-r--r-- | drivers/watchdog/sp805_wdt.c | 111 |
1 files changed, 67 insertions, 44 deletions
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 3ff9e47bd218..bbb170e50055 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/miscdevice.h> | 25 | #include <linux/miscdevice.h> |
26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
27 | #include <linux/moduleparam.h> | 27 | #include <linux/moduleparam.h> |
28 | #include <linux/pm.h> | ||
28 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
29 | #include <linux/spinlock.h> | 30 | #include <linux/spinlock.h> |
30 | #include <linux/types.h> | 31 | #include <linux/types.h> |
@@ -55,14 +56,13 @@ | |||
55 | 56 | ||
56 | /** | 57 | /** |
57 | * struct sp805_wdt: sp805 wdt device structure | 58 | * struct sp805_wdt: sp805 wdt device structure |
58 | * | 59 | * @lock: spin lock protecting dev structure and io access |
59 | * lock: spin lock protecting dev structure and io access | 60 | * @base: base address of wdt |
60 | * base: base address of wdt | 61 | * @clk: clock structure of wdt |
61 | * clk: clock structure of wdt | 62 | * @adev: amba device structure of wdt |
62 | * dev: amba device structure of wdt | 63 | * @status: current status of wdt |
63 | * status: current status of wdt | 64 | * @load_val: load value to be set for current timeout |
64 | * load_val: load value to be set for current timeout | 65 | * @timeout: current programmed timeout |
65 | * timeout: current programmed timeout | ||
66 | */ | 66 | */ |
67 | struct sp805_wdt { | 67 | struct sp805_wdt { |
68 | spinlock_t lock; | 68 | spinlock_t lock; |
@@ -78,7 +78,7 @@ struct sp805_wdt { | |||
78 | 78 | ||
79 | /* local variables */ | 79 | /* local variables */ |
80 | static struct sp805_wdt *wdt; | 80 | static struct sp805_wdt *wdt; |
81 | static int nowayout = WATCHDOG_NOWAYOUT; | 81 | static bool nowayout = WATCHDOG_NOWAYOUT; |
82 | 82 | ||
83 | /* This routine finds load value that will reset system in required timout */ | 83 | /* This routine finds load value that will reset system in required timout */ |
84 | static void wdt_setload(unsigned int timeout) | 84 | static void wdt_setload(unsigned int timeout) |
@@ -113,10 +113,10 @@ static u32 wdt_timeleft(void) | |||
113 | rate = clk_get_rate(wdt->clk); | 113 | rate = clk_get_rate(wdt->clk); |
114 | 114 | ||
115 | spin_lock(&wdt->lock); | 115 | spin_lock(&wdt->lock); |
116 | load = readl(wdt->base + WDTVALUE); | 116 | load = readl_relaxed(wdt->base + WDTVALUE); |
117 | 117 | ||
118 | /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */ | 118 | /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */ |
119 | if (!(readl(wdt->base + WDTRIS) & INT_MASK)) | 119 | if (!(readl_relaxed(wdt->base + WDTRIS) & INT_MASK)) |
120 | load += wdt->load_val + 1; | 120 | load += wdt->load_val + 1; |
121 | spin_unlock(&wdt->lock); | 121 | spin_unlock(&wdt->lock); |
122 | 122 | ||
@@ -128,14 +128,14 @@ static void wdt_enable(void) | |||
128 | { | 128 | { |
129 | spin_lock(&wdt->lock); | 129 | spin_lock(&wdt->lock); |
130 | 130 | ||
131 | writel(UNLOCK, wdt->base + WDTLOCK); | 131 | writel_relaxed(UNLOCK, wdt->base + WDTLOCK); |
132 | writel(wdt->load_val, wdt->base + WDTLOAD); | 132 | writel_relaxed(wdt->load_val, wdt->base + WDTLOAD); |
133 | writel(INT_MASK, wdt->base + WDTINTCLR); | 133 | writel_relaxed(INT_MASK, wdt->base + WDTINTCLR); |
134 | writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); | 134 | writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); |
135 | writel(LOCK, wdt->base + WDTLOCK); | 135 | writel_relaxed(LOCK, wdt->base + WDTLOCK); |
136 | 136 | ||
137 | /* Flush posted writes. */ | 137 | /* Flush posted writes. */ |
138 | readl(wdt->base + WDTLOCK); | 138 | readl_relaxed(wdt->base + WDTLOCK); |
139 | spin_unlock(&wdt->lock); | 139 | spin_unlock(&wdt->lock); |
140 | } | 140 | } |
141 | 141 | ||
@@ -144,12 +144,12 @@ static void wdt_disable(void) | |||
144 | { | 144 | { |
145 | spin_lock(&wdt->lock); | 145 | spin_lock(&wdt->lock); |
146 | 146 | ||
147 | writel(UNLOCK, wdt->base + WDTLOCK); | 147 | writel_relaxed(UNLOCK, wdt->base + WDTLOCK); |
148 | writel(0, wdt->base + WDTCONTROL); | 148 | writel_relaxed(0, wdt->base + WDTCONTROL); |
149 | writel(LOCK, wdt->base + WDTLOCK); | 149 | writel_relaxed(LOCK, wdt->base + WDTLOCK); |
150 | 150 | ||
151 | /* Flush posted writes. */ | 151 | /* Flush posted writes. */ |
152 | readl(wdt->base + WDTLOCK); | 152 | readl_relaxed(wdt->base + WDTLOCK); |
153 | spin_unlock(&wdt->lock); | 153 | spin_unlock(&wdt->lock); |
154 | } | 154 | } |
155 | 155 | ||
@@ -285,32 +285,33 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) | |||
285 | { | 285 | { |
286 | int ret = 0; | 286 | int ret = 0; |
287 | 287 | ||
288 | if (!request_mem_region(adev->res.start, resource_size(&adev->res), | 288 | if (!devm_request_mem_region(&adev->dev, adev->res.start, |
289 | "sp805_wdt")) { | 289 | resource_size(&adev->res), "sp805_wdt")) { |
290 | dev_warn(&adev->dev, "Failed to get memory region resource\n"); | 290 | dev_warn(&adev->dev, "Failed to get memory region resource\n"); |
291 | ret = -ENOENT; | 291 | ret = -ENOENT; |
292 | goto err; | 292 | goto err; |
293 | } | 293 | } |
294 | 294 | ||
295 | wdt = kzalloc(sizeof(*wdt), GFP_KERNEL); | 295 | wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL); |
296 | if (!wdt) { | 296 | if (!wdt) { |
297 | dev_warn(&adev->dev, "Kzalloc failed\n"); | 297 | dev_warn(&adev->dev, "Kzalloc failed\n"); |
298 | ret = -ENOMEM; | 298 | ret = -ENOMEM; |
299 | goto err_kzalloc; | 299 | goto err; |
300 | } | ||
301 | |||
302 | wdt->base = devm_ioremap(&adev->dev, adev->res.start, | ||
303 | resource_size(&adev->res)); | ||
304 | if (!wdt->base) { | ||
305 | ret = -ENOMEM; | ||
306 | dev_warn(&adev->dev, "ioremap fail\n"); | ||
307 | goto err; | ||
300 | } | 308 | } |
301 | 309 | ||
302 | wdt->clk = clk_get(&adev->dev, NULL); | 310 | wdt->clk = clk_get(&adev->dev, NULL); |
303 | if (IS_ERR(wdt->clk)) { | 311 | if (IS_ERR(wdt->clk)) { |
304 | dev_warn(&adev->dev, "Clock not found\n"); | 312 | dev_warn(&adev->dev, "Clock not found\n"); |
305 | ret = PTR_ERR(wdt->clk); | 313 | ret = PTR_ERR(wdt->clk); |
306 | goto err_clk_get; | 314 | goto err; |
307 | } | ||
308 | |||
309 | wdt->base = ioremap(adev->res.start, resource_size(&adev->res)); | ||
310 | if (!wdt->base) { | ||
311 | ret = -ENOMEM; | ||
312 | dev_warn(&adev->dev, "ioremap fail\n"); | ||
313 | goto err_ioremap; | ||
314 | } | 315 | } |
315 | 316 | ||
316 | wdt->adev = adev; | 317 | wdt->adev = adev; |
@@ -327,14 +328,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) | |||
327 | return 0; | 328 | return 0; |
328 | 329 | ||
329 | err_misc_register: | 330 | err_misc_register: |
330 | iounmap(wdt->base); | ||
331 | err_ioremap: | ||
332 | clk_put(wdt->clk); | 331 | clk_put(wdt->clk); |
333 | err_clk_get: | ||
334 | kfree(wdt); | ||
335 | wdt = NULL; | ||
336 | err_kzalloc: | ||
337 | release_mem_region(adev->res.start, resource_size(&adev->res)); | ||
338 | err: | 332 | err: |
339 | dev_err(&adev->dev, "Probe Failed!!!\n"); | 333 | dev_err(&adev->dev, "Probe Failed!!!\n"); |
340 | return ret; | 334 | return ret; |
@@ -343,14 +337,42 @@ err: | |||
343 | static int __devexit sp805_wdt_remove(struct amba_device *adev) | 337 | static int __devexit sp805_wdt_remove(struct amba_device *adev) |
344 | { | 338 | { |
345 | misc_deregister(&sp805_wdt_miscdev); | 339 | misc_deregister(&sp805_wdt_miscdev); |
346 | iounmap(wdt->base); | ||
347 | clk_put(wdt->clk); | 340 | clk_put(wdt->clk); |
348 | kfree(wdt); | ||
349 | release_mem_region(adev->res.start, resource_size(&adev->res)); | ||
350 | 341 | ||
351 | return 0; | 342 | return 0; |
352 | } | 343 | } |
353 | 344 | ||
345 | #ifdef CONFIG_PM | ||
346 | static int sp805_wdt_suspend(struct device *dev) | ||
347 | { | ||
348 | if (test_bit(WDT_BUSY, &wdt->status)) { | ||
349 | wdt_disable(); | ||
350 | clk_disable(wdt->clk); | ||
351 | } | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static int sp805_wdt_resume(struct device *dev) | ||
357 | { | ||
358 | int ret = 0; | ||
359 | |||
360 | if (test_bit(WDT_BUSY, &wdt->status)) { | ||
361 | ret = clk_enable(wdt->clk); | ||
362 | if (ret) { | ||
363 | dev_err(dev, "clock enable fail"); | ||
364 | return ret; | ||
365 | } | ||
366 | wdt_enable(); | ||
367 | } | ||
368 | |||
369 | return ret; | ||
370 | } | ||
371 | #endif /* CONFIG_PM */ | ||
372 | |||
373 | static SIMPLE_DEV_PM_OPS(sp805_wdt_dev_pm_ops, sp805_wdt_suspend, | ||
374 | sp805_wdt_resume); | ||
375 | |||
354 | static struct amba_id sp805_wdt_ids[] = { | 376 | static struct amba_id sp805_wdt_ids[] = { |
355 | { | 377 | { |
356 | .id = 0x00141805, | 378 | .id = 0x00141805, |
@@ -364,6 +386,7 @@ MODULE_DEVICE_TABLE(amba, sp805_wdt_ids); | |||
364 | static struct amba_driver sp805_wdt_driver = { | 386 | static struct amba_driver sp805_wdt_driver = { |
365 | .drv = { | 387 | .drv = { |
366 | .name = MODULE_NAME, | 388 | .name = MODULE_NAME, |
389 | .pm = &sp805_wdt_dev_pm_ops, | ||
367 | }, | 390 | }, |
368 | .id_table = sp805_wdt_ids, | 391 | .id_table = sp805_wdt_ids, |
369 | .probe = sp805_wdt_probe, | 392 | .probe = sp805_wdt_probe, |
@@ -372,7 +395,7 @@ static struct amba_driver sp805_wdt_driver = { | |||
372 | 395 | ||
373 | module_amba_driver(sp805_wdt_driver); | 396 | module_amba_driver(sp805_wdt_driver); |
374 | 397 | ||
375 | module_param(nowayout, int, 0); | 398 | module_param(nowayout, bool, 0); |
376 | MODULE_PARM_DESC(nowayout, | 399 | MODULE_PARM_DESC(nowayout, |
377 | "Set to 1 to keep watchdog running after device release"); | 400 | "Set to 1 to keep watchdog running after device release"); |
378 | 401 | ||