aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-07-27 09:45:54 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2009-09-17 03:46:58 -0400
commit7e9f9fd4b8285c52c0950a1929864346de5caa6d (patch)
treed7e0f88f4f60e20a37b160a9555807c754849c74 /drivers/mfd
parent7d4d0a3e7343e3190afaa17253073db58e3d9bff (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/mfd')
-rw-r--r--drivers/mfd/wm831x-core.c101
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
24enum wm831x_parent { 27enum wm831x_parent {
25 WM8310 = 0, 28 WM8310 = 0,
@@ -244,6 +247,103 @@ out:
244} 247}
245EXPORT_SYMBOL_GPL(wm831x_set_bits); 248EXPORT_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 */
256int 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
319disable:
320 wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
321out:
322 mutex_unlock(&wm831x->auxadc_lock);
323 return ret;
324}
325EXPORT_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 */
333int 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}
345EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
346
247static struct resource wm831x_dcdc1_resources[] = { 347static 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);