in adc/ab8500-gpadc.c [666:888]
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;
/* 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]));
}
}
/*
* The ADC calibration data is stored in OTP registers.
* The layout of the calibration data is outlined below and a more
* detailed description can be found in UM0836
*
* vm_h/l = vmain_high/low
* bt_h/l = btemp_high/low
* vb_h/l = vbat_high/low
*
* Data bits 8500/9540:
* | 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:
* 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:
*
* vmain_high: Vin = 19500mV / ADC ideal code = 997
* vmain_low: Vin = 315mV / ADC ideal code = 16
* btemp_high: Vin = 1300mV / ADC ideal code = 985
* btemp_low: Vin = 21mV / ADC ideal code = 16
* vbat_high: Vin = 4700mV / ADC ideal code = 982
* vbat_low: Vin = 2380mV / ADC ideal code = 33
*/
if (is_ab8540(gpadc->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[AB8500_CAL_VMAIN].otp_calib_hi =
(u16)vmain_high;
gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo =
(u16)vmain_low;
gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE *
(19500 - 315) / (vmain_high - vmain_low);
gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE *
19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) /
(vmain_high - vmain_low)) * vmain_high;
} else {
gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0;
}
/* 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]);
}
/* 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[AB8500_CAL_IBAT].otp_calib_hi =
(u16)ibat_high;
gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_lo =
(u16)ibat_low;
V_gain = ((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L)
<< AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low);
V_offset = (AB8500_GPADC_IBAT_VDROP_H << AB8500_GPADC_CALIB_SHIFT_IBAT) -
(((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) <<
AB8500_GPADC_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 = (AB8500_ADC_CH_IBAT_MAX - AB8500_ADC_CH_IBAT_MIN)/
(AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V);
V2A_offset = ((AB8500_ADC_CH_IBAT_MAX_V * AB8500_ADC_CH_IBAT_MIN -
AB8500_ADC_CH_IBAT_MAX * AB8500_ADC_CH_IBAT_MIN_V)
<< AB8500_GPADC_CALIB_SHIFT_IBAT)
/ (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V);
gpadc->cal_data[AB8500_CAL_IBAT].gain =
V_gain * V2A_gain;
gpadc->cal_data[AB8500_CAL_IBAT].offset =
V_offset * V2A_gain + V2A_offset;
} else {
gpadc->cal_data[AB8500_CAL_IBAT].gain = 0;
}
} else {
/* 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[AB8500_CAL_VMAIN].otp_calib_hi =
(u16)vmain_high;
gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo =
(u16)vmain_low;
gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE *
(19500 - 315) / (vmain_high - vmain_low);
gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE *
19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) /
(vmain_high - vmain_low)) * vmain_high;
} else {
gpadc->cal_data[AB8500_CAL_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));
btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_hi = (u16)btemp_high;
gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_lo = (u16)btemp_low;
gpadc->cal_data[AB8500_CAL_BTEMP].gain =
AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
gpadc->cal_data[AB8500_CAL_BTEMP].offset = AB8500_GPADC_CALIB_SCALE * 1300 -
(AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low))
* btemp_high;
} else {
gpadc->cal_data[AB8500_CAL_BTEMP].gain = 0;
}
/* Calculate gain and offset for VBAT if all reads succeeded */
if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) {
vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_hi = (u16)vbat_high;
gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_lo = (u16)vbat_low;
gpadc->cal_data[AB8500_CAL_VBAT].gain = AB8500_GPADC_CALIB_SCALE *
(4700 - 2380) / (vbat_high - vbat_low);
gpadc->cal_data[AB8500_CAL_VBAT].offset = AB8500_GPADC_CALIB_SCALE * 4700 -
(AB8500_GPADC_CALIB_SCALE * (4700 - 2380) /
(vbat_high - vbat_low)) * vbat_high;
} else {
gpadc->cal_data[AB8500_CAL_VBAT].gain = 0;
}
}