Re: [PATCH v5 2/2] iio: twl6030-gpadc: TWL6030, TWL6032 GPADC driver

2013-07-19 Thread Oleksandr Kozaruk
Hello Lars,

On Thu, Jul 18, 2013 at 12:48 PM, Lars-Peter Clausen l...@metafoo.de wrote:
 On 07/18/2013 10:36 AM, Oleksandr Kozaruk wrote:
 Hello Lars,

 On Wed, Jul 17, 2013 at 9:04 PM, Lars-Peter Clausen l...@metafoo.de wrote:
 +static int twl6032_calibration(struct twl6030_gpadc_data *gpadc)
 +{
 + int chn, d1 = 0, d2 = 0, temp;
 + u8 trim_regs[17];
 + int ret;
 +
 + ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs + 1,
 + TWL6030_GPADC_TRIM1, 16);
 + if (ret  0) {
 + dev_err(gpadc-dev, calibration failed\n);
 + return ret;
 + }
 +
 + /*
 +  * Loop to calculate the value needed for returning voltages from
 +  * GPADC not values.
 +  *
 +  * gain is calculated to 3 decimal places fixed point.
 +  */
 + for (chn = 0; chn  TWL6032_GPADC_MAX_CHANNELS; chn++) {
 +
 + switch (chn) {
 + case 0:
 + case 1:
 + case 2:
 + case 3:
 + case 4:
 + case 5:
 + case 6:
 + case 11:
 + case 12:
 + case 13:
 + case 14:
 + /* D1 */
 + d1 = (trim_regs[3]  0x1F)  2;
 + d1 |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + d1 = -d1;
 +
 + /* D2 */
 + d2 = (trim_regs[4]  0x3F)  2;
 + d2 |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + d2 = -d2;
 + break;
 + case 8:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[8]  0x18)  1;
 + d1 |= (trim_regs[7]  0x1E)  1;
 + if (trim_regs[7]  0x01)
 + d1 = -d1;
 +
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[10]  0x1F)  2;
 + d2 |= (trim_regs[8]  0x06)  1;
 + if (trim_regs[8]  0x01)
 + d2 = -d2;
 +
 + d2 += temp;
 + break;
 + case 9:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[14]  0x18)  1;
 + d1 |= (trim_regs[12]  0x1E)  1;
 + if (trim_regs[12]  0x01)
 + d1 = -d1;
 +
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[16]  0x1F)  2;
 + d2 |= (trim_regs[14]  0x06)  1;
 + if (trim_regs[14]  0x01)
 + d2 = -d2;
 +
 + d2 += temp;
 + case 10:
 + /* D1 */
 + d1 = (trim_regs[11]  0x0F)  3;
 + d1 |= (trim_regs[9]  0x0E)  1;
 + if (trim_regs[9]  0x01)
 + d1 = -d1;
 +
 + /* D2 */
 + d2 = (trim_regs[15]  0x0F)  3;
 + d2 |= (trim_regs[13]  0x0E)  1;
 + if (trim_regs[13]  0x01)
 + d2 = -d2;
 + break;
 + case 7:
 + case 18:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[5]  0x7E)  1;
 + if (trim_regs[5]  0x01)
 + d1 = -d1;
 +.
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[6]  0xFE)  1;
 + if (trim_regs[6]  0x01)
 + d2 = -d2;
 +
 +

Re: [PATCH v5 2/2] iio: twl6030-gpadc: TWL6030, TWL6032 GPADC driver

2013-07-18 Thread Oleksandr Kozaruk
Hello Lars,

