Blame view

drivers/watchdog/wm831x_wdt.c 7.08 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
  #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>
86a1e1896   Wim Van Sebroeck   watchdog: nowayou...
24
25
  static bool nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, bool, 0);
502a0106b   Mark Brown   [WATCHDOG] Add su...
26
27
28
  MODULE_PARM_DESC(nowayout,
  		 "Watchdog cannot be stopped once started (default="
  		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
00411ee93   Mark Brown   watchdog: Convert...
29
30
31
32
33
34
35
  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...
36
37
38
39
  
  /* We can't use the sub-second values here but they're included
   * for completeness.  */
  static struct {
00411ee93   Mark Brown   watchdog: Convert...
40
41
  	unsigned int time;  /* Seconds */
  	u16 val;            /* WDOG_TO value */
502a0106b   Mark Brown   [WATCHDOG] Add su...
42
43
44
45
46
47
48
49
50
  } 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...
51
  static int wm831x_wdt_start(struct watchdog_device *wdt_dev)
502a0106b   Mark Brown   [WATCHDOG] Add su...
52
  {
00411ee93   Mark Brown   watchdog: Convert...
53
54
  	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct wm831x *wm831x = driver_data->wm831x;
502a0106b   Mark Brown   [WATCHDOG] Add su...
55
  	int ret;
00411ee93   Mark Brown   watchdog: Convert...
56
  	mutex_lock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
57
58
59
60
61
62
63
64
65
66
67
  
  	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...
68
  	mutex_unlock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
69
70
71
  
  	return ret;
  }
00411ee93   Mark Brown   watchdog: Convert...
72
  static int wm831x_wdt_stop(struct watchdog_device *wdt_dev)
502a0106b   Mark Brown   [WATCHDOG] Add su...
73
  {
00411ee93   Mark Brown   watchdog: Convert...
74
75
  	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct wm831x *wm831x = driver_data->wm831x;
502a0106b   Mark Brown   [WATCHDOG] Add su...
76
  	int ret;
00411ee93   Mark Brown   watchdog: Convert...
77
  	mutex_lock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
78
79
80
81
82
83
84
85
86
87
88
  
  	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...
89
  	mutex_unlock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
90
91
92
  
  	return ret;
  }
00411ee93   Mark Brown   watchdog: Convert...
93
  static int wm831x_wdt_ping(struct watchdog_device *wdt_dev)
502a0106b   Mark Brown   [WATCHDOG] Add su...
94
  {
00411ee93   Mark Brown   watchdog: Convert...
95
96
  	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct wm831x *wm831x = driver_data->wm831x;
502a0106b   Mark Brown   [WATCHDOG] Add su...
97
98
  	int ret;
  	u16 reg;
00411ee93   Mark Brown   watchdog: Convert...
99
  	mutex_lock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
100

00411ee93   Mark Brown   watchdog: Convert...
101
102
103
104
  	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...
105
106
107
  		ret = 0;
  		goto out;
  	}
502a0106b   Mark Brown   [WATCHDOG] Add su...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  	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...
130
  	mutex_unlock(&driver_data->lock);
502a0106b   Mark Brown   [WATCHDOG] Add su...
131
132
133
  
  	return ret;
  }
00411ee93   Mark Brown   watchdog: Convert...
134
135
  static int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev,
  				  unsigned int timeout)
502a0106b   Mark Brown   [WATCHDOG] Add su...
136
  {
00411ee93   Mark Brown   watchdog: Convert...
137
138
139
  	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...
140

00411ee93   Mark Brown   watchdog: Convert...
141
142
143
144
  	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...
145
  		return -EINVAL;
502a0106b   Mark Brown   [WATCHDOG] Add su...
146

00411ee93   Mark Brown   watchdog: Convert...
147
148
149
150
151
152
153
154
155
156
  	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...
157
  	}
0197c1c49   Wim Van Sebroeck   watchdog: fix set...
158
  	wdt_dev->timeout = timeout;
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
  };
2d991a164   Bill Pemberton   watchdog: remove ...
172
  static int wm831x_wdt_probe(struct platform_device *pdev)
