diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-10-04 11:43:40 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2011-11-05 16:28:59 -0400 |
commit | 00411ee9308e4b5f4b04caaa01685f955e259373 (patch) | |
tree | 5e614e61fdee4237ab0675651e8df52b24b9523f /drivers/watchdog | |
parent | 25dc46e3837cd01dc1742eefb73d064f6336850f (diff) |
watchdog: Convert wm831x driver to watchdog core
Fairly large code churn but not much doing with that and the overall
result is a definite win.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 1 | ||||
-rw-r--r-- | drivers/watchdog/wm831x_wdt.c | 318 |
2 files changed, 106 insertions, 213 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 40a36c50a5f4..6285867a9356 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -66,6 +66,7 @@ config SOFT_WATCHDOG | |||
66 | config WM831X_WATCHDOG | 66 | config WM831X_WATCHDOG |
67 | tristate "WM831x watchdog" | 67 | tristate "WM831x watchdog" |
68 | depends on MFD_WM831X | 68 | depends on MFD_WM831X |
69 | select WATCHDOG_CORE | ||
69 | help | 70 | help |
70 | Support for the watchdog in the WM831x AudioPlus PMICs. When | 71 | Support for the watchdog in the WM831x AudioPlus PMICs. When |
71 | the watchdog triggers the system will be reset. | 72 | the watchdog triggers the system will be reset. |
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c index 871caea4e1c6..7be38556aed0 100644 --- a/drivers/watchdog/wm831x_wdt.c +++ b/drivers/watchdog/wm831x_wdt.c | |||
@@ -12,8 +12,7 @@ | |||
12 | #include <linux/moduleparam.h> | 12 | #include <linux/moduleparam.h> |
13 | #include <linux/types.h> | 13 | #include <linux/types.h> |
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/fs.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/miscdevice.h> | ||
17 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
18 | #include <linux/watchdog.h> | 17 | #include <linux/watchdog.h> |
19 | #include <linux/uaccess.h> | 18 | #include <linux/uaccess.h> |
@@ -29,19 +28,19 @@ MODULE_PARM_DESC(nowayout, | |||
29 | "Watchdog cannot be stopped once started (default=" | 28 | "Watchdog cannot be stopped once started (default=" |
30 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 29 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
31 | 30 | ||
32 | static unsigned long wm831x_wdt_users; | 31 | struct wm831x_wdt_drvdata { |
33 | static struct miscdevice wm831x_wdt_miscdev; | 32 | struct watchdog_device wdt; |
34 | static int wm831x_wdt_expect_close; | 33 | struct wm831x *wm831x; |
35 | static DEFINE_MUTEX(wdt_mutex); | 34 | struct mutex lock; |
36 | static struct wm831x *wm831x; | 35 | int update_gpio; |
37 | static unsigned int update_gpio; | 36 | int update_state; |
38 | static unsigned int update_state; | 37 | }; |
39 | 38 | ||
40 | /* We can't use the sub-second values here but they're included | 39 | /* We can't use the sub-second values here but they're included |
41 | * for completeness. */ | 40 | * for completeness. */ |
42 | static struct { | 41 | static struct { |
43 | int time; /* Seconds */ | 42 | unsigned int time; /* Seconds */ |
44 | u16 val; /* WDOG_TO value */ | 43 | u16 val; /* WDOG_TO value */ |
45 | } wm831x_wdt_cfgs[] = { | 44 | } wm831x_wdt_cfgs[] = { |
46 | { 1, 2 }, | 45 | { 1, 2 }, |
47 | { 2, 3 }, | 46 | { 2, 3 }, |
@@ -52,32 +51,13 @@ static struct { | |||
52 | { 33, 7 }, /* Actually 32.768s so include both, others round down */ | 51 | { 33, 7 }, /* Actually 32.768s so include both, others round down */ |
53 | }; | 52 | }; |
54 | 53 | ||
55 | static int wm831x_wdt_set_timeout(struct wm831x *wm831x, u16 value) | 54 | static int wm831x_wdt_start(struct watchdog_device *wdt_dev) |
56 | { | ||
57 | int ret; | ||
58 | |||
59 | mutex_lock(&wdt_mutex); | ||
60 | |||
61 | ret = wm831x_reg_unlock(wm831x); | ||
62 | if (ret == 0) { | ||
63 | ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG, | ||
64 | WM831X_WDOG_TO_MASK, value); | ||
65 | wm831x_reg_lock(wm831x); | ||
66 | } else { | ||
67 | dev_err(wm831x->dev, "Failed to unlock security key: %d\n", | ||
68 | ret); | ||
69 | } | ||
70 | |||
71 | mutex_unlock(&wdt_mutex); | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static int wm831x_wdt_start(struct wm831x *wm831x) | ||
77 | { | 55 | { |
56 | struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev); | ||
57 | struct wm831x *wm831x = driver_data->wm831x; | ||
78 | int ret; | 58 | int ret; |
79 | 59 | ||
80 | mutex_lock(&wdt_mutex); | 60 | mutex_lock(&driver_data->lock); |
81 | 61 | ||
82 | ret = wm831x_reg_unlock(wm831x); | 62 | ret = wm831x_reg_unlock(wm831x); |
83 | if (ret == 0) { | 63 | if (ret == 0) { |
@@ -89,16 +69,18 @@ static int wm831x_wdt_start(struct wm831x *wm831x) | |||
89 | ret); | 69 | ret); |
90 | } | 70 | } |
91 | 71 | ||
92 | mutex_unlock(&wdt_mutex); | 72 | mutex_unlock(&driver_data->lock); |
93 | 73 | ||
94 | return ret; | 74 | return ret; |
95 | } | 75 | } |
96 | 76 | ||
97 | static int wm831x_wdt_stop(struct wm831x *wm831x) | 77 | static int wm831x_wdt_stop(struct watchdog_device *wdt_dev) |
98 | { | 78 | { |
79 | struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev); | ||
80 | struct wm831x *wm831x = driver_data->wm831x; | ||
99 | int ret; | 81 | int ret; |
100 | 82 | ||
101 | mutex_lock(&wdt_mutex); | 83 | mutex_lock(&driver_data->lock); |
102 | 84 | ||
103 | ret = wm831x_reg_unlock(wm831x); | 85 | ret = wm831x_reg_unlock(wm831x); |
104 | if (ret == 0) { | 86 | if (ret == 0) { |
@@ -110,26 +92,28 @@ static int wm831x_wdt_stop(struct wm831x *wm831x) | |||
110 | ret); | 92 | ret); |
111 | } | 93 | } |
112 | 94 | ||
113 | mutex_unlock(&wdt_mutex); | 95 | mutex_unlock(&driver_data->lock); |
114 | 96 | ||
115 | return ret; | 97 | return ret; |
116 | } | 98 | } |
117 | 99 | ||
118 | static int wm831x_wdt_kick(struct wm831x *wm831x) | 100 | static int wm831x_wdt_ping(struct watchdog_device *wdt_dev) |
119 | { | 101 | { |
102 | struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev); | ||
103 | struct wm831x *wm831x = driver_data->wm831x; | ||
120 | int ret; | 104 | int ret; |
121 | u16 reg; | 105 | u16 reg; |
122 | 106 | ||
123 | mutex_lock(&wdt_mutex); | 107 | mutex_lock(&driver_data->lock); |
124 | 108 | ||
125 | if (update_gpio) { | 109 | if (driver_data->update_gpio) { |
126 | gpio_set_value_cansleep(update_gpio, update_state); | 110 | gpio_set_value_cansleep(driver_data->update_gpio, |
127 | update_state = !update_state; | 111 | driver_data->update_state); |
112 | driver_data->update_state = !driver_data->update_state; | ||
128 | ret = 0; | 113 | ret = 0; |
129 | goto out; | 114 | goto out; |
130 | } | 115 | } |
131 | 116 | ||
132 | |||
133 | reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG); | 117 | reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG); |
134 | 118 | ||
135 | if (!(reg & WM831X_WDOG_RST_SRC)) { | 119 | if (!(reg & WM831X_WDOG_RST_SRC)) { |
@@ -150,182 +134,59 @@ static int wm831x_wdt_kick(struct wm831x *wm831x) | |||
150 | } | 134 | } |
151 | 135 | ||
152 | out: | 136 | out: |
153 | mutex_unlock(&wdt_mutex); | 137 | mutex_unlock(&driver_data->lock); |
154 | 138 | ||
155 | return ret; | 139 | return ret; |
156 | } | 140 | } |
157 | 141 | ||
158 | static int wm831x_wdt_open(struct inode *inode, struct file *file) | 142 | static int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev, |
143 | unsigned int timeout) | ||
159 | { | 144 | { |
160 | int ret; | 145 | struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev); |
161 | 146 | struct wm831x *wm831x = driver_data->wm831x; | |
162 | if (!wm831x) | 147 | int ret, i; |
163 | return -ENODEV; | ||
164 | |||
165 | if (test_and_set_bit(0, &wm831x_wdt_users)) | ||
166 | return -EBUSY; | ||
167 | 148 | ||
168 | ret = wm831x_wdt_start(wm831x); | 149 | for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++) |
169 | if (ret != 0) | 150 | if (wm831x_wdt_cfgs[i].time == timeout) |
170 | return ret; | 151 | break; |
171 | 152 | if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) | |
172 | return nonseekable_open(inode, file); | 153 | ret = -EINVAL; |
173 | } | ||
174 | 154 | ||
175 | static int wm831x_wdt_release(struct inode *inode, struct file *file) | 155 | ret = wm831x_reg_unlock(wm831x); |
176 | { | 156 | if (ret == 0) { |
177 | if (wm831x_wdt_expect_close) | 157 | ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG, |
178 | wm831x_wdt_stop(wm831x); | 158 | WM831X_WDOG_TO_MASK, |
179 | else { | 159 | wm831x_wdt_cfgs[i].val); |
180 | dev_warn(wm831x->dev, "Watchdog device closed uncleanly\n"); | 160 | wm831x_reg_lock(wm831x); |
181 | wm831x_wdt_kick(wm831x); | 161 | } else { |
162 | dev_err(wm831x->dev, "Failed to unlock security key: %d\n", | ||
163 | ret); | ||
182 | } | 164 | } |
183 | 165 | ||
184 | clear_bit(0, &wm831x_wdt_users); | 166 | return ret; |
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static ssize_t wm831x_wdt_write(struct file *file, | ||
190 | const char __user *data, size_t count, | ||
191 | loff_t *ppos) | ||
192 | { | ||
193 | size_t i; | ||
194 | |||
195 | if (count) { | ||
196 | wm831x_wdt_kick(wm831x); | ||
197 | |||
198 | if (!nowayout) { | ||
199 | /* In case it was set long ago */ | ||
200 | wm831x_wdt_expect_close = 0; | ||
201 | |||
202 | /* scan to see whether or not we got the magic | ||
203 | character */ | ||
204 | for (i = 0; i != count; i++) { | ||
205 | char c; | ||
206 | if (get_user(c, data + i)) | ||
207 | return -EFAULT; | ||
208 | if (c == 'V') | ||
209 | wm831x_wdt_expect_close = 42; | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | return count; | ||
214 | } | 167 | } |
215 | 168 | ||
216 | static const struct watchdog_info ident = { | 169 | static const struct watchdog_info wm831x_wdt_info = { |
217 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | 170 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
218 | .identity = "WM831x Watchdog", | 171 | .identity = "WM831x Watchdog", |
219 | }; | 172 | }; |
220 | 173 | ||
221 | static long wm831x_wdt_ioctl(struct file *file, unsigned int cmd, | 174 | static const struct watchdog_ops wm831x_wdt_ops = { |
222 | unsigned long arg) | ||
223 | { | ||
224 | int ret = -ENOTTY, time, i; | ||
225 | void __user *argp = (void __user *)arg; | ||
226 | int __user *p = argp; | ||
227 | u16 reg; | ||
228 | |||
229 | switch (cmd) { | ||
230 | case WDIOC_GETSUPPORT: | ||
231 | ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
232 | break; | ||
233 | |||
234 | case WDIOC_GETSTATUS: | ||
235 | case WDIOC_GETBOOTSTATUS: | ||
236 | ret = put_user(0, p); | ||
237 | break; | ||
238 | |||
239 | case WDIOC_SETOPTIONS: | ||
240 | { | ||
241 | int options; | ||
242 | |||
243 | if (get_user(options, p)) | ||
244 | return -EFAULT; | ||
245 | |||
246 | ret = -EINVAL; | ||
247 | |||
248 | /* Setting both simultaneously means at least one must fail */ | ||
249 | if (options == WDIOS_DISABLECARD) | ||
250 | ret = wm831x_wdt_start(wm831x); | ||
251 | |||
252 | if (options == WDIOS_ENABLECARD) | ||
253 | ret = wm831x_wdt_stop(wm831x); | ||
254 | break; | ||
255 | } | ||
256 | |||
257 | case WDIOC_KEEPALIVE: | ||
258 | ret = wm831x_wdt_kick(wm831x); | ||
259 | break; | ||
260 | |||
261 | case WDIOC_SETTIMEOUT: | ||
262 | ret = get_user(time, p); | ||
263 | if (ret) | ||
264 | break; | ||
265 | |||
266 | if (time == 0) { | ||
267 | if (nowayout) | ||
268 | ret = -EINVAL; | ||
269 | else | ||
270 | wm831x_wdt_stop(wm831x); | ||
271 | break; | ||
272 | } | ||
273 | |||
274 | for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++) | ||
275 | if (wm831x_wdt_cfgs[i].time == time) | ||
276 | break; | ||
277 | if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) | ||
278 | ret = -EINVAL; | ||
279 | else | ||
280 | ret = wm831x_wdt_set_timeout(wm831x, | ||
281 | wm831x_wdt_cfgs[i].val); | ||
282 | break; | ||
283 | |||
284 | case WDIOC_GETTIMEOUT: | ||
285 | reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG); | ||
286 | reg &= WM831X_WDOG_TO_MASK; | ||
287 | for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++) | ||
288 | if (wm831x_wdt_cfgs[i].val == reg) | ||
289 | break; | ||
290 | if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) { | ||
291 | dev_warn(wm831x->dev, | ||
292 | "Unknown watchdog configuration: %x\n", reg); | ||
293 | ret = -EINVAL; | ||
294 | } else | ||
295 | ret = put_user(wm831x_wdt_cfgs[i].time, p); | ||
296 | |||
297 | } | ||
298 | |||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | static const struct file_operations wm831x_wdt_fops = { | ||
303 | .owner = THIS_MODULE, | 175 | .owner = THIS_MODULE, |
304 | .llseek = no_llseek, | 176 | .start = wm831x_wdt_start, |
305 | .write = wm831x_wdt_write, | 177 | .stop = wm831x_wdt_stop, |
306 | .unlocked_ioctl = wm831x_wdt_ioctl, | 178 | .ping = wm831x_wdt_ping, |
307 | .open = wm831x_wdt_open, | 179 | .set_timeout = wm831x_wdt_set_timeout, |
308 | .release = wm831x_wdt_release, | ||
309 | }; | ||
310 | |||
311 | static struct miscdevice wm831x_wdt_miscdev = { | ||
312 | .minor = WATCHDOG_MINOR, | ||
313 | .name = "watchdog", | ||
314 | .fops = &wm831x_wdt_fops, | ||
315 | }; | 180 | }; |
316 | 181 | ||
317 | static int __devinit wm831x_wdt_probe(struct platform_device *pdev) | 182 | static int __devinit wm831x_wdt_probe(struct platform_device *pdev) |
318 | { | 183 | { |
184 | struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); | ||
319 | struct wm831x_pdata *chip_pdata; | 185 | struct wm831x_pdata *chip_pdata; |
320 | struct wm831x_watchdog_pdata *pdata; | 186 | struct wm831x_watchdog_pdata *pdata; |
321 | int reg, ret; | 187 | struct wm831x_wdt_drvdata *driver_data; |
322 | 188 | struct watchdog_device *wm831x_wdt; | |
323 | if (wm831x) { | 189 | int reg, ret, i; |
324 | dev_err(&pdev->dev, "wm831x watchdog already registered\n"); | ||
325 | return -EBUSY; | ||
326 | } | ||
327 | |||
328 | wm831x = dev_get_drvdata(pdev->dev.parent); | ||
329 | 190 | ||
330 | ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG); | 191 | ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG); |
331 | if (ret < 0) { | 192 | if (ret < 0) { |
@@ -338,6 +199,36 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) | |||
338 | if (reg & WM831X_WDOG_DEBUG) | 199 | if (reg & WM831X_WDOG_DEBUG) |
339 | dev_warn(wm831x->dev, "Watchdog is paused\n"); | 200 | dev_warn(wm831x->dev, "Watchdog is paused\n"); |
340 | 201 | ||
202 | driver_data = kzalloc(sizeof(*driver_data), GFP_KERNEL); | ||
203 | if (!driver_data) { | ||
204 | dev_err(wm831x->dev, "Unable to alloacate watchdog device\n"); | ||
205 | ret = -ENOMEM; | ||
206 | goto err; | ||
207 | } | ||
208 | |||
209 | mutex_init(&driver_data->lock); | ||
210 | driver_data->wm831x = wm831x; | ||
211 | |||
212 | wm831x_wdt = &driver_data->wdt; | ||
213 | |||
214 | wm831x_wdt->info = &wm831x_wdt_info; | ||
215 | wm831x_wdt->ops = &wm831x_wdt_ops; | ||
216 | watchdog_set_drvdata(wm831x_wdt, driver_data); | ||
217 | |||
218 | if (nowayout) | ||
219 | wm831x_wdt->status |= WDOG_NO_WAY_OUT; | ||
220 | |||
221 | reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG); | ||
222 | reg &= WM831X_WDOG_TO_MASK; | ||
223 | for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++) | ||
224 | if (wm831x_wdt_cfgs[i].val == reg) | ||
225 | break; | ||
226 | if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) | ||
227 | dev_warn(wm831x->dev, | ||
228 | "Unknown watchdog timeout: %x\n", reg); | ||
229 | else | ||
230 | wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time; | ||
231 | |||
341 | /* Apply any configuration */ | 232 | /* Apply any configuration */ |
342 | if (pdev->dev.parent->platform_data) { | 233 | if (pdev->dev.parent->platform_data) { |
343 | chip_pdata = pdev->dev.parent->platform_data; | 234 | chip_pdata = pdev->dev.parent->platform_data; |
@@ -361,7 +252,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) | |||
361 | dev_err(wm831x->dev, | 252 | dev_err(wm831x->dev, |
362 | "Failed to request update GPIO: %d\n", | 253 | "Failed to request update GPIO: %d\n", |
363 | ret); | 254 | ret); |
364 | goto err; | 255 | goto err_alloc; |
365 | } | 256 | } |
366 | 257 | ||
367 | ret = gpio_direction_output(pdata->update_gpio, 0); | 258 | ret = gpio_direction_output(pdata->update_gpio, 0); |
@@ -372,7 +263,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) | |||
372 | goto err_gpio; | 263 | goto err_gpio; |
373 | } | 264 | } |
374 | 265 | ||
375 | update_gpio = pdata->update_gpio; | 266 | driver_data->update_gpio = pdata->update_gpio; |
376 | 267 | ||
377 | /* Make sure the watchdog takes hardware updates */ | 268 | /* Make sure the watchdog takes hardware updates */ |
378 | reg |= WM831X_WDOG_RST_SRC; | 269 | reg |= WM831X_WDOG_RST_SRC; |
@@ -389,33 +280,34 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) | |||
389 | } | 280 | } |
390 | } | 281 | } |
391 | 282 | ||
392 | wm831x_wdt_miscdev.parent = &pdev->dev; | 283 | ret = watchdog_register_device(&driver_data->wdt); |
393 | |||
394 | ret = misc_register(&wm831x_wdt_miscdev); | ||
395 | if (ret != 0) { | 284 | if (ret != 0) { |
396 | dev_err(wm831x->dev, "Failed to register miscdev: %d\n", ret); | 285 | dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n", |
286 | ret); | ||
397 | goto err_gpio; | 287 | goto err_gpio; |
398 | } | 288 | } |
399 | 289 | ||
290 | dev_set_drvdata(&pdev->dev, driver_data); | ||
291 | |||
400 | return 0; | 292 | return 0; |
401 | 293 | ||
402 | err_gpio: | 294 | err_gpio: |
403 | if (update_gpio) { | 295 | if (driver_data->update_gpio) |
404 | gpio_free(update_gpio); | 296 | gpio_free(driver_data->update_gpio); |
405 | update_gpio = 0; | 297 | err_alloc: |
406 | } | 298 | kfree(driver_data); |
407 | err: | 299 | err: |
408 | return ret; | 300 | return ret; |
409 | } | 301 | } |
410 | 302 | ||
411 | static int __devexit wm831x_wdt_remove(struct platform_device *pdev) | 303 | static int __devexit wm831x_wdt_remove(struct platform_device *pdev) |
412 | { | 304 | { |
413 | if (update_gpio) { | 305 | struct wm831x_wdt_drvdata *driver_data = dev_get_drvdata(&pdev->dev); |
414 | gpio_free(update_gpio); | 306 | |
415 | update_gpio = 0; | 307 | watchdog_unregister_device(&driver_data->wdt); |
416 | } | ||
417 | 308 | ||
418 | misc_deregister(&wm831x_wdt_miscdev); | 309 | if (driver_data->update_gpio) |
310 | gpio_free(driver_data->update_gpio); | ||
419 | 311 | ||
420 | return 0; | 312 | return 0; |
421 | } | 313 | } |