On Wed, Jul 17, 2013 at 9:04 PM, Lars-Peter Clausen l...@metafoo.de wrote:
 +static int twl6032_calibration(struct twl6030_gpadc_data *gpadc)
 +{
 + int chn, d1 = 0, d2 = 0, temp;
 + u8 trim_regs[17];
 + int ret;
 +
 + ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs + 1,
 + TWL6030_GPADC_TRIM1, 16);
 + if (ret  0) {
 + dev_err(gpadc-dev, calibration failed\n);
 + return ret;
 + }
 +
 + /*
 +  * Loop to calculate the value needed for returning voltages from
 +  * GPADC not values.
 +  *
 +  * gain is calculated to 3 decimal places fixed point.
 +  */
 + for (chn = 0; chn  TWL6032_GPADC_MAX_CHANNELS; chn++) {
 +
 + switch (chn) {
 + case 0:
 + case 1:
 + case 2:
 + case 3:
 + case 4:
 + case 5:
 + case 6:
 + case 11:
 + case 12:
 + case 13:
 + case 14:
 + /* D1 */
 + d1 = (trim_regs[3]  0x1F)  2;
 + d1 |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + d1 = -d1;
 +
 + /* D2 */
 + d2 = (trim_regs[4]  0x3F)  2;
 + d2 |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + d2 = -d2;
 + break;
 + case 8:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[8]  0x18)  1;
 + d1 |= (trim_regs[7]  0x1E)  1;
 + if (trim_regs[7]  0x01)
 + d1 = -d1;
 +
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[10]  0x1F)  2;
 + d2 |= (trim_regs[8]  0x06)  1;
 + if (trim_regs[8]  0x01)
 + d2 = -d2;
 +
 + d2 += temp;
 + break;
 + case 9:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[14]  0x18)  1;
 + d1 |= (trim_regs[12]  0x1E)  1;
 + if (trim_regs[12]  0x01)
 + d1 = -d1;
 +
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[16]  0x1F)  2;
 + d2 |= (trim_regs[14]  0x06)  1;
 + if (trim_regs[14]  0x01)
 + d2 = -d2;
 +
 + d2 += temp;
 + case 10:
 + /* D1 */
 + d1 = (trim_regs[11]  0x0F)  3;
 + d1 |= (trim_regs[9]  0x0E)  1;
 + if (trim_regs[9]  0x01)
 + d1 = -d1;
 +
 + /* D2 */
 + d2 = (trim_regs[15]  0x0F)  3;
 + d2 |= (trim_regs[13]  0x0E)  1;
 + if (trim_regs[13]  0x01)
 + d2 = -d2;
 + break;
 + case 7:
 + case 18:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[5]  0x7E)  1;
 + if (trim_regs[5]  0x01)
 + d1 = -d1;
 +.
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[6]  0xFE)  1;
 + if (trim_regs[6]  0x01)
 + d2 = -d2;
 +
 + d2 += temp;
 + break;


 There is quite a bit of copy paste in here. Putting the bit swizziling into
 a helper 

Re: [PATCH v5 2/2] iio: twl6030-gpadc: TWL6030, TWL6032 GPADC driver

