Blame view

drivers/watchdog/wm831x_wdt.c 7.71 KB
502a0106b   Mark Brown   [WATCHDOG] Add su...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * Watchdog driver for the wm831x PMICs
   *
   * Copyright (C) 2009 Wolfson Microelectronics
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation
   */
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
00411ee93   Mark Brown   watchdog: Convert...
15
  #include <linux/slab.h>
502a0106b   Mark Brown   [WATCHDOG] Add su...
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  #include <linux/platform_device.h>
  #include <linux/watchdog.h>
  #include <linux/uaccess.h>
  #include <linux/gpio.h>
  
  #include <linux/mfd/wm831x/core.h>
  #include <linux/mfd/wm831x/pdata.h>
  #include <linux/mfd/wm831x/watchdog.h>
  
  static int nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, int, 0);
  MODULE_PARM_DESC(nowayout,
  		 "Watchdog cannot be stopped once started (default="
  		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
00411ee93   Mark Brown   watchdog: Convert...
30
31
32
33
34
35
36
  struct wm831x_wdt_drvdata {
  	struct watchdog_device wdt;
  	struct wm831x *wm831x;
  	struct mutex lock;
  	int update_gpio;
  	int update_state;
  };
502a0106b   Mark Brown   [WATCHDOG] Add su...
37
38
39
40
  
  /* We can't use the sub-second values here but they're included
   * for completeness.  */
  static struct {
00411ee93   Mark Brown   watchdog: Convert...
41
42
  	unsigned int time;  /* Seconds */
  	u16 val;            /* WDOG_TO value */
502a0106b   Mark Brown   [WATCHDOG] Add su...
43
44
45
46
47
48
49
50
51
  } wm831x_wdt_cfgs[] = {
  	{  1, 2 },
  	{  2, 3 },
  	{  4, 4 },
  	{  8, 5 },
  	{ 16, 6 },
  	{ 32, 7 },
  	{ 33, 7 },  /* Actually 32.768s so include both, others round down */
  };
00411ee93   Mark Brown   watchdog: Convert...
52
  static int wm831x_wdt_start(struct watchdog_device *wdt_dev)
502a0106b   Mark Brown   [WATCHDOG] Add su...
53
  {
00411ee93   Mark Brown   watchdog: Convert...
54
55
  	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct wm831x *wm831x = driver_data->wm831x;
502a0106b   Mark Brown   [WATCHDOG] Add su...
56
  	int ret;
00411ee93   Mark Brown   watchdog: Convert...
57
  	mutex_lock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
58
59
60
61
62
63
64
65
66
67
68
  
  	ret = wm831x_reg_unlock(wm831x);
  	if (ret == 0) {
  		ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
  				      WM831X_WDOG_ENA, WM831X_WDOG_ENA);
  		wm831x_reg_lock(wm831x);
  	} else {
  		dev_err(wm831x->dev, "Failed to unlock security key: %d
  ",
  			ret);
  	}
00411ee93   Mark Brown   watchdog: Convert...
69
  	mutex_unlock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
70
71
72
  
  	return ret;
  }
00411ee93   Mark Brown   watchdog: Convert...
73
  static int wm831x_wdt_stop(struct watchdog_device *wdt_dev)
502a0106b   Mark Brown   [WATCHDOG] Add su...
74
  {
00411ee93   Mark Brown   watchdog: Convert...
75
76
  	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct wm831x *wm831x = driver_data->wm831x;
502a0106b   Mark Brown   [WATCHDOG] Add su...
77
  	int ret;
00411ee93   Mark Brown   watchdog: Convert...
78
  	mutex_lock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
79
80
81
82
83
84
85
86
87
88
89
  
  	ret = wm831x_reg_unlock(wm831x);
  	if (ret == 0) {
  		ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
  				      WM831X_WDOG_ENA, 0);
  		wm831x_reg_lock(wm831x);
  	} else {
  		dev_err(wm831x->dev, "Failed to unlock security key: %d
  ",
  			ret);
  	}
00411ee93   Mark Brown   watchdog: Convert...
90
  	mutex_unlock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
91
92
93
  
  	return ret;
  }
00411ee93   Mark Brown   watchdog: Convert...
94
  static int wm831x_wdt_ping(struct watchdog_device *wdt_dev)
