/* GPADC constants from AB8540 spec */ #define AB8500_ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */ #define AB8500_ADC_CH_IBAT_MAX 6000 #define AB8500_ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */ #define AB8500_ADC_CH_IBAT_MAX_V 60 #define AB8500_GPADC_IBAT_VDROP_L (-56) /* mV */ #define AB8500_GPADC_IBAT_VDROP_H 56
/* This is used to not lose precision when dividing to get gain and offset */ #define AB8500_GPADC_CALIB_SCALE 1000 /* *Numberofbitsshiftusedtonotloseprecision *whendividingtogetibatgain.
*/ #define AB8500_GPADC_CALIB_SHIFT_IBAT 20
/* Time in ms before disabling regulator */ #define AB8500_GPADC_AUTOSUSPEND_DELAY 1
for (i = 0; i < gpadc->nchans; i++) {
ch = &gpadc->chans[i]; if (ch->id == chan) break;
} if (i == gpadc->nchans) return NULL;
return ch;
}
/** *ab8500_gpadc_ad_to_voltage()-ConvertarawADCvaluetoavoltage *@gpadc:GPADCinstance *@ch:thesampledchannelthisrawvalueiscomingfrom *@ad_value:therawvalue
*/ staticint ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, enum ab8500_gpadc_channel ch, int ad_value)
{ int res;
switch (ch) { case AB8500_GPADC_CHAN_MAIN_CHARGER: /* No calibration data available: just interpolate */ if (!gpadc->cal_data[AB8500_CAL_VMAIN].gain) {
res = AB8500_ADC_CH_CHG_V_MIN + (AB8500_ADC_CH_CHG_V_MAX -
AB8500_ADC_CH_CHG_V_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
} /* Here we can use calibration */
res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VMAIN].gain +
gpadc->cal_data[AB8500_CAL_VMAIN].offset) / AB8500_GPADC_CALIB_SCALE; break;
case AB8500_GPADC_CHAN_BAT_CTRL: case AB8500_GPADC_CHAN_BAT_TEMP: case AB8500_GPADC_CHAN_ACC_DET_1: case AB8500_GPADC_CHAN_ADC_AUX_1: case AB8500_GPADC_CHAN_ADC_AUX_2: case AB8500_GPADC_CHAN_XTAL_TEMP: /* No calibration data available: just interpolate */ if (!gpadc->cal_data[AB8500_CAL_BTEMP].gain) {
res = AB8500_ADC_CH_BTEMP_MIN + (AB8500_ADC_CH_BTEMP_MAX -
AB8500_ADC_CH_BTEMP_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
} /* Here we can use calibration */
res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_BTEMP].gain +
gpadc->cal_data[AB8500_CAL_BTEMP].offset) / AB8500_GPADC_CALIB_SCALE; break;
case AB8500_GPADC_CHAN_VBAT_A: case AB8500_GPADC_CHAN_VBAT_TRUE_MEAS: /* No calibration data available: just interpolate */ if (!gpadc->cal_data[AB8500_CAL_VBAT].gain) {
res = AB8500_ADC_CH_VBAT_MIN + (AB8500_ADC_CH_VBAT_MAX -
AB8500_ADC_CH_VBAT_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
} /* Here we can use calibration */
res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VBAT].gain +
gpadc->cal_data[AB8500_CAL_VBAT].offset) / AB8500_GPADC_CALIB_SCALE; break;
case AB8505_GPADC_CHAN_DIE_TEMP:
res = AB8500_ADC_CH_DIETEMP_MIN +
(AB8500_ADC_CH_DIETEMP_MAX - AB8500_ADC_CH_DIETEMP_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_ACC_DET_2:
res = AB8500_ADC_CH_ACCDET2_MIN +
(AB8500_ADC_CH_ACCDET2_MAX - AB8500_ADC_CH_ACCDET2_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_VBUS:
res = AB8500_ADC_CH_CHG_V_MIN +
(AB8500_ADC_CH_CHG_V_MAX - AB8500_ADC_CH_CHG_V_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT:
res = AB8500_ADC_CH_CHG_I_MIN +
(AB8500_ADC_CH_CHG_I_MAX - AB8500_ADC_CH_CHG_I_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_BACKUP_BAT:
res = AB8500_ADC_CH_BKBAT_MIN +
(AB8500_ADC_CH_BKBAT_MAX - AB8500_ADC_CH_BKBAT_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
case AB8500_GPADC_CHAN_IBAT_VIRTUAL: /* No calibration data available: just interpolate */ if (!gpadc->cal_data[AB8500_CAL_IBAT].gain) {
res = AB8500_ADC_CH_IBAT_MIN + (AB8500_ADC_CH_IBAT_MAX -
AB8500_ADC_CH_IBAT_MIN) * ad_value /
AB8500_ADC_RESOLUTION; break;
} /* Here we can use calibration */
res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_IBAT].gain +
gpadc->cal_data[AB8500_CAL_IBAT].offset)
>> AB8500_GPADC_CALIB_SHIFT_IBAT; break;
default:
dev_err(gpadc->dev, "unknown channel ID: %d, not possible to convert\n",
ch);
res = -EINVAL; break;
/* 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;
}
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;
}
/* *EnableADC,buffering,selectrisingedgeandenableADCpath *chargingcurrentsenseifitneeded,ABB3.0needssomespecial *treatmenttoo.
*/ 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; /* *DelaymightbeneededforABB8500cut3.0,ifnot, *removewhenhardwarewillbeavailable
*/
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);
/* 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");
}
staticvoid 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;
/* First we read all OTP registers and store the error code */ for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
ret[i] = abx500_get_register_interruptible(gpadc->dev,
AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); if (ret[i] < 0) { /* Continue anyway: maybe the other registers are OK */
dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n",
__func__, otp_cal_regs[i]);
} else { /* Put this in the entropy pool as device-unique */
add_device_randomness(&ret[i], sizeof(ret[i]));
}
}
ret = fwnode_property_read_u32(fwnode, "reg", &chan); if (ret) {
dev_err(dev, "invalid channel number %s\n", name); return ret;
} if (chan > AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT) {
dev_err(dev, "%s channel number out of range %d\n", name, chan); return -EINVAL;
}
iio_chan->channel = chan;
iio_chan->datasheet_name = name;
iio_chan->indexed = 1;
iio_chan->address = chan;
iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_PROCESSED); /* Most are voltages (also temperatures), some are currents */ if ((chan == AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT) ||
(chan == AB8500_GPADC_CHAN_USB_CHARGER_CURRENT))
iio_chan->type = IIO_CURRENT; else
iio_chan->type = IIO_VOLTAGE;
if (gpadc->irq_hw) {
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, "ab8500-gpadc-hw", gpadc); if (ret < 0) {
dev_err(dev, "Failed to request hw conversion irq: %d\n",
gpadc->irq_hw); return ret;
}
}
/* The VTVout LDO used to power the AB8500 GPADC */
gpadc->vddadc = devm_regulator_get(dev, "vddadc"); if (IS_ERR(gpadc->vddadc)) return dev_err_probe(dev, PTR_ERR(gpadc->vddadc), "failed to get vddadc\n");
ret = regulator_enable(gpadc->vddadc); if (ret) {
dev_err(dev, "failed to enable vddadc: %d\n", ret); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.