diff options
-rw-r--r-- | drivers/watchdog/Kconfig | 1 | ||||
-rw-r--r-- | drivers/watchdog/booke_wdt.c | 185 |
2 files changed, 66 insertions, 120 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 100caabf7fc8..beded63689d1 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -1145,6 +1145,7 @@ config PIKA_WDT | |||
1145 | config BOOKE_WDT | 1145 | config BOOKE_WDT |
1146 | tristate "PowerPC Book-E Watchdog Timer" | 1146 | tristate "PowerPC Book-E Watchdog Timer" |
1147 | depends on BOOKE || 4xx | 1147 | depends on BOOKE || 4xx |
1148 | select WATCHDOG_CORE | ||
1148 | ---help--- | 1149 | ---help--- |
1149 | Watchdog driver for PowerPC Book-E chips, such as the Freescale | 1150 | Watchdog driver for PowerPC Book-E chips, such as the Freescale |
1150 | MPC85xx SOCs and the IBM PowerPC 440. | 1151 | MPC85xx SOCs and the IBM PowerPC 440. |
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index c0bc92d8e438..a8dbceb32914 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c | |||
@@ -15,12 +15,8 @@ | |||
15 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 15 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
16 | 16 | ||
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/fs.h> | ||
19 | #include <linux/smp.h> | 18 | #include <linux/smp.h> |
20 | #include <linux/miscdevice.h> | ||
21 | #include <linux/notifier.h> | ||
22 | #include <linux/watchdog.h> | 19 | #include <linux/watchdog.h> |
23 | #include <linux/uaccess.h> | ||
24 | 20 | ||
25 | #include <asm/reg_booke.h> | 21 | #include <asm/reg_booke.h> |
26 | #include <asm/time.h> | 22 | #include <asm/time.h> |
@@ -45,7 +41,7 @@ u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; | |||
45 | #define WDTP_MASK (TCR_WP_MASK) | 41 | #define WDTP_MASK (TCR_WP_MASK) |
46 | #endif | 42 | #endif |
47 | 43 | ||
48 | static DEFINE_SPINLOCK(booke_wdt_lock); | 44 | #ifdef CONFIG_PPC_FSL_BOOK3E |
49 | 45 | ||
50 | /* For the specified period, determine the number of seconds | 46 | /* For the specified period, determine the number of seconds |
51 | * corresponding to the reset time. There will be a watchdog | 47 | * corresponding to the reset time. There will be a watchdog |
@@ -86,6 +82,24 @@ static unsigned int sec_to_period(unsigned int secs) | |||
86 | return 0; | 82 | return 0; |
87 | } | 83 | } |
88 | 84 | ||
85 | #define MAX_WDT_TIMEOUT period_to_sec(1) | ||
86 | |||
87 | #else /* CONFIG_PPC_FSL_BOOK3E */ | ||
88 | |||
89 | static unsigned long long period_to_sec(unsigned int period) | ||
90 | { | ||
91 | return period; | ||
92 | } | ||
93 | |||
94 | static unsigned int sec_to_period(unsigned int secs) | ||
95 | { | ||
96 | return secs; | ||
97 | } | ||
98 | |||
99 | #define MAX_WDT_TIMEOUT 3 /* from Kconfig */ | ||
100 | |||
101 | #endif /* !CONFIG_PPC_FSL_BOOK3E */ | ||
102 | |||
89 | static void __booke_wdt_set(void *data) | 103 | static void __booke_wdt_set(void *data) |
90 | { | 104 | { |
91 | u32 val; | 105 | u32 val; |
@@ -107,9 +121,11 @@ static void __booke_wdt_ping(void *data) | |||
107 | mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); | 121 | mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); |
108 | } | 122 | } |
109 | 123 | ||
110 | static void booke_wdt_ping(void) | 124 | static int booke_wdt_ping(struct watchdog_device *wdog) |
111 | { | 125 | { |
112 | on_each_cpu(__booke_wdt_ping, NULL, 0); | 126 | on_each_cpu(__booke_wdt_ping, NULL, 0); |
127 | |||
128 | return 0; | ||
113 | } | 129 | } |
114 | 130 | ||
115 | static void __booke_wdt_enable(void *data) | 131 | static void __booke_wdt_enable(void *data) |
@@ -146,152 +162,81 @@ static void __booke_wdt_disable(void *data) | |||
146 | 162 | ||
147 | } | 163 | } |
148 | 164 | ||
149 | static ssize_t booke_wdt_write(struct file *file, const char __user *buf, | 165 | static void __booke_wdt_start(struct watchdog_device *wdog) |
150 | size_t count, loff_t *ppos) | ||
151 | { | 166 | { |
152 | booke_wdt_ping(); | 167 | on_each_cpu(__booke_wdt_enable, NULL, 0); |
153 | return count; | 168 | pr_debug("watchdog enabled (timeout = %u sec)\n", wdog->timeout); |
154 | } | 169 | } |
155 | 170 | ||
156 | static struct watchdog_info ident = { | 171 | static int booke_wdt_start(struct watchdog_device *wdog) |
157 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
158 | .identity = "PowerPC Book-E Watchdog", | ||
159 | }; | ||
160 | |||
161 | static long booke_wdt_ioctl(struct file *file, | ||
162 | unsigned int cmd, unsigned long arg) | ||
163 | { | 172 | { |
164 | u32 tmp = 0; | ||
165 | u32 __user *p = (u32 __user *)arg; | ||
166 | |||
167 | switch (cmd) { | ||
168 | case WDIOC_GETSUPPORT: | ||
169 | return copy_to_user(p, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
170 | case WDIOC_GETSTATUS: | ||
171 | return put_user(0, p); | ||
172 | case WDIOC_GETBOOTSTATUS: | ||
173 | /* XXX: something is clearing TSR */ | ||
174 | tmp = mfspr(SPRN_TSR) & TSR_WRS(3); | ||
175 | /* returns CARDRESET if last reset was caused by the WDT */ | ||
176 | return put_user((tmp ? WDIOF_CARDRESET : 0), p); | ||
177 | case WDIOC_SETOPTIONS: | ||
178 | if (get_user(tmp, p)) | ||
179 | return -EFAULT; | ||
180 | if (tmp == WDIOS_ENABLECARD) { | ||
181 | booke_wdt_ping(); | ||
182 | break; | ||
183 | } else | ||
184 | return -EINVAL; | ||
185 | return 0; | ||
186 | case WDIOC_KEEPALIVE: | ||
187 | booke_wdt_ping(); | ||
188 | return 0; | ||
189 | case WDIOC_SETTIMEOUT: | ||
190 | if (get_user(tmp, p)) | ||
191 | return -EFAULT; | ||
192 | #ifdef CONFIG_PPC_FSL_BOOK3E | ||
193 | /* period of 1 gives the largest possible timeout */ | ||
194 | if (tmp > period_to_sec(1)) | ||
195 | return -EINVAL; | ||
196 | booke_wdt_period = sec_to_period(tmp); | ||
197 | #else | ||
198 | booke_wdt_period = tmp; | ||
199 | #endif | ||
200 | booke_wdt_set(); | ||
201 | /* Fall */ | ||
202 | case WDIOC_GETTIMEOUT: | ||
203 | #ifdef CONFIG_FSL_BOOKE | ||
204 | return put_user(period_to_sec(booke_wdt_period), p); | ||
205 | #else | ||
206 | return put_user(booke_wdt_period, p); | ||
207 | #endif | ||
208 | default: | ||
209 | return -ENOTTY; | ||
210 | } | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | /* wdt_is_active stores whether or not the /dev/watchdog device is opened */ | ||
216 | static unsigned long wdt_is_active; | ||
217 | |||
218 | static int booke_wdt_open(struct inode *inode, struct file *file) | ||
219 | { | ||
220 | /* /dev/watchdog can only be opened once */ | ||
221 | if (test_and_set_bit(0, &wdt_is_active)) | ||
222 | return -EBUSY; | ||
223 | |||
224 | spin_lock(&booke_wdt_lock); | ||
225 | if (booke_wdt_enabled == 0) { | 173 | if (booke_wdt_enabled == 0) { |
226 | booke_wdt_enabled = 1; | 174 | booke_wdt_enabled = 1; |
227 | on_each_cpu(__booke_wdt_enable, NULL, 0); | 175 | __booke_wdt_start(wdog); |
228 | pr_debug("watchdog enabled (timeout = %llu sec)\n", | ||
229 | period_to_sec(booke_wdt_period)); | ||
230 | } | 176 | } |
231 | spin_unlock(&booke_wdt_lock); | 177 | return 0; |
232 | |||
233 | return nonseekable_open(inode, file); | ||
234 | } | 178 | } |
235 | 179 | ||
236 | static int booke_wdt_release(struct inode *inode, struct file *file) | 180 | static int booke_wdt_stop(struct watchdog_device *wdog) |
237 | { | 181 | { |
238 | #ifndef CONFIG_WATCHDOG_NOWAYOUT | ||
239 | /* Normally, the watchdog is disabled when /dev/watchdog is closed, but | ||
240 | * if CONFIG_WATCHDOG_NOWAYOUT is defined, then it means that the | ||
241 | * watchdog should remain enabled. So we disable it only if | ||
242 | * CONFIG_WATCHDOG_NOWAYOUT is not defined. | ||
243 | */ | ||
244 | on_each_cpu(__booke_wdt_disable, NULL, 0); | 182 | on_each_cpu(__booke_wdt_disable, NULL, 0); |
245 | booke_wdt_enabled = 0; | 183 | booke_wdt_enabled = 0; |
246 | pr_debug("watchdog disabled\n"); | 184 | pr_debug("watchdog disabled\n"); |
247 | #endif | ||
248 | 185 | ||
249 | clear_bit(0, &wdt_is_active); | 186 | return 0; |
187 | } | ||
188 | |||
189 | static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev, | ||
190 | unsigned int timeout) | ||
191 | { | ||
192 | if (timeout > MAX_WDT_TIMEOUT) | ||
193 | return -EINVAL; | ||
194 | booke_wdt_period = sec_to_period(timeout); | ||
195 | wdt_dev->timeout = timeout; | ||
196 | booke_wdt_set(); | ||
250 | 197 | ||
251 | return 0; | 198 | return 0; |
252 | } | 199 | } |
253 | 200 | ||
254 | static const struct file_operations booke_wdt_fops = { | 201 | static struct watchdog_info booke_wdt_info = { |
202 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
203 | .identity = "PowerPC Book-E Watchdog", | ||
204 | }; | ||
205 | |||
206 | static struct watchdog_ops booke_wdt_ops = { | ||
255 | .owner = THIS_MODULE, | 207 | .owner = THIS_MODULE, |
256 | .llseek = no_llseek, | 208 | .start = booke_wdt_start, |
257 | .write = booke_wdt_write, | 209 | .stop = booke_wdt_stop, |
258 | .unlocked_ioctl = booke_wdt_ioctl, | 210 | .ping = booke_wdt_ping, |
259 | .open = booke_wdt_open, | 211 | .set_timeout = booke_wdt_set_timeout, |
260 | .release = booke_wdt_release, | ||
261 | }; | 212 | }; |
262 | 213 | ||
263 | static struct miscdevice booke_wdt_miscdev = { | 214 | static struct watchdog_device booke_wdt_dev = { |
264 | .minor = WATCHDOG_MINOR, | 215 | .info = &booke_wdt_info, |
265 | .name = "watchdog", | 216 | .ops = &booke_wdt_ops, |
266 | .fops = &booke_wdt_fops, | 217 | .min_timeout = 1, |
218 | .max_timeout = 0xFFFF | ||
267 | }; | 219 | }; |
268 | 220 | ||
269 | static void __exit booke_wdt_exit(void) | 221 | static void __exit booke_wdt_exit(void) |
270 | { | 222 | { |
271 | misc_deregister(&booke_wdt_miscdev); | 223 | watchdog_unregister_device(&booke_wdt_dev); |
272 | } | 224 | } |
273 | 225 | ||
274 | static int __init booke_wdt_init(void) | 226 | static int __init booke_wdt_init(void) |
275 | { | 227 | { |
276 | int ret = 0; | 228 | int ret = 0; |
229 | bool nowayout = WATCHDOG_NOWAYOUT; | ||
277 | 230 | ||
278 | pr_info("powerpc book-e watchdog driver loaded\n"); | 231 | pr_info("powerpc book-e watchdog driver loaded\n"); |
279 | ident.firmware_version = cur_cpu_spec->pvr_value; | 232 | booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value; |
280 | 233 | booke_wdt_set_timeout(&booke_wdt_dev, | |
281 | ret = misc_register(&booke_wdt_miscdev); | 234 | period_to_sec(CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT)); |
282 | if (ret) { | 235 | watchdog_set_nowayout(&booke_wdt_dev, nowayout); |
283 | pr_err("cannot register device (minor=%u, ret=%i)\n", | 236 | if (booke_wdt_enabled) |
284 | WATCHDOG_MINOR, ret); | 237 | __booke_wdt_start(&booke_wdt_dev); |
285 | return ret; | 238 | |
286 | } | 239 | ret = watchdog_register_device(&booke_wdt_dev); |
287 | |||
288 | spin_lock(&booke_wdt_lock); | ||
289 | if (booke_wdt_enabled == 1) { | ||
290 | pr_info("watchdog enabled (timeout = %llu sec)\n", | ||
291 | period_to_sec(booke_wdt_period)); | ||
292 | on_each_cpu(__booke_wdt_enable, NULL, 0); | ||
293 | } | ||
294 | spin_unlock(&booke_wdt_lock); | ||
295 | 240 | ||
296 | return ret; | 241 | return ret; |
297 | } | 242 | } |