summaryrefslogtreecommitdiffstats
path: root/drivers/mfd/ab8500-gpadc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-04-30 15:15:24 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-30 15:15:24 -0700
commit151173e8ce9b95bbbbd7eedb9035cfaffbdb7cb2 (patch)
treebca02f40bdd054fa2e30f4923e1513d40873c4d9 /drivers/mfd/ab8500-gpadc.c
parent30945669593df14692e933c557d00c4bb2727857 (diff)
parent6b170807cd5cac8dc6353f47a88ccb14bbf76c4f (diff)
downloadlinux-151173e8ce9b95bbbbd7eedb9035cfaffbdb7cb2.tar.bz2
Merge tag 'for-v3.10' of git://git.infradead.org/battery-2.6
Pull battery updates from Anton Vorontsov: "Highlights: - OpenFirmware/DeviceTree support for the Power Supply core: the core now automatically populates supplied_from hierarchy from the device tree. With these patches chargers and batteries can now lookup each other without the board files support shim. Rhyland Klein at NVIDIA did the work - New ST-Ericsson ABX500 hwmon driver. The driver is heavily using the AB85xx core and depends on some recent changes to it, so that is why the driver comes through the battery tree. It has an appropriate ack from the hwmon maintainer (i.e. Guenter Roeck). Martin Persson at ST-Ericsson and Hongbo Zhang at Linaro authored the driver - Final bits to sync AB85xx ST-Ericsson changes into mainline. The changes touch mfd parts, but these were acked by the appropriate MFD maintainer (ie Samuel Ortiz). Lee Jones at Linaro did most of the work and lead the submission process. Minor changes, but still worth mentioning: - Battery temperature reporting fix for Nokia N900 phones - Versatile Express poweroff driver moved into drivers/power/reset/ - Tree-wide: use devm_kzalloc() where appropriate - Tree-wide: dev_pm_ops cleanups/fixes" * tag 'for-v3.10' of git://git.infradead.org/battery-2.6: (112 commits) pm2301-charger: Fix suspend/resume charger-manager: Use kmemdup instead of kzalloc + memcpy power_supply: Populate supplied_from hierarchy from the device tree power_supply: Add core support for supplied_from power_supply: Define Binding for power-supplies rx51_battery: Fix reporting temperature hwmon: Add ST-Ericsson ABX500 hwmon driver ab8500_bmdata: Export abx500_res_to_temp tables for hwmon ab8500_{bmdata,fg}: Add const attributes to some data arrays ab8500_bmdata: Eliminate CamelCase warning of some variables ab8500_btemp: Make ab8500_btemp_get* interfaces public goldfish_battery: Use resource_size() lp8788-charger: Use PAGE_SIZE for the sysfs read operation max8925_power: Use devm_kzalloc() da9030_battery: Use devm_kzalloc() da9052-battery: Use devm_kzalloc() ds2760_battery: Use devm_kzalloc() ds2780_battery: Use devm_kzalloc() gpio-charger: Use devm_kzalloc() isp1704_charger: Use devm_kzalloc() ...
Diffstat (limited to 'drivers/mfd/ab8500-gpadc.c')
-rw-r--r--drivers/mfd/ab8500-gpadc.c559
1 files changed, 456 insertions, 103 deletions
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 5f341a50ee5a..65f72284185d 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -37,6 +37,13 @@
#define AB8500_GPADC_AUTODATAL_REG 0x07
#define AB8500_GPADC_AUTODATAH_REG 0x08
#define AB8500_GPADC_MUX_CTRL_REG 0x09
+#define AB8540_GPADC_MANDATA2L_REG 0x09
+#define AB8540_GPADC_MANDATA2H_REG 0x0A
+#define AB8540_GPADC_APEAAX_REG 0x10
+#define AB8540_GPADC_APEAAT_REG 0x11
+#define AB8540_GPADC_APEAAM_REG 0x12
+#define AB8540_GPADC_APEAAH_REG 0x13
+#define AB8540_GPADC_APEAAL_REG 0x14
/*
* OTP register offsets
@@ -49,19 +56,29 @@
#define AB8500_GPADC_CAL_5 0x13
#define AB8500_GPADC_CAL_6 0x14
#define AB8500_GPADC_CAL_7 0x15
+/* New calibration for 8540 */
+#define AB8540_GPADC_OTP4_REG_7 0x38
+#define AB8540_GPADC_OTP4_REG_6 0x39
+#define AB8540_GPADC_OTP4_REG_5 0x3A
/* gpadc constants */
#define EN_VINTCORE12 0x04
#define EN_VTVOUT 0x02
#define EN_GPADC 0x01
#define DIS_GPADC 0x00
-#define SW_AVG_16 0x60
+#define AVG_1 0x00
+#define AVG_4 0x20
+#define AVG_8 0x40
+#define AVG_16 0x60
#define ADC_SW_CONV 0x04
#define EN_ICHAR 0x80
#define BTEMP_PULL_UP 0x08
#define EN_BUF 0x40
#define DIS_ZERO 0x00
#define GPADC_BUSY 0x01
+#define EN_FALLING 0x10
+#define EN_TRIG_EDGE 0x02
+#define EN_VBIAS_XTAL_TEMP 0x02
/* GPADC constants from AB8500 spec, UM0836 */
#define ADC_RESOLUTION 1024
@@ -80,8 +97,21 @@
#define ADC_CH_BKBAT_MIN 0
#define ADC_CH_BKBAT_MAX 3200
+/* GPADC constants from AB8540 spec */
+#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat*/
+#define ADC_CH_IBAT_MAX 6000
+#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat*/
+#define ADC_CH_IBAT_MAX_V 60
+#define IBAT_VDROP_L (-56) /* mV */
+#define IBAT_VDROP_H 56
+
/* This is used to not lose precision when dividing to get gain and offset */
-#define CALIB_SCALE 1000
+#define CALIB_SCALE 1000
+/*
+ * Number of bits shift used to not lose precision
+ * when dividing to get ibat gain.
+ */
+#define CALIB_SHIFT_IBAT 20
/* Time in ms before disabling regulator */
#define GPADC_AUDOSUSPEND_DELAY 1
@@ -92,6 +122,7 @@ enum cal_channels {
ADC_INPUT_VMAIN = 0,
ADC_INPUT_BTEMP,
ADC_INPUT_VBAT,
+ ADC_INPUT_IBAT,
NBR_CAL_INPUTS,
};
@@ -102,8 +133,10 @@ enum cal_channels {
* @offset: Offset of the ADC channel
*/
struct adc_cal_data {
- u64 gain;
- u64 offset;
+ s64 gain;
+ s64 offset;
+ u16 otp_calib_hi;
+ u16 otp_calib_lo;
};
/**
@@ -116,7 +149,10 @@ struct adc_cal_data {
* the completion of gpadc conversion
* @ab8500_gpadc_lock: structure of type mutex
* @regu: pointer to the struct regulator
- * @irq: interrupt number that is used by gpadc
+ * @irq_sw: interrupt number that is used by gpadc for Sw
+ * conversion
+ * @irq_hw: interrupt number that is used by gpadc for Hw
+ * conversion
* @cal_data array of ADC calibration data structs
*/
struct ab8500_gpadc {
@@ -126,7 +162,8 @@ struct ab8500_gpadc {
struct completion ab8500_gpadc_complete;
struct mutex ab8500_gpadc_lock;
struct regulator *regu;
- int irq;
+ int irq_sw;
+ int irq_hw;
struct adc_cal_data cal_data[NBR_CAL_INPUTS];
};
@@ -171,6 +208,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE;
break;
+ case XTAL_TEMP:
case BAT_CTRL:
case BTEMP_BALL:
case ACC_DETECT1:
@@ -189,6 +227,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
break;
case MAIN_BAT_V:
+ case VBAT_TRUE_MEAS:
/* For some reason we don't have calibrated data */
if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) {
res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX -
@@ -232,6 +271,20 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
ADC_RESOLUTION;
break;
+ case IBAT_VIRTUAL_CHANNEL:
+ /* For some reason we don't have calibrated data */
+ if (!gpadc->cal_data[ADC_INPUT_IBAT].gain) {
+ res = ADC_CH_IBAT_MIN + (ADC_CH_IBAT_MAX -
+ ADC_CH_IBAT_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use the calibrated data */
+ res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_IBAT].gain +
+ gpadc->cal_data[ADC_INPUT_IBAT].offset)
+ >> CALIB_SHIFT_IBAT;
+ break;
+
default:
dev_err(gpadc->dev,
"unknown channel, not possible to convert\n");
@@ -244,25 +297,35 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
/**
- * ab8500_gpadc_convert() - gpadc conversion
+ * ab8500_gpadc_sw_hw_convert() - gpadc conversion
* @channel: analog channel to be converted to digital data
+ * @avg_sample: number of ADC sample to average
+ * @trig_egde: selected ADC trig edge
+ * @trig_timer: selected ADC trigger delay timer
+ * @conv_type: selected conversion type (HW or SW conversion)
*
* This function converts the selected analog i/p to digital
* data.
*/
-int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
+int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
+ u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
{
int ad_value;
int voltage;
- ad_value = ab8500_gpadc_read_raw(gpadc, channel);
- if (ad_value < 0) {
- dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
+ ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
+ trig_edge, trig_timer, conv_type);
+/* On failure retry a second time */
+ if (ad_value < 0)
+ ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
+ trig_edge, trig_timer, conv_type);
+if (ad_value < 0) {
+ dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n",
+ channel);
return ad_value;
}
voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
-
if (voltage < 0)
dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
" %d AD: 0x%x\n", channel, ad_value);
@@ -274,21 +337,46 @@ EXPORT_SYMBOL(ab8500_gpadc_convert);
/**
* ab8500_gpadc_read_raw() - gpadc read
* @channel: analog channel to be read
+ * @avg_sample: number of ADC sample to average
+ * @trig_edge: selected trig edge
+ * @trig_timer: selected ADC trigger delay timer
+ * @conv_type: selected conversion type (HW or SW conversion)
*
- * This function obtains the raw ADC value, this then needs
- * to be converted by calling ab8500_gpadc_ad_to_voltage()
+ * This function obtains the raw ADC value for an hardware conversion,
+ * this then needs to be converted by calling ab8500_gpadc_ad_to_voltage()
*/
-int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
+int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
+ u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
+{
+ int raw_data;
+ raw_data = ab8500_gpadc_double_read_raw(gpadc, channel,
+ avg_sample, trig_edge, trig_timer, conv_type, NULL);
+ return raw_data;
+}
+
+int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
+ u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type,
+ int *ibat)
{
int ret;
int looplimit = 0;
- u8 val, low_data, high_data;
+ unsigned long completion_timeout;
+ u8 val, low_data, high_data, low_data2, high_data2;
+ u8 val_reg1 = 0;
+ unsigned int delay_min = 0;
+ unsigned int delay_max = 0;
+ u8 data_low_addr, data_high_addr;
if (!gpadc)
return -ENODEV;
- mutex_lock(&gpadc->ab8500_gpadc_lock);
+ /* check if convertion is supported */
+ if ((gpadc->irq_sw < 0) && (conv_type == ADC_SW))
+ return -ENOTSUPP;
+ if ((gpadc->irq_hw < 0) && (conv_type == ADC_HW))
+ return -ENOTSUPP;
+ mutex_lock(&gpadc->ab8500_gpadc_lock);
/* Enable VTVout LDO this is required for GPADC */
pm_runtime_get_sync(gpadc->dev);
@@ -309,16 +397,34 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
}
/* Enable GPADC */
- ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC);
- if (ret < 0) {
- dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n");
- goto out;
+ val_reg1 |= EN_GPADC;
+
+ /* Select the channel source and set average samples */
+ switch (avg_sample) {
+ case SAMPLE_1:
+ val = channel | AVG_1;
+ break;
+ case SAMPLE_4:
+ val = channel | AVG_4;
+ break;
+ case SAMPLE_8:
+ val = channel | AVG_8;
+ break;
+ default:
+ val = channel | AVG_16;
+ break;
}
- /* Select the channel source and set average samples to 16 */
- ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
- AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
+ if (conv_type == ADC_HW) {
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val);
+ val_reg1 |= EN_TRIG_EDGE;
+ if (trig_edge)
+ val_reg1 |= EN_FALLING;
+ }
+ else
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: set avg samples failed\n");
@@ -333,71 +439,129 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
switch (channel) {
case MAIN_CHARGER_C:
case USB_CHARGER_C:
- ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
- EN_BUF | EN_ICHAR,
- EN_BUF | EN_ICHAR);
+ val_reg1 |= EN_BUF | EN_ICHAR;
break;
case BTEMP_BALL:
if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
- /* Turn on btemp pull-up on ABB 3.0 */
- ret = abx500_mask_and_set_register_interruptible(
- gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
- EN_BUF | BTEMP_PULL_UP,
- EN_BUF | BTEMP_PULL_UP);
-
- /*
- * Delay might be needed for ABB8500 cut 3.0, if not, remove
- * when hardware will be available
- */
- usleep_range(1000, 1000);
+ val_reg1 |= EN_BUF | BTEMP_PULL_UP;
+ /*
+ * Delay might be needed for ABB8500 cut 3.0, if not,
+ * remove when hardware will be availible
+ */
+ delay_min = 1000; /* Delay in micro seconds */
+ delay_max = 10000; /* large range to optimise sleep mode */
break;
}
/* Intentional fallthrough */
default:
- ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
+ val_reg1 |= EN_BUF;
break;
}
+
+ /* Write configuration to register */
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG, val_reg1);
if (ret < 0) {
dev_err(gpadc->dev,
- "gpadc_conversion: select falling edge failed\n");
+ "gpadc_conversion: set Control register failed\n");
goto out;
}
- ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV);
- if (ret < 0) {
- dev_err(gpadc->dev,
- "gpadc_conversion: start s/w conversion failed\n");
- goto out;
+ if (delay_min != 0)
+ usleep_range(delay_min, delay_max);
+
+ if (conv_type == ADC_HW) {
+ /* Set trigger delay timer */
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: trig timer failed\n");
+ goto out;
+ }
+ completion_timeout = 2 * HZ;
+ data_low_addr = AB8500_GPADC_AUTODATAL_REG;
+ data_high_addr = AB8500_GPADC_AUTODATAH_REG;
+ } else {
+ /* Start SW conversion */
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+ ADC_SW_CONV, ADC_SW_CONV);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: start s/w conv failed\n");
+ goto out;
+ }
+ completion_timeout = msecs_to_jiffies(CONVERSION_TIME);
+ data_low_addr = AB8500_GPADC_MANDATAL_REG;
+ data_high_addr = AB8500_GPADC_MANDATAH_REG;
}
+
/* wait for completion of conversion */
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
- msecs_to_jiffies(CONVERSION_TIME))) {
+ completion_timeout)) {
dev_err(gpadc->dev,
- "timeout: didn't receive GPADC conversion interrupt\n");
+ "timeout didn't receive GPADC conv interrupt\n");
ret = -EINVAL;
goto out;
}
/* Read the converted RAW data */
- ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
- AB8500_GPADC_MANDATAL_REG, &low_data);
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, data_low_addr, &low_data);
if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n");
goto out;
}
- ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
- AB8500_GPADC_MANDATAH_REG, &high_data);
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, data_high_addr, &high_data);
if (ret < 0) {
- dev_err(gpadc->dev,
- "gpadc_conversion: read high data failed\n");
+ dev_err(gpadc->dev, "gpadc_conversion: read high data failed\n");
goto out;
}
+ /* Check if double convertion is required */
+ if ((channel == BAT_CTRL_AND_IBAT) ||
+ (channel == VBAT_MEAS_AND_IBAT) ||
+ (channel == VBAT_TRUE_MEAS_AND_IBAT) ||
+ (channel == BAT_TEMP_AND_IBAT)) {
+
+ if (conv_type == ADC_HW) {
+ /* not supported */
+ ret = -ENOTSUPP;
+ dev_err(gpadc->dev,
+ "gpadc_conversion: only SW double conversion supported\n");
+ goto out;
+ } else {
+ /* Read the converted RAW data 2 */
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG,
+ &low_data2);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read sw low data 2 failed\n");
+ goto out;
+ }
+
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG,
+ &high_data2);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read sw high data 2 failed\n");
+ goto out;
+ }
+ if (ibat != NULL) {
+ *ibat = (high_data2 << 8) | low_data2;
+ } else {
+ dev_warn(gpadc->dev,
+ "gpadc_conversion: ibat not stored\n");
+ }
+
+ }
+ }
+
/* Disable GPADC */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
@@ -406,6 +570,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out;
}
+ /* Disable VTVout LDO this is required for GPADC */
pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev);
@@ -422,9 +587,7 @@ out:
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
-
pm_runtime_put(gpadc->dev);
-
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
@@ -433,16 +596,16 @@ out:
EXPORT_SYMBOL(ab8500_gpadc_read_raw);
/**
- * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
+ * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion
* @irq: irq number
* @data: pointer to the data passed during request irq
*
- * This is a interrupt service routine for s/w gpadc conversion completion.
+ * This is a interrupt service routine for gpadc conversion completion.
* Notifies the gpadc completion is completed and the converted raw value
* can be read from the registers.
* Returns IRQ status(IRQ_HANDLED)
*/
-static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc)
+static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc)
{
struct ab8500_gpadc *gpadc = _gpadc;
@@ -461,15 +624,27 @@ static int otp_cal_regs[] = {
AB8500_GPADC_CAL_7,
};
+static int otp4_cal_regs[] = {
+ AB8540_GPADC_OTP4_REG_7,
+ AB8540_GPADC_OTP4_REG_6,
+ AB8540_GPADC_OTP4_REG_5,
+};
+
static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
{
int i;
int ret[ARRAY_SIZE(otp_cal_regs)];
u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)];
-
+ int ret_otp4[ARRAY_SIZE(otp4_cal_regs)];
+ u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)];
int vmain_high, vmain_low;
int btemp_high, btemp_low;
int vbat_high, vbat_low;
+ int ibat_high, ibat_low;
+ s64 V_gain, V_offset, V2A_gain, V2A_offset;
+ struct ab8500 *ab8500;
+
+ ab8500 = gpadc->parent;
/* First we read all OTP registers and store the error code */
for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
@@ -489,7 +664,7 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* bt_h/l = btemp_high/low
* vb_h/l = vbat_high/low
*
- * Data bits:
+ * Data bits 8500/9540:
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
* |.......|.......|.......|.......|.......|.......|.......|.......
* | | vm_h9 | vm_h8
@@ -507,6 +682,35 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
* |.......|.......|.......|.......|.......|.......|.......|.......
*
+ * Data bits 8540:
+ * OTP2
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * |
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ *
+ * Data bits 8540:
+ * OTP4
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | | ib_h9 | ib_h8 | ib_h7
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 |
+ *
*
* Ideal output ADC codes corresponding to injected input voltages
* during manufacturing is:
@@ -519,38 +723,116 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* vbat_low: Vin = 2380mV / ADC ideal code = 33
*/
- /* Calculate gain and offset for VMAIN if all reads succeeded */
- if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
- vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
- ((gpadc_cal[1] & 0x3F) << 2) |
- ((gpadc_cal[2] & 0xC0) >> 6));
+ if (is_ab8540(ab8500)) {
+ /* Calculate gain and offset for VMAIN if all reads succeeded*/
+ if (!(ret[1] < 0 || ret[2] < 0)) {
+ vmain_high = (((gpadc_cal[1] & 0xFF) << 2) |
+ ((gpadc_cal[2] & 0xC0) >> 6));
+ vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
+ (u16)vmain_high;
+ gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
+ (u16)vmain_low;
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
+ (19500 - 315) / (vmain_high - vmain_low);
+ gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
+ 19500 - (CALIB_SCALE * (19500 - 315) /
+ (vmain_high - vmain_low)) * vmain_high;
+ } else {
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+ }
- vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+ /* Read IBAT calibration Data */
+ for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) {
+ ret_otp4[i] = abx500_get_register_interruptible(
+ gpadc->dev, AB8500_OTP_EMUL,
+ otp4_cal_regs[i], &gpadc_otp4[i]);
+ if (ret_otp4[i] < 0)
+ dev_err(gpadc->dev,
+ "%s: read otp4 reg 0x%02x failed\n",
+ __func__, otp4_cal_regs[i]);
+ }
- gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
- (19500 - 315) / (vmain_high - vmain_low);
+ /* Calculate gain and offset for IBAT if all reads succeeded */
+ if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) {
+ ibat_high = (((gpadc_otp4[0] & 0x07) << 7) |
+ ((gpadc_otp4[1] & 0xFE) >> 1));
+ ibat_low = (((gpadc_otp4[1] & 0x01) << 5) |
+ ((gpadc_otp4[2] & 0xF8) >> 3));
+
+ gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi =
+ (u16)ibat_high;
+ gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo =
+ (u16)ibat_low;
+
+ V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L)
+ << CALIB_SHIFT_IBAT) / (ibat_high - ibat_low);
+
+ V_offset = (IBAT_VDROP_H << CALIB_SHIFT_IBAT) -
+ (((IBAT_VDROP_H - IBAT_VDROP_L) <<
+ CALIB_SHIFT_IBAT) / (ibat_high - ibat_low))
+ * ibat_high;
+ /*
+ * Result obtained is in mV (at a scale factor),
+ * we need to calculate gain and offset to get mA
+ */
+ V2A_gain = (ADC_CH_IBAT_MAX - ADC_CH_IBAT_MIN)/
+ (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
+ V2A_offset = ((ADC_CH_IBAT_MAX_V * ADC_CH_IBAT_MIN -
+ ADC_CH_IBAT_MAX * ADC_CH_IBAT_MIN_V)
+ << CALIB_SHIFT_IBAT)
+ / (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
+
+ gpadc->cal_data[ADC_INPUT_IBAT].gain = V_gain * V2A_gain;
+ gpadc->cal_data[ADC_INPUT_IBAT].offset = V_offset *
+ V2A_gain + V2A_offset;
+ } else {
+ gpadc->cal_data[ADC_INPUT_IBAT].gain = 0;
+ }
- gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 -
- (CALIB_SCALE * (19500 - 315) /
- (vmain_high - vmain_low)) * vmain_high;
+ dev_dbg(gpadc->dev, "IBAT gain %llu offset %llu\n",
+ gpadc->cal_data[ADC_INPUT_IBAT].gain,
+ gpadc->cal_data[ADC_INPUT_IBAT].offset);
} else {
- gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+ /* Calculate gain and offset for VMAIN if all reads succeeded */
+ if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
+ vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
+ ((gpadc_cal[1] & 0x3F) << 2) |
+ ((gpadc_cal[2] & 0xC0) >> 6));
+ vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
+ (u16)vmain_high;
+ gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
+ (u16)vmain_low;
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
+ (19500 - 315) / (vmain_high - vmain_low);
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
+ 19500 - (CALIB_SCALE * (19500 - 315) /
+ (vmain_high - vmain_low)) * vmain_high;
+ } else {
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+ }
}
/* Calculate gain and offset for BTEMP if all reads succeeded */
if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) {
btemp_high = (((gpadc_cal[2] & 0x01) << 9) |
- (gpadc_cal[3] << 1) |
- ((gpadc_cal[4] & 0x80) >> 7));
-
+ (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7));
btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
+ gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi = (u16)btemp_high;
+ gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo = (u16)btemp_low;
+
gpadc->cal_data[ADC_INPUT_BTEMP].gain =
CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
-
gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 -
- (CALIB_SCALE * (1300 - 21) /
- (btemp_high - btemp_low)) * btemp_high;
+ (CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low))
+ * btemp_high;
} else {
gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0;
}
@@ -560,9 +842,11 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
+ gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi = (u16)vbat_high;
+ gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo = (u16)vbat_low;
+
gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE *
(4700 - 2380) / (vbat_high - vbat_low);
-
gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 -
(CALIB_SCALE * (4700 - 2380) /
(vbat_high - vbat_low)) * vbat_high;
@@ -608,6 +892,31 @@ static int ab8500_gpadc_runtime_idle(struct device *dev)
return 0;
}
+static int ab8500_gpadc_suspend(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ mutex_lock(&gpadc->ab8500_gpadc_lock);
+
+ pm_runtime_get_sync(dev);
+
+ regulator_disable(gpadc->regu);
+ return 0;
+}
+
+static int ab8500_gpadc_resume(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ regulator_enable(gpadc->regu);
+
+ pm_runtime_mark_last_busy(gpadc->dev);
+ pm_runtime_put_autosuspend(gpadc->dev);
+
+ mutex_unlock(&gpadc->ab8500_gpadc_lock);
+ return 0;
+}
+
static int ab8500_gpadc_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -619,13 +928,13 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
return -ENOMEM;
}
- gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
- if (gpadc->irq < 0) {
- dev_err(&pdev->dev, "failed to get platform irq-%d\n",
- gpadc->irq);
- ret = gpadc->irq;
- goto fail;
- }
+ gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
+ if (gpadc->irq_sw < 0)
+ dev_err(gpadc->dev, "failed to get platform sw_conv_end irq\n");
+
+ gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
+ if (gpadc->irq_hw < 0)
+ dev_err(gpadc->dev, "failed to get platform hw_conv_end irq\n");
gpadc->dev = &pdev->dev;
gpadc->parent = dev_get_drvdata(pdev->dev.parent);
@@ -634,15 +943,31 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
/* Initialize completion used to notify completion of conversion */
init_completion(&gpadc->ab8500_gpadc_complete);
- /* Register interrupt - SwAdcComplete */
- ret = request_threaded_irq(gpadc->irq, NULL,
- ab8500_bm_gpswadcconvend_handler,
- IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
- "ab8500-gpadc", gpadc);
- if (ret < 0) {
- dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
- gpadc->irq);
- goto fail;
+ /* Register interrupts */
+ if (gpadc->irq_sw >= 0) {
+ ret = request_threaded_irq(gpadc->irq_sw, NULL,
+ ab8500_bm_gpadcconvend_handler,
+ IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw",
+ gpadc);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "Failed to register interrupt irq: %d\n",
+ gpadc->irq_sw);
+ goto fail;
+ }
+ }
+
+ if (gpadc->irq_hw >= 0) {
+ ret = request_threaded_irq(gpadc->irq_hw, NULL,
+ ab8500_bm_gpadcconvend_handler,
+ IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw",
+ gpadc);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "Failed to register interrupt irq: %d\n",
+ gpadc->irq_hw);
+ goto fail_irq;
+ }
}
/* VTVout LDO used to power up ab8500-GPADC */
@@ -669,11 +994,13 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
ab8500_gpadc_read_calibration_data(gpadc);
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
dev_dbg(gpadc->dev, "probe success\n");
+
return 0;
fail_enable:
fail_irq:
- free_irq(gpadc->irq, gpadc);
+ free_irq(gpadc->irq_sw, gpadc);
+ free_irq(gpadc->irq_hw, gpadc);
fail:
kfree(gpadc);
gpadc = NULL;
@@ -687,7 +1014,10 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
/* remove this gpadc entry from the list */
list_del(&gpadc->node);
/* remove interrupt - completion of Sw ADC conversion */
- free_irq(gpadc->irq, gpadc);
+ if (gpadc->irq_sw >= 0)
+ free_irq(gpadc->irq_sw, gpadc);
+ if (gpadc->irq_hw >= 0)
+ free_irq(gpadc->irq_hw, gpadc);
pm_runtime_get_sync(gpadc->dev);
pm_runtime_disable(gpadc->dev);
@@ -707,6 +1037,9 @@ static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
ab8500_gpadc_runtime_resume,
ab8500_gpadc_runtime_idle)
+ SET_SYSTEM_SLEEP_PM_OPS(ab8500_gpadc_suspend,
+ ab8500_gpadc_resume)
+
};
static struct platform_driver ab8500_gpadc_driver = {
@@ -729,10 +1062,30 @@ static void __exit ab8500_gpadc_exit(void)
platform_driver_unregister(&ab8500_gpadc_driver);
}
+/**
+ * ab8540_gpadc_get_otp() - returns OTP values
+ *
+ */
+void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
+ u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h,
+ u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h)
+{
+ *vmain_l = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo;
+ *vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi;
+ *btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo;
+ *btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi;
+ *vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo;
+ *vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi;
+ *ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo;
+ *ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi;
+ return ;
+}
+
subsys_initcall_sync(ab8500_gpadc_init);
module_exit(ab8500_gpadc_exit);
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson");
+MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson,"
+ "M'boumba Cedric Madianga");
MODULE_ALIAS("platform:ab8500_gpadc");
MODULE_DESCRIPTION("AB8500 GPADC driver");