2013-07-18 Thread Lars-Peter Clausen
On 07/18/2013 10:36 AM, Oleksandr Kozaruk wrote:
 Hello Lars,
 
 On Wed, Jul 17, 2013 at 9:04 PM, Lars-Peter Clausen l...@metafoo.de wrote:
 +static int twl6032_calibration(struct twl6030_gpadc_data *gpadc)
 +{
 + int chn, d1 = 0, d2 = 0, temp;
 + u8 trim_regs[17];
 + int ret;
 +
 + ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs + 1,
 + TWL6030_GPADC_TRIM1, 16);
 + if (ret  0) {
 + dev_err(gpadc-dev, calibration failed\n);
 + return ret;
 + }
 +
 + /*
 +  * Loop to calculate the value needed for returning voltages from
 +  * GPADC not values.
 +  *
 +  * gain is calculated to 3 decimal places fixed point.
 +  */
 + for (chn = 0; chn  TWL6032_GPADC_MAX_CHANNELS; chn++) {
 +
 + switch (chn) {
 + case 0:
 + case 1:
 + case 2:
 + case 3:
 + case 4:
 + case 5:
 + case 6:
 + case 11:
 + case 12:
 + case 13:
 + case 14:
 + /* D1 */
 + d1 = (trim_regs[3]  0x1F)  2;
 + d1 |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + d1 = -d1;
 +
 + /* D2 */
 + d2 = (trim_regs[4]  0x3F)  2;
 + d2 |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + d2 = -d2;
 + break;
 + case 8:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[8]  0x18)  1;
 + d1 |= (trim_regs[7]  0x1E)  1;
 + if (trim_regs[7]  0x01)
 + d1 = -d1;
 +
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[10]  0x1F)  2;
 + d2 |= (trim_regs[8]  0x06)  1;
 + if (trim_regs[8]  0x01)
 + d2 = -d2;
 +
 + d2 += temp;
 + break;
 + case 9:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[14]  0x18)  1;
 + d1 |= (trim_regs[12]  0x1E)  1;
 + if (trim_regs[12]  0x01)
 + d1 = -d1;
 +
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[16]  0x1F)  2;
 + d2 |= (trim_regs[14]  0x06)  1;
 + if (trim_regs[14]  0x01)
 + d2 = -d2;
 +
 + d2 += temp;
 + case 10:
 + /* D1 */
 + d1 = (trim_regs[11]  0x0F)  3;
 + d1 |= (trim_regs[9]  0x0E)  1;
 + if (trim_regs[9]  0x01)
 + d1 = -d1;
 +
 + /* D2 */
 + d2 = (trim_regs[15]  0x0F)  3;
 + d2 |= (trim_regs[13]  0x0E)  1;
 + if (trim_regs[13]  0x01)
 + d2 = -d2;
 + break;
 + case 7:
 + case 18:
 + /* D1 */
 + temp = (trim_regs[3]  0x1F)  2;
 + temp |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + temp = -temp;
 +
 + d1 = (trim_regs[5]  0x7E)  1;
 + if (trim_regs[5]  0x01)
 + d1 = -d1;
 +.
 + d1 += temp;
 +
 + /* D2 */
 + temp = (trim_regs[4]  0x3F)  2;
 + temp |= (trim_regs[2]  0x06)  1;
 + if (trim_regs[2]  0x01)
 + temp = -temp;
 +
 + d2 = (trim_regs[6]  0xFE)  1;
 + if (trim_regs[6]  0x01)
 + d2 = -d2;
 +
 + d2 += temp;
 + break;


 There is quite a bit of copy paste in 

[PATCH v5 2/2] iio: twl6030-gpadc: TWL6030, TWL6032 GPADC driver

2013-07-17 Thread Oleksandr Kozaruk
The GPADC is general purpose ADC found on TWL6030, and TWL6032 PMIC,
known also as Phoenix and PhoenixLite.

The TWL6030 and TWL6032 have GPADC with 17 and 19 channels
respectively. Some channels have current source and are used for
measuring voltage drop on resistive load for detecting battery ID
resistance, or measuring voltage drop on NTC resistors for external
temperature measurements. Some channels measure voltage, (i.e. battery
voltage), and have voltage dividers, thus, capable to scale voltage.
Some channels are dedicated for measuring die temperature.

Some channels are calibrated in 2 points, having offsets from ideal
values kept in trim registers. This is used to correct measurements.

The differences between GPADC in TWL6030 and TWL6032:
- 10 bit vs 12 bit ADC;
- 17 vs 19 channels;
- channels have different purpose(i.e. battery voltage
  channel 8 vs channel 18);
- trim values are interpreted differently.

Based on the driver patched from Balaji TK, Graeme Gregory, Ambresh K,
Girish S Ghongdemath.

Signed-off-by: Balaji T K balaj...@ti.com
Signed-off-by: Graeme Gregory g...@slimlogic.co.uk
Signed-off-by: Oleksandr Kozaruk oleksandr.koza...@ti.com
---
 drivers/iio/adc/Kconfig |   14 +
 drivers/iio/adc/Makefile|1 +
 drivers/iio/adc/twl6030-gpadc.c | 1026 +++
 3 files changed, 1041 insertions(+)
 create mode 100644 drivers/iio/adc/twl6030-gpadc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index ab0767e6..3172461 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -150,6 +150,20 @@ config TI_AM335X_ADC
  Say yes here to build support for Texas Instruments ADC
  driver which is also a MFD client.
 
