diff options
Diffstat (limited to 'drivers/watchdog/ux500_wdt.c')
-rw-r--r-- | drivers/watchdog/ux500_wdt.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c new file mode 100644 index 000000000000..a614d84121c3 --- /dev/null +++ b/drivers/watchdog/ux500_wdt.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * Copyright (C) ST-Ericsson SA 2011-2013 | ||
3 | * | ||
4 | * License Terms: GNU General Public License v2 | ||
5 | * | ||
6 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> for ST-Ericsson | ||
7 | * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson | ||
8 | */ | ||
9 | |||
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/miscdevice.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/uaccess.h> | ||
18 | #include <linux/watchdog.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/platform_data/ux500_wdt.h> | ||
21 | |||
22 | #include <linux/mfd/dbx500-prcmu.h> | ||
23 | |||
24 | #define WATCHDOG_TIMEOUT 600 /* 10 minutes */ | ||
25 | |||
26 | #define WATCHDOG_MIN 0 | ||
27 | #define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ | ||
28 | #define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */ | ||
29 | |||
30 | static unsigned int timeout = WATCHDOG_TIMEOUT; | ||
31 | module_param(timeout, uint, 0); | ||
32 | MODULE_PARM_DESC(timeout, | ||
33 | "Watchdog timeout in seconds. default=" | ||
34 | __MODULE_STRING(WATCHDOG_TIMEOUT) "."); | ||
35 | |||
36 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
37 | module_param(nowayout, bool, 0); | ||
38 | MODULE_PARM_DESC(nowayout, | ||
39 | "Watchdog cannot be stopped once started (default=" | ||
40 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
41 | |||
42 | static int ux500_wdt_start(struct watchdog_device *wdd) | ||
43 | { | ||
44 | return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); | ||
45 | } | ||
46 | |||
47 | static int ux500_wdt_stop(struct watchdog_device *wdd) | ||
48 | { | ||
49 | return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); | ||
50 | } | ||
51 | |||
52 | static int ux500_wdt_keepalive(struct watchdog_device *wdd) | ||
53 | { | ||
54 | return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); | ||
55 | } | ||
56 | |||
57 | static int ux500_wdt_set_timeout(struct watchdog_device *wdd, | ||
58 | unsigned int timeout) | ||
59 | { | ||
60 | ux500_wdt_stop(wdd); | ||
61 | prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); | ||
62 | ux500_wdt_start(wdd); | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static const struct watchdog_info ux500_wdt_info = { | ||
68 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
69 | .identity = "Ux500 WDT", | ||
70 | .firmware_version = 1, | ||
71 | }; | ||
72 | |||
73 | static const struct watchdog_ops ux500_wdt_ops = { | ||
74 | .owner = THIS_MODULE, | ||
75 | .start = ux500_wdt_start, | ||
76 | .stop = ux500_wdt_stop, | ||
77 | .ping = ux500_wdt_keepalive, | ||
78 | .set_timeout = ux500_wdt_set_timeout, | ||
79 | }; | ||
80 | |||
81 | static struct watchdog_device ux500_wdt = { | ||
82 | .info = &ux500_wdt_info, | ||
83 | .ops = &ux500_wdt_ops, | ||
84 | .min_timeout = WATCHDOG_MIN, | ||
85 | .max_timeout = WATCHDOG_MAX32, | ||
86 | }; | ||
87 | |||
88 | static int ux500_wdt_probe(struct platform_device *pdev) | ||
89 | { | ||
90 | int ret; | ||
91 | struct ux500_wdt_data *pdata = pdev->dev.platform_data; | ||
92 | |||
93 | if (pdata) { | ||
94 | if (pdata->timeout > 0) | ||
95 | timeout = pdata->timeout; | ||
96 | if (pdata->has_28_bits_resolution) | ||
97 | ux500_wdt.max_timeout = WATCHDOG_MAX28; | ||
98 | } | ||
99 | |||
100 | watchdog_set_nowayout(&ux500_wdt, nowayout); | ||
101 | |||
102 | /* disable auto off on sleep */ | ||
103 | prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); | ||
104 | |||
105 | /* set HW initial value */ | ||
106 | prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); | ||
107 | |||
108 | ret = watchdog_register_device(&ux500_wdt); | ||
109 | if (ret) | ||
110 | return ret; | ||
111 | |||
112 | dev_info(&pdev->dev, "initialized\n"); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int ux500_wdt_remove(struct platform_device *dev) | ||
118 | { | ||
119 | watchdog_unregister_device(&ux500_wdt); | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | #ifdef CONFIG_PM | ||
125 | static int ux500_wdt_suspend(struct platform_device *pdev, | ||
126 | pm_message_t state) | ||
127 | { | ||
128 | if (watchdog_active(&ux500_wdt)) { | ||
129 | ux500_wdt_stop(&ux500_wdt); | ||
130 | prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); | ||
131 | |||
132 | prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); | ||
133 | ux500_wdt_start(&ux500_wdt); | ||
134 | } | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int ux500_wdt_resume(struct platform_device *pdev) | ||
139 | { | ||
140 | if (watchdog_active(&ux500_wdt)) { | ||
141 | ux500_wdt_stop(&ux500_wdt); | ||
142 | prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); | ||
143 | |||
144 | prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); | ||
145 | ux500_wdt_start(&ux500_wdt); | ||
146 | } | ||
147 | return 0; | ||
148 | } | ||
149 | #else | ||
150 | #define ux500_wdt_suspend NULL | ||
151 | #define ux500_wdt_resume NULL | ||
152 | #endif | ||
153 | |||
154 | static struct platform_driver ux500_wdt_driver = { | ||
155 | .probe = ux500_wdt_probe, | ||
156 | .remove = ux500_wdt_remove, | ||
157 | .suspend = ux500_wdt_suspend, | ||
158 | .resume = ux500_wdt_resume, | ||
159 | .driver = { | ||
160 | .owner = THIS_MODULE, | ||
161 | .name = "ux500_wdt", | ||
162 | }, | ||
163 | }; | ||
164 | |||
165 | module_platform_driver(ux500_wdt_driver); | ||
166 | |||
167 | MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>"); | ||
168 | MODULE_DESCRIPTION("Ux500 Watchdog Driver"); | ||
169 | MODULE_LICENSE("GPL"); | ||
170 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
171 | MODULE_ALIAS("platform:ux500_wdt"); | ||