diff options
-rw-r--r-- | arch/arm/mach-orion5x/common.c | 29 | ||||
-rw-r--r-- | arch/arm/plat-orion/include/plat/orion5x_wdt.h | 18 | ||||
-rw-r--r-- | drivers/watchdog/orion5x_wdt.c | 76 |
3 files changed, 109 insertions, 14 deletions
diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c index 8a0e49d84256..1a1df24f419e 100644 --- a/arch/arm/mach-orion5x/common.c +++ b/arch/arm/mach-orion5x/common.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <plat/ehci-orion.h> | 31 | #include <plat/ehci-orion.h> |
32 | #include <plat/mv_xor.h> | 32 | #include <plat/mv_xor.h> |
33 | #include <plat/orion_nand.h> | 33 | #include <plat/orion_nand.h> |
34 | #include <plat/orion5x_wdt.h> | ||
34 | #include <plat/time.h> | 35 | #include <plat/time.h> |
35 | #include "common.h" | 36 | #include "common.h" |
36 | 37 | ||
@@ -533,6 +534,29 @@ void __init orion5x_xor_init(void) | |||
533 | 534 | ||
534 | 535 | ||
535 | /***************************************************************************** | 536 | /***************************************************************************** |
537 | * Watchdog | ||
538 | ****************************************************************************/ | ||
539 | static struct orion5x_wdt_platform_data orion5x_wdt_data = { | ||
540 | .tclk = 0, | ||
541 | }; | ||
542 | |||
543 | static struct platform_device orion5x_wdt_device = { | ||
544 | .name = "orion5x_wdt", | ||
545 | .id = -1, | ||
546 | .dev = { | ||
547 | .platform_data = &orion5x_wdt_data, | ||
548 | }, | ||
549 | .num_resources = 0, | ||
550 | }; | ||
551 | |||
552 | void __init orion5x_wdt_init(void) | ||
553 | { | ||
554 | orion5x_wdt_data.tclk = orion5x_tclk; | ||
555 | platform_device_register(&orion5x_wdt_device); | ||
556 | } | ||
557 | |||
558 | |||
559 | /***************************************************************************** | ||
536 | * Time handling | 560 | * Time handling |
537 | ****************************************************************************/ | 561 | ****************************************************************************/ |
538 | int orion5x_tclk; | 562 | int orion5x_tclk; |
@@ -631,6 +655,11 @@ void __init orion5x_init(void) | |||
631 | printk(KERN_INFO "Orion: Applying 5281 D0 WFI workaround.\n"); | 655 | printk(KERN_INFO "Orion: Applying 5281 D0 WFI workaround.\n"); |
632 | disable_hlt(); | 656 | disable_hlt(); |
633 | } | 657 | } |
658 | |||
659 | /* | ||
660 | * Register watchdog driver | ||
661 | */ | ||
662 | orion5x_wdt_init(); | ||
634 | } | 663 | } |
635 | 664 | ||
636 | /* | 665 | /* |
diff --git a/arch/arm/plat-orion/include/plat/orion5x_wdt.h b/arch/arm/plat-orion/include/plat/orion5x_wdt.h new file mode 100644 index 000000000000..3c9cf6a305ef --- /dev/null +++ b/arch/arm/plat-orion/include/plat/orion5x_wdt.h | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * arch/arm/plat-orion/include/plat/orion5x_wdt.h | ||
3 | * | ||
4 | * This file is licensed under the terms of the GNU General Public | ||
5 | * License version 2. This program is licensed "as is" without any | ||
6 | * warranty of any kind, whether express or implied. | ||
7 | */ | ||
8 | |||
9 | #ifndef __PLAT_ORION5X_WDT_H | ||
10 | #define __PLAT_ORION5X_WDT_H | ||
11 | |||
12 | struct orion5x_wdt_platform_data { | ||
13 | u32 tclk; /* no <linux/clk.h> support yet */ | ||
14 | }; | ||
15 | |||
16 | |||
17 | #endif | ||
18 | |||
diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c index b64ae1a17832..e81441f103dd 100644 --- a/drivers/watchdog/orion5x_wdt.c +++ b/drivers/watchdog/orion5x_wdt.c | |||
@@ -16,11 +16,13 @@ | |||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
18 | #include <linux/miscdevice.h> | 18 | #include <linux/miscdevice.h> |
19 | #include <linux/platform_device.h> | ||
19 | #include <linux/watchdog.h> | 20 | #include <linux/watchdog.h> |
20 | #include <linux/init.h> | 21 | #include <linux/init.h> |
21 | #include <linux/uaccess.h> | 22 | #include <linux/uaccess.h> |
22 | #include <linux/io.h> | 23 | #include <linux/io.h> |
23 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
25 | #include <plat/orion5x_wdt.h> | ||
24 | 26 | ||
25 | /* | 27 | /* |
26 | * Watchdog timer block registers. | 28 | * Watchdog timer block registers. |
@@ -29,13 +31,14 @@ | |||
29 | #define WDT_EN 0x0010 | 31 | #define WDT_EN 0x0010 |
30 | #define WDT_VAL (TIMER_VIRT_BASE + 0x0024) | 32 | #define WDT_VAL (TIMER_VIRT_BASE + 0x0024) |
31 | 33 | ||
32 | #define ORION5X_TCLK 166666667 | 34 | #define WDT_MAX_CYCLE_COUNT 0xffffffff |
33 | #define WDT_MAX_DURATION (0xffffffff / ORION5X_TCLK) | ||
34 | #define WDT_IN_USE 0 | 35 | #define WDT_IN_USE 0 |
35 | #define WDT_OK_TO_CLOSE 1 | 36 | #define WDT_OK_TO_CLOSE 1 |
36 | 37 | ||
37 | static int nowayout = WATCHDOG_NOWAYOUT; | 38 | static int nowayout = WATCHDOG_NOWAYOUT; |
38 | static int heartbeat = WDT_MAX_DURATION; /* (seconds) */ | 39 | static int heartbeat = -1; /* module parameter (seconds) */ |
40 | static unsigned int wdt_max_duration; /* (seconds) */ | ||
41 | static unsigned int wdt_tclk; | ||
39 | static unsigned long wdt_status; | 42 | static unsigned long wdt_status; |
40 | static spinlock_t wdt_lock; | 43 | static spinlock_t wdt_lock; |
41 | 44 | ||
@@ -46,7 +49,7 @@ static void wdt_enable(void) | |||
46 | spin_lock(&wdt_lock); | 49 | spin_lock(&wdt_lock); |
47 | 50 | ||
48 | /* Set watchdog duration */ | 51 | /* Set watchdog duration */ |
49 | writel(ORION5X_TCLK * heartbeat, WDT_VAL); | 52 | writel(wdt_tclk * heartbeat, WDT_VAL); |
50 | 53 | ||
51 | /* Clear watchdog timer interrupt */ | 54 | /* Clear watchdog timer interrupt */ |
52 | reg = readl(BRIDGE_CAUSE); | 55 | reg = readl(BRIDGE_CAUSE); |
@@ -88,7 +91,7 @@ static void wdt_disable(void) | |||
88 | static int orion5x_wdt_get_timeleft(int *time_left) | 91 | static int orion5x_wdt_get_timeleft(int *time_left) |
89 | { | 92 | { |
90 | spin_lock(&wdt_lock); | 93 | spin_lock(&wdt_lock); |
91 | *time_left = readl(WDT_VAL) / ORION5X_TCLK; | 94 | *time_left = readl(WDT_VAL) / wdt_tclk; |
92 | spin_unlock(&wdt_lock); | 95 | spin_unlock(&wdt_lock); |
93 | return 0; | 96 | return 0; |
94 | } | 97 | } |
@@ -158,7 +161,7 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd, | |||
158 | if (ret) | 161 | if (ret) |
159 | break; | 162 | break; |
160 | 163 | ||
161 | if (time <= 0 || time > WDT_MAX_DURATION) { | 164 | if (time <= 0 || time > wdt_max_duration) { |
162 | ret = -EINVAL; | 165 | ret = -EINVAL; |
163 | break; | 166 | break; |
164 | } | 167 | } |
@@ -210,23 +213,69 @@ static struct miscdevice orion5x_wdt_miscdev = { | |||
210 | .fops = &orion5x_wdt_fops, | 213 | .fops = &orion5x_wdt_fops, |
211 | }; | 214 | }; |
212 | 215 | ||
213 | static int __init orion5x_wdt_init(void) | 216 | static int __devinit orion5x_wdt_probe(struct platform_device *pdev) |
214 | { | 217 | { |
218 | struct orion5x_wdt_platform_data *pdata = pdev->dev.platform_data; | ||
215 | int ret; | 219 | int ret; |
216 | 220 | ||
217 | spin_lock_init(&wdt_lock); | 221 | if (pdata) { |
222 | wdt_tclk = pdata->tclk; | ||
223 | } else { | ||
224 | printk(KERN_ERR "Orion5x Watchdog misses platform data\n"); | ||
225 | return -ENODEV; | ||
226 | } | ||
227 | |||
228 | if (orion5x_wdt_miscdev.parent) | ||
229 | return -EBUSY; | ||
230 | orion5x_wdt_miscdev.parent = &pdev->dev; | ||
231 | |||
232 | wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; | ||
233 | if (heartbeat <= 0 || heartbeat > wdt_max_duration) | ||
234 | heartbeat = wdt_max_duration; | ||
218 | 235 | ||
219 | ret = misc_register(&orion5x_wdt_miscdev); | 236 | ret = misc_register(&orion5x_wdt_miscdev); |
220 | if (ret == 0) | 237 | if (ret) |
221 | printk("Orion5x Watchdog Timer: heartbeat %d sec\n", | 238 | return ret; |
222 | heartbeat); | 239 | |
240 | printk(KERN_INFO "Orion5x Watchdog Timer: Initial timeout %d sec%s\n", | ||
241 | heartbeat, nowayout ? ", nowayout" : ""); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static int __devexit orion5x_wdt_remove(struct platform_device *pdev) | ||
246 | { | ||
247 | int ret; | ||
248 | |||
249 | if (test_bit(WDT_IN_USE, &wdt_status)) { | ||
250 | wdt_disable(); | ||
251 | clear_bit(WDT_IN_USE, &wdt_status); | ||
252 | } | ||
253 | |||
254 | ret = misc_deregister(&orion5x_wdt_miscdev); | ||
255 | if (!ret) | ||
256 | orion5x_wdt_miscdev.parent = NULL; | ||
223 | 257 | ||
224 | return ret; | 258 | return ret; |
225 | } | 259 | } |
226 | 260 | ||
261 | static struct platform_driver orion5x_wdt_driver = { | ||
262 | .probe = orion5x_wdt_probe, | ||
263 | .remove = __devexit_p(orion5x_wdt_remove), | ||
264 | .driver = { | ||
265 | .owner = THIS_MODULE, | ||
266 | .name = "orion5x_wdt", | ||
267 | }, | ||
268 | }; | ||
269 | |||
270 | static int __init orion5x_wdt_init(void) | ||
271 | { | ||
272 | spin_lock_init(&wdt_lock); | ||
273 | return platform_driver_register(&orion5x_wdt_driver); | ||
274 | } | ||
275 | |||
227 | static void __exit orion5x_wdt_exit(void) | 276 | static void __exit orion5x_wdt_exit(void) |
228 | { | 277 | { |
229 | misc_deregister(&orion5x_wdt_miscdev); | 278 | platform_driver_unregister(&orion5x_wdt_driver); |
230 | } | 279 | } |
231 | 280 | ||
232 | module_init(orion5x_wdt_init); | 281 | module_init(orion5x_wdt_init); |
@@ -236,8 +285,7 @@ MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>"); | |||
236 | MODULE_DESCRIPTION("Orion5x Processor Watchdog"); | 285 | MODULE_DESCRIPTION("Orion5x Processor Watchdog"); |
237 | 286 | ||
238 | module_param(heartbeat, int, 0); | 287 | module_param(heartbeat, int, 0); |
239 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default is " | 288 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds"); |
240 | __MODULE_STRING(WDT_MAX_DURATION) ")"); | ||
241 | 289 | ||
242 | module_param(nowayout, int, 0); | 290 | module_param(nowayout, int, 0); |
243 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); | 291 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); |