+config TWL6030_GPADC
+   tristate TWL6030 GPADC (General Purpose A/D Converter) Support
+   depends on TWL4030_CORE
+   default n
+   help
+ Say yes here if you want support for the TWL6030/TWL6032 General
+ Purpose A/D Converter. This will add support for battery type
+ detection, battery voltage and temperature measurement, die
+ temperature measurement, system supply voltage, audio accessory,
+ USB ID detection.
+
+ This driver can also be built as a module. If so, the module will be
+ called twl6030-gpadc.
+
 config VIPERBOARD_ADC
tristate Viperboard ADC support
depends on MFD_VIPERBOARD  USB
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 0a825be..996ba09 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -16,4 +16,5 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
 obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
new file mode 100644
index 000..97fad5b
--- /dev/null
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -0,0 +1,1026 @@
+/*
+ * TWL6030 GPADC module driver
+ *
+ * Copyright (C) 2009-2013 Texas Instruments Inc.
+ * Nishant Kamat nska...@ti.com
+ * Balaji T K balaj...@ti.com
+ * Graeme Gregory g...@slimlogic.co.uk
+ * Girish S Ghongdemath giris...@ti.com
+ * Ambresh K ambr...@ti.com
+ * Oleksandr Kozaruk oleksandr.koza...@ti.com
+ *
+ * Based on twl4030-madc.c
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen mikko.k.yli...@nokia.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include linux/init.h
+#include linux/interrupt.h
+#include linux/kernel.h
+#include linux/module.h
+#include linux/platform_device.h
+#include linux/of_platform.h
+#include linux/i2c/twl.h
+#include linux/iio/iio.h
+#include linux/iio/sysfs.h
+
+#define DRIVER_NAMEtwl6030_gpadc
+
+#define TWL6030_GPADC_MAX_CHANNELS 17
+#define TWL6032_GPADC_MAX_CHANNELS 19
+
+#define TWL6030_GPADC_CTRL_P1  0x05
+
+#define TWL6032_GPADC_GPSELECT_ISB 0x07
+#define TWL6032_GPADC_CTRL_P1  0x08
+
+#define TWL6032_GPADC_GPCH0_LSB0x0d
+#define TWL6032_GPADC_GPCH0_MSB0x0e
+
+#define TWL6030_GPADC_CTRL_P1_SP1  BIT(3)
+
+#define TWL6030_GPADC_GPCH0_LSB(0x29)
+
+#define TWL6030_GPADC_RT_SW1_EOC_MASK

Re: [PATCH v5 2/2] iio: twl6030-gpadc: TWL6030, TWL6032 GPADC driver

2013-07-17 Thread Lars-Peter Clausen
On 07/17/2013 01:12 PM, Oleksandr Kozaruk wrote:
 The GPADC is general purpose ADC found on TWL6030, and TWL6032 PMIC,
 known also as Phoenix and PhoenixLite.
 
 The TWL6030 and TWL6032 have GPADC with 17 and 19 channels
 respectively. Some channels have current source and are used for
 measuring voltage drop on resistive load for detecting battery ID
 resistance, or measuring voltage drop on NTC resistors for external
 temperature measurements. Some channels measure voltage, (i.e. battery
 voltage), and have voltage dividers, thus, capable to scale voltage.
 Some channels are dedicated for measuring die temperature.
 
 Some channels are calibrated in 2 points, having offsets from ideal
 values kept in trim registers. This is used to correct measurements.
 
 The differences between GPADC in TWL6030 and TWL6032:
 - 10 bit vs 12 bit ADC;
 - 17 vs 19 channels;
 - channels have different purpose(i.e. battery voltage
   channel 8 vs channel 18);
 - trim values are interpreted differently.
 
 Based on the driver patched from Balaji TK, Graeme Gregory, Ambresh K,
 Girish S Ghongdemath.
 
 Signed-off-by: Balaji T K balaj...@ti.com
 Signed-off-by: Graeme Gregory g...@slimlogic.co.uk
 Signed-off-by: Oleksandr Kozaruk oleksandr.koza...@ti.com

Almost there (I hope). But please run scripts/checkpatch, make C=2 and make
coccicheck on your driver and fix all errors those tools give you before
submitting the driver upstream.

[...]
 +static int twl6030_gpadc_get_raw(struct twl6030_gpadc_data *gpadc,
 + int channel, int *res)
 +{
 + u8 reg = gpadc-pdata-channel_to_reg(channel);
 + __le16 val;
 + int raw_code;
 + int ret;
 +
 + ret = twl6030_gpadc_read(reg, (u8 *)val);
 + if (ret) {
 + dev_dbg(gpadc-dev, unable to read register 0x%X\n, reg);
 + return ret;
 + }
 +
 + raw_code = le16_to_cpup(val);

raw_code = le16_to_cpu(val)

 + dev_dbg(gpadc-dev, GPADC raw code: %d, raw_code);
 +
 + if (twl6030_channel_calibrated(gpadc-pdata, channel))
 + *res = twl6030_gpadc_make_correction(gpadc, channel, raw_code);
 + else
 + *res = raw_code;
 +
 + return ret;
 +}
 +
[...]
 +static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev,
 +  const struct iio_chan_spec *chan,
 +  int *val, int *val2, long mask)
 +{
 + struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev);
 + int ret = -EINVAL;
 + long timeout;
 +
 + mutex_lock(gpadc-lock);
 +
 + ret = gpadc-pdata-start_conversion(chan-channel);
 + if (ret) {
 + dev_err(gpadc-dev, failed to start conversion\n);
 + goto err;
 + }
 + /* wait for conversion to complete */
 + timeout = wait_for_completion_interruptible_timeout(
 + gpadc-irq_complete, msecs_to_jiffies(5000));
 + if (!timeout)
 + return -ETIMEDOUT;
 + else if (timeout  0)
 + return EINTR;

