in adc/ab8500-gpadc.c [386:629]
static int ab8500_gpadc_read(struct ab8500_gpadc *gpadc,
const struct ab8500_gpadc_chan_info *ch,
int *ibat)
{
int ret;
int looplimit = 0;
unsigned long completion_timeout;
u8 val;
u8 low_data, high_data, low_data2, high_data2;
u8 ctrl1;
u8 ctrl23;
unsigned int delay_min = 0;
unsigned int delay_max = 0;
u8 data_low_addr, data_high_addr;
if (!gpadc)
return -ENODEV;
/* check if conversion is supported */
if ((gpadc->irq_sw <= 0) && !ch->hardware_control)
return -ENOTSUPP;
if ((gpadc->irq_hw <= 0) && ch->hardware_control)
return -ENOTSUPP;
/* Enable vddadc by grabbing PM runtime */
pm_runtime_get_sync(gpadc->dev);
/* Check if ADC is not busy, lock and proceed */
do {
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_STAT_REG, &val);
if (ret < 0)
goto out;
if (!(val & AB8500_GPADC_STAT_BUSY))
break;
msleep(20);
} while (++looplimit < 10);
if (looplimit >= 10 && (val & AB8500_GPADC_STAT_BUSY)) {
dev_err(gpadc->dev, "gpadc_conversion: GPADC busy");
ret = -EINVAL;
goto out;
}
/* Enable GPADC */
ctrl1 = AB8500_GPADC_CTRL1_ENABLE;
/* Select the channel source and set average samples */
switch (ch->avg_sample) {
case 1:
ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_1;
break;
case 4:
ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_4;
break;
case 8:
ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_8;
break;
default:
ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_16;
break;
}
if (ch->hardware_control) {
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL3_REG, ctrl23);
ctrl1 |= AB8500_GPADC_CTRL1_TRIG_ENA;
if (ch->falling_edge)
ctrl1 |= AB8500_GPADC_CTRL1_TRIG_EDGE;
} else {
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL2_REG, ctrl23);
}
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: set avg samples failed\n");
goto out;
}
/*
* Enable ADC, buffering, select rising edge and enable ADC path
* charging current sense if it needed, ABB 3.0 needs some special
* treatment too.
*/
switch (ch->id) {
case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT:
case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT:
ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA |
AB8500_GPADC_CTRL1_ICHAR_ENA;
break;
case AB8500_GPADC_CHAN_BAT_TEMP:
if (!is_ab8500_2p0_or_earlier(gpadc->ab8500)) {
ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA |
AB8500_GPADC_CTRL1_BTEMP_PULL_UP;
/*
* Delay might be needed for ABB8500 cut 3.0, if not,
* remove when hardware will be available
*/
delay_min = 1000; /* Delay in micro seconds */
delay_max = 10000; /* large range optimises sleepmode */
break;
}
fallthrough;
default:
ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA;
break;
}
/* Write configuration to control register 1 */
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ctrl1);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: set Control register failed\n");
goto out;
}
if (delay_min != 0)
usleep_range(delay_min, delay_max);
if (ch->hardware_control) {
/* Set trigger delay timer */
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG,
ch->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,
AB8500_GPADC_CTRL1_START_SW_CONV,
AB8500_GPADC_CTRL1_START_SW_CONV);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: start s/w conv failed\n");
goto out;
}
completion_timeout = msecs_to_jiffies(AB8500_GPADC_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->complete,
completion_timeout)) {
dev_err(gpadc->dev,
"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, 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, data_high_addr, &high_data);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: read high data failed\n");
goto out;
}
/* Check if double conversion is required */
if ((ch->id == AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT) ||
(ch->id == AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT) ||
(ch->id == AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT) ||
(ch->id == AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT)) {
if (ch->hardware_control) {
/* 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, AB8500_GPADC_CTRL1_DISABLE);
if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
goto out;
}
/* This eventually drops the regulator */
pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev);
return (high_data << 8) | low_data;
out:
/*
* It has shown to be needed to turn off the GPADC if an error occurs,
* otherwise we might have problem when waiting for the busy bit in the
* GPADC status register to go low. In V1.1 there wait_for_completion
* seems to timeout when waiting for an interrupt.. Not seen in V2.0
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE);
pm_runtime_put(gpadc->dev);
dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", ch->id);
return ret;
}