502a0106b   Mark Brown   [WATCHDOG] Add su...
95
  {
00411ee93   Mark Brown   watchdog: Convert...
96
97
  	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct wm831x *wm831x = driver_data->wm831x;
502a0106b   Mark Brown   [WATCHDOG] Add su...
98
99
  	int ret;
  	u16 reg;
00411ee93   Mark Brown   watchdog: Convert...
100
  	mutex_lock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
101

00411ee93   Mark Brown   watchdog: Convert...
102
103
104
105
  	if (driver_data->update_gpio) {
  		gpio_set_value_cansleep(driver_data->update_gpio,
  					driver_data->update_state);
  		driver_data->update_state = !driver_data->update_state;
502a0106b   Mark Brown   [WATCHDOG] Add su...
106
107
108
  		ret = 0;
  		goto out;
  	}
502a0106b   Mark Brown   [WATCHDOG] Add su...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  	reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
  
  	if (!(reg & WM831X_WDOG_RST_SRC)) {
  		dev_err(wm831x->dev, "Hardware watchdog update unsupported
  ");
  		ret = -EINVAL;
  		goto out;
  	}
  
  	reg |= WM831X_WDOG_RESET;
  
  	ret = wm831x_reg_unlock(wm831x);
  	if (ret == 0) {
  		ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
  		wm831x_reg_lock(wm831x);
  	} else {
  		dev_err(wm831x->dev, "Failed to unlock security key: %d
  ",
  			ret);
  	}
  
  out:
00411ee93   Mark Brown   watchdog: Convert...
131
  	mutex_unlock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
132
133
134
  
  	return ret;
  }
00411ee93   Mark Brown   watchdog: Convert...
135
136
  static int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev,
  				  unsigned int timeout)
502a0106b   Mark Brown   [WATCHDOG] Add su...
137
  {
00411ee93   Mark Brown   watchdog: Convert...
138
139
140
  	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct wm831x *wm831x = driver_data->wm831x;
  	int ret, i;
502a0106b   Mark Brown   [WATCHDOG] Add su...
141

00411ee93   Mark Brown   watchdog: Convert...
142
143
144
145
  	for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
  		if (wm831x_wdt_cfgs[i].time == timeout)
  			break;
  	if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
f98491008   Mark Brown   watchdog: Don't o...
146
  		return -EINVAL;
502a0106b   Mark Brown   [WATCHDOG] Add su...
147

00411ee93   Mark Brown   watchdog: Convert...
148
149
150
151
152
153
154
155
156
157
  	ret = wm831x_reg_unlock(wm831x);
  	if (ret == 0) {
  		ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
  				      WM831X_WDOG_TO_MASK,
  				      wm831x_wdt_cfgs[i].val);
  		wm831x_reg_lock(wm831x);
  	} else {
  		dev_err(wm831x->dev, "Failed to unlock security key: %d
  ",
  			ret);
502a0106b   Mark Brown   [WATCHDOG] Add su...
158
  	}
00411ee93   Mark Brown   watchdog: Convert...
159
  	return ret;
502a0106b   Mark Brown   [WATCHDOG] Add su...
160
  }
00411ee93   Mark Brown   watchdog: Convert...
161
  static const struct watchdog_info wm831x_wdt_info = {
502a0106b   Mark Brown   [WATCHDOG] Add su...
162
163
164
  	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  	.identity = "WM831x Watchdog",
  };