502a0106b   Mark Brown   [WATCHDOG] Add su...
173
  {
00411ee93   Mark Brown   watchdog: Convert...
174
  	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
bc8fdfbe7   Jingoo Han   watchdog: use dev...
175
  	struct wm831x_pdata *chip_pdata = dev_get_platdata(pdev->dev.parent);
502a0106b   Mark Brown   [WATCHDOG] Add su...
176
  	struct wm831x_watchdog_pdata *pdata;
00411ee93   Mark Brown   watchdog: Convert...
177
178
179
  	struct wm831x_wdt_drvdata *driver_data;
  	struct watchdog_device *wm831x_wdt;
  	int reg, ret, i;
502a0106b   Mark Brown   [WATCHDOG] Add su...
180
181
182
183
184
185
  
  	ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
  	if (ret < 0) {
  		dev_err(wm831x->dev, "Failed to read watchdog status: %d
  ",
  			ret);
30cba9a10   Guenter Roeck   watchdog: wm831x_...
186
  		return ret;
502a0106b   Mark Brown   [WATCHDOG] Add su...
187
188
189
190
191
192
  	}
  	reg = ret;
  
  	if (reg & WM831X_WDOG_DEBUG)
  		dev_warn(wm831x->dev, "Watchdog is paused
  ");
7b9bb6d8c   Mark Brown   watchdog: Convert...
193
194
  	driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
  				   GFP_KERNEL);
30cba9a10   Guenter Roeck   watchdog: wm831x_...
195
196
  	if (!driver_data)
  		return -ENOMEM;
00411ee93   Mark Brown   watchdog: Convert...
197
198
199
200
201
202
203
204
  
  	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;
6551881c8   Pratyush Anand   Watchdog: Fix par...
205
  	wm831x_wdt->parent = &pdev->dev;
ff0b3cd4a   Wim Van Sebroeck   watchdog: add now...
206
  	watchdog_set_nowayout(wm831x_wdt, nowayout);
00411ee93   Mark Brown   watchdog: Convert...
207
  	watchdog_set_drvdata(wm831x_wdt, driver_data);
00411ee93   Mark Brown   watchdog: Convert...
208
209
210
211
212
213
214
215
216
217
218
  	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...
219
  	/* Apply any configuration */
bc8fdfbe7   Jingoo Han   watchdog: use dev...
220
  	if (chip_pdata)
502a0106b   Mark Brown   [WATCHDOG] Add su...
221
  		pdata = chip_pdata->watchdog;
bc8fdfbe7   Jingoo Han   watchdog: use dev...
222
  	else
502a0106b   Mark Brown   [WATCHDOG] Add su...
223
  		pdata = NULL;
502a0106b   Mark Brown   [WATCHDOG] Add su...
224
225
226
227
228
229
230
231
232
233
  
  	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) {
7a5da030c   Jingoo Han   watchdog: wm831x_...
234
235
236
237
  			ret = devm_gpio_request_one(&pdev->dev,
  						pdata->update_gpio,
  						GPIOF_OUT_INIT_LOW,
  						"Watchdog update");
502a0106b   Mark Brown   [WATCHDOG] Add su...
238
239
240
241
242
  			if (ret < 0) {
  				dev_err(wm831x->dev,
  					"Failed to request update GPIO: %d
  ",
  					ret);
30cba9a10   Guenter Roeck   watchdog: wm831x_...
243
  				return ret;
502a0106b   Mark Brown   [WATCHDOG] Add su...
244
  			}
00411ee93   Mark Brown   watchdog: Convert...
245
  			driver_data->update_gpio = pdata->update_gpio;
502a0106b   Mark Brown   [WATCHDOG] Add su...
246
247
248
249
250
251
252
253
254
255
256
257
258
  
  			/* 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);
30cba9a10   Guenter Roeck   watchdog: wm831x_...
259
  			return ret;
502a0106b   Mark Brown   [WATCHDOG] Add su...
260
261
  		}
  	}
30cba9a10   Guenter Roeck   watchdog: wm831x_...
262
  	ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt);
502a0106b   Mark Brown   [WATCHDOG] Add su...
263
  	if (ret != 0) {
00411ee93   Mark Brown   watchdog: Convert...
264
265
266
  		dev_err(wm831x->dev, "watchdog_register_device() failed: %d
  ",
  			ret);
30cba9a10   Guenter Roeck   watchdog: wm831x_...
267
  		return ret;
502a0106b   Mark Brown   [WATCHDOG] Add su...
268
  	}
502a0106b   Mark Brown   [WATCHDOG] Add su...
269
270
271
272
273
  	return 0;
  }
  
  static struct platform_driver wm831x_wdt_driver = {
  	.probe = wm831x_wdt_probe,
502a0106b   Mark Brown   [WATCHDOG] Add su...
274
275
276
277
  	.driver = {
  		.name = "wm831x-watchdog",
  	},
  };
216f3ad9a   Mark Brown   watchdog: Convert...
278
  module_platform_driver(wm831x_wdt_driver);
502a0106b   Mark Brown   [WATCHDOG] Add su...
279
280
281
282
283
  
  MODULE_AUTHOR("Mark Brown");
  MODULE_DESCRIPTION("WM831x Watchdog");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:wm831x-watchdog");