diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-07-27 09:45:54 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2009-09-17 03:46:58 -0400 |
commit | 7e9f9fd4b8285c52c0950a1929864346de5caa6d (patch) | |
tree | d7e0f88f4f60e20a37b160a9555807c754849c74 /drivers | |
parent | 7d4d0a3e7343e3190afaa17253073db58e3d9bff (diff) |
mfd: Add WM831x AUXADC support
The WM831x contains an auxiliary ADC with a number of switchable
inputs which is used to monitor some of the voltages and
temperatures in the system and has some external inputs which can be
used for machine specific purposes. Provide an API allowing drivers
to read values from the ADC.
An internal reference voltage is provided to allow callibration of
the ADC. This is used to calibrate the device at startup.
The hardware also supports continuous readings and digital comparators.
These are not yet supported by the driver.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/wm831x-core.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index eb63d22160d1..42bef1dd2ca1 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c | |||
@@ -15,11 +15,14 @@ | |||
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/i2c.h> | 17 | #include <linux/i2c.h> |
18 | #include <linux/bcd.h> | ||
19 | #include <linux/delay.h> | ||
18 | #include <linux/mfd/core.h> | 20 | #include <linux/mfd/core.h> |
19 | 21 | ||
20 | #include <linux/mfd/wm831x/core.h> | 22 | #include <linux/mfd/wm831x/core.h> |
21 | #include <linux/mfd/wm831x/pdata.h> | 23 | #include <linux/mfd/wm831x/pdata.h> |
22 | #include <linux/mfd/wm831x/irq.h> | 24 | #include <linux/mfd/wm831x/irq.h> |
25 | #include <linux/mfd/wm831x/auxadc.h> | ||
23 | 26 | ||
24 | enum wm831x_parent { | 27 | enum wm831x_parent { |
25 | WM8310 = 0, | 28 | WM8310 = 0, |
@@ -244,6 +247,103 @@ out: | |||
244 | } | 247 | } |
245 | EXPORT_SYMBOL_GPL(wm831x_set_bits); | 248 | EXPORT_SYMBOL_GPL(wm831x_set_bits); |
246 | 249 | ||
250 | /** | ||
251 | * wm831x_auxadc_read: Read a value from the WM831x AUXADC | ||
252 | * | ||
253 | * @wm831x: Device to read from. | ||
254 | * @input: AUXADC input to read. | ||
255 | */ | ||
256 | int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) | ||
257 | { | ||
258 | int tries = 10; | ||
259 | int ret, src; | ||
260 | |||
261 | mutex_lock(&wm831x->auxadc_lock); | ||
262 | |||
263 | ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, | ||
264 | WM831X_AUX_ENA, WM831X_AUX_ENA); | ||
265 | if (ret < 0) { | ||
266 | dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret); | ||
267 | goto out; | ||
268 | } | ||
269 | |||
270 | /* We force a single source at present */ | ||
271 | src = input; | ||
272 | ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE, | ||
273 | 1 << src); | ||
274 | if (ret < 0) { | ||
275 | dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret); | ||
276 | goto out; | ||
277 | } | ||
278 | |||
279 | ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, | ||
280 | WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA); | ||
281 | if (ret < 0) { | ||
282 | dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret); | ||
283 | goto disable; | ||
284 | } | ||
285 | |||
286 | do { | ||
287 | msleep(1); | ||
288 | |||
289 | ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL); | ||
290 | if (ret < 0) | ||
291 | ret = WM831X_AUX_CVT_ENA; | ||
292 | } while ((ret & WM831X_AUX_CVT_ENA) && --tries); | ||
293 | |||
294 | if (ret & WM831X_AUX_CVT_ENA) { | ||
295 | dev_err(wm831x->dev, "Timed out reading AUXADC\n"); | ||
296 | ret = -EBUSY; | ||
297 | goto disable; | ||
298 | } | ||
299 | |||
300 | ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); | ||
301 | if (ret < 0) { | ||
302 | dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret); | ||
303 | } else { | ||
304 | src = ((ret & WM831X_AUX_DATA_SRC_MASK) | ||
305 | >> WM831X_AUX_DATA_SRC_SHIFT) - 1; | ||
306 | |||
307 | if (src == 14) | ||
308 | src = WM831X_AUX_CAL; | ||
309 | |||
310 | if (src != input) { | ||
311 | dev_err(wm831x->dev, "Data from source %d not %d\n", | ||
312 | src, input); | ||
313 | ret = -EINVAL; | ||
314 | } else { | ||
315 | ret &= WM831X_AUX_DATA_MASK; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | disable: | ||
320 | wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0); | ||
321 | out: | ||
322 | mutex_unlock(&wm831x->auxadc_lock); | ||
323 | return ret; | ||
324 | } | ||
325 | EXPORT_SYMBOL_GPL(wm831x_auxadc_read); | ||
326 | |||
327 | /** | ||
328 | * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC | ||
329 | * | ||
330 | * @wm831x: Device to read from. | ||
331 | * @input: AUXADC input to read. | ||
332 | */ | ||
333 | int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input) | ||
334 | { | ||
335 | int ret; | ||
336 | |||
337 | ret = wm831x_auxadc_read(wm831x, input); | ||
338 | if (ret < 0) | ||
339 | return ret; | ||
340 | |||
341 | ret *= 1465; | ||
342 | |||
343 | return ret; | ||
344 | } | ||
345 | EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv); | ||
346 | |||
247 | static struct resource wm831x_dcdc1_resources[] = { | 347 | static struct resource wm831x_dcdc1_resources[] = { |
248 | { | 348 | { |
249 | .start = WM831X_DC1_CONTROL_1, | 349 | .start = WM831X_DC1_CONTROL_1, |
@@ -1084,6 +1184,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) | |||
1084 | 1184 | ||
1085 | mutex_init(&wm831x->io_lock); | 1185 | mutex_init(&wm831x->io_lock); |
1086 | mutex_init(&wm831x->key_lock); | 1186 | mutex_init(&wm831x->key_lock); |
1187 | mutex_init(&wm831x->auxadc_lock); | ||
1087 | dev_set_drvdata(wm831x->dev, wm831x); | 1188 | dev_set_drvdata(wm831x->dev, wm831x); |
1088 | 1189 | ||
1089 | ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); | 1190 | ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); |