00411ee93   Mark Brown   watchdog: Convert...
165
  static const struct watchdog_ops wm831x_wdt_ops = {
502a0106b   Mark Brown   [WATCHDOG] Add su...
166
  	.owner = THIS_MODULE,
00411ee93   Mark Brown   watchdog: Convert...
167
168
169
170
  	.start = wm831x_wdt_start,
  	.stop = wm831x_wdt_stop,
  	.ping = wm831x_wdt_ping,
  	.set_timeout = wm831x_wdt_set_timeout,
502a0106b   Mark Brown   [WATCHDOG] Add su...
171
172
173
174
  };
  
  static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
  {
00411ee93   Mark Brown   watchdog: Convert...
175
  	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
502a0106b   Mark Brown   [WATCHDOG] Add su...
176
177
  	struct wm831x_pdata *chip_pdata;
  	struct wm831x_watchdog_pdata *pdata;
00411ee93   Mark Brown   watchdog: Convert...
178
179
180
  	struct wm831x_wdt_drvdata *driver_data;
  	struct watchdog_device *wm831x_wdt;
  	int reg, ret, i;
502a0106b   Mark Brown   [WATCHDOG] Add su...
181
182
183
184
185
186
187
188
189
190
191
192
193
  
  	ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
  	if (ret < 0) {
  		dev_err(wm831x->dev, "Failed to read watchdog status: %d
  ",
  			ret);
  		goto err;
  	}
  	reg = ret;
  
  	if (reg & WM831X_WDOG_DEBUG)
  		dev_warn(wm831x->dev, "Watchdog is paused
  ");
7b9bb6d8c   Mark Brown   watchdog: Convert...
194
195
  	driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
  				   GFP_KERNEL);
00411ee93   Mark Brown   watchdog: Convert...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  	if (!driver_data) {
  		dev_err(wm831x->dev, "Unable to alloacate watchdog device
  ");
  		ret = -ENOMEM;
  		goto err;
  	}
  
  	mutex_init(&driver_data->lock);
  	driver_data->wm831x = wm831x;
  
  	wm831x_wdt = &driver_data->wdt;
  
  	wm831x_wdt->info = &wm831x_wdt_info;
  	wm831x_wdt->ops = &wm831x_wdt_ops;
ff0b3cd4a   Wim Van Sebroeck   watchdog: add now...
210
  	watchdog_set_nowayout(wm831x_wdt, nowayout);
00411ee93   Mark Brown   watchdog: Convert...
211
  	watchdog_set_drvdata(wm831x_wdt, driver_data);
00411ee93   Mark Brown   watchdog: Convert...
212
213
214
215
216
217
218
219
220
221
222
  	reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
  	reg &= WM831X_WDOG_TO_MASK;
  	for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
  		if (wm831x_wdt_cfgs[i].val == reg)
  			break;
  	if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
  		dev_warn(wm831x->dev,
  			 "Unknown watchdog timeout: %x
  ", reg);
  	else
  		wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time;
502a0106b   Mark Brown   [WATCHDOG] Add su...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  	/* Apply any configuration */
  	if (pdev->dev.parent->platform_data) {
  		chip_pdata = pdev->dev.parent->platform_data;
  		pdata = chip_pdata->watchdog;
  	} else {
  		pdata = NULL;
  	}
  
  	if (pdata) {
  		reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
  			 WM831X_WDOG_RST_SRC);
  
  		reg |= pdata->primary << WM831X_WDOG_PRIMACT_SHIFT;
  		reg |= pdata->secondary << WM831X_WDOG_SECACT_SHIFT;
  		reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
  
  		if (pdata->update_gpio) {
  			ret = gpio_request(pdata->update_gpio,
  					   "Watchdog update");
  			if (ret < 0) {
  				dev_err(wm831x->dev,
  					"Failed to request update GPIO: %d
  ",
  					ret);
7b9bb6d8c   Mark Brown   watchdog: Convert...
247
  				goto err;
502a0106b   Mark Brown   [WATCHDOG] Add su...
248
249
250
251
252
253
254
255
256
257
  			}
  
  			ret = gpio_direction_output(pdata->update_gpio, 0);
  			if (ret != 0) {
  				dev_err(wm831x->dev,
  					"gpio_direction_output returned: %d
  ",
  					ret);
  				goto err_gpio;
  			}
00411ee93   Mark Brown   watchdog: Convert...
258
  			driver_data->update_gpio = pdata->update_gpio;
502a0106b   Mark Brown   [WATCHDOG] Add su...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  
  			/* Make sure the watchdog takes hardware updates */
  			reg |= WM831X_WDOG_RST_SRC;
  		}
  
  		ret = wm831x_reg_unlock(wm831x);
  		if (ret == 0) {
  			ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
  			wm831x_reg_lock(wm831x);
  		} else {
  			dev_err(wm831x->dev,
  				"Failed to unlock security key: %d
  ", ret);
  			goto err_gpio;
  		}
  	}
00411ee93   Mark Brown   watchdog: Convert...
275
  	ret = watchdog_register_device(&driver_data->wdt);
502a0106b   Mark Brown   [WATCHDOG] Add su...
276
  	if (ret != 0) {
00411ee93   Mark Brown   watchdog: Convert...
277
278
279
  		dev_err(wm831x->dev, "watchdog_register_device() failed: %d
  ",
  			ret);
502a0106b   Mark Brown   [WATCHDOG] Add su...
280
281
  		goto err_gpio;
  	}
00411ee93   Mark Brown   watchdog: Convert...
282
  	dev_set_drvdata(&pdev->dev, driver_data);
502a0106b   Mark Brown   [WATCHDOG] Add su...
283
284
285
  	return 0;
  
  err_gpio:
00411ee93   Mark Brown   watchdog: Convert...
286
287
  	if (driver_data->update_gpio)
  		gpio_free(driver_data->update_gpio);
502a0106b   Mark Brown   [WATCHDOG] Add su...
288
289
290
291
292
293
  err:
  	return ret;
  }
  
  static int __devexit wm831x_wdt_remove(struct platform_device *pdev)
  {
00411ee93   Mark Brown   watchdog: Convert...
294
295
296
  	struct wm831x_wdt_drvdata *driver_data = dev_get_drvdata(&pdev->dev);
  
  	watchdog_unregister_device(&driver_data->wdt);
502a0106b   Mark Brown   [WATCHDOG] Add su...
297

00411ee93   Mark Brown   watchdog: Convert...
298
299
  	if (driver_data->update_gpio)
  		gpio_free(driver_data->update_gpio);
502a0106b   Mark Brown   [WATCHDOG] Add su...
300
301
302
303
304
305
306
307
308
309
310
  
  	return 0;
  }
  
  static struct platform_driver wm831x_wdt_driver = {
  	.probe = wm831x_wdt_probe,
  	.remove = __devexit_p(wm831x_wdt_remove),
  	.driver = {
  		.name = "wm831x-watchdog",
  	},
  };
216f3ad9a   Mark Brown   watchdog: Convert...
311
  module_platform_driver(wm831x_wdt_driver);
502a0106b   Mark Brown   [WATCHDOG] Add su...
312
313
314
315
316
  
  MODULE_AUTHOR("Mark Brown");
  MODULE_DESCRIPTION("WM831x Watchdog");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:wm831x-watchdog");