You still hold the mutex, try this instead:

ret = wait_for_
if (ret == 0)
ret = -ETIMEDOUT;
if (ret  0)
goto err;


 +
 + switch (mask) {
 + case IIO_CHAN_INFO_RAW:
 + ret = twl6030_gpadc_get_raw(gpadc, chan-channel, val);
 + ret = ret ? -EIO : IIO_VAL_INT;
 + break;
 +
 + case IIO_CHAN_INFO_PROCESSED:
 + ret = twl6030_gpadc_get_processed(gpadc, chan-channel, val);
 + ret = ret ? -EIO : IIO_VAL_INT;
 + break;
 +
 + default:
 + break;
 + }
 +err:
 + mutex_unlock(gpadc-lock);
 +
 + return ret;
 +}
[...]
 +
 +static int twl6032_calibration(struct twl6030_gpadc_data *gpadc)
 +{
 + int chn, d1 = 0, d2 = 0, temp;
 + u8 trim_regs[17];
 + int ret;
 +
 + ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs + 1,
 + TWL6030_GPADC_TRIM1, 16);
 + if (ret  0) {
 + dev_err(gpadc-dev, calibration failed\n);
 + return ret;
 + }
 +
 + /*
 +  * Loop to calculate the value needed for returning voltages from
 +  * GPADC not values.
 +  *
 +  * gain is calculated to 3 decimal places fixed point.
 +  */
 + for (chn = 0; chn  TWL6032_GPADC_MAX_CHANNELS; chn++) {
 +
 + switch (chn) {
 + case 0:
 + case 1:
 + case 2:
 + case 3:
 + case 4:
 + case 5:
 + case 6:
 + case 11:
 + case 12:
 + case 13:
 + case 14:
 + /* D1 */
 + d1 = (trim_regs[3]  0x1F)  2;
 + d1 |= (trim_regs[1]  0x06)  1;
 + if (trim_regs[1]  0x01)
 + d1 = -d1;
 +
 + /* D2 */
 + d2 = (trim_regs[4]  0x3F)  2;
 + d2 |= (trim_regs[2]  0x06)  1;
 + if