Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31a1d5e9 authored by Lakshmi Sunkara's avatar Lakshmi Sunkara
Browse files

hwmon: qpnp-adc-voltage: Update VADC_HC peripheral support



Update VADC_HC peripheral support.

This snapshot is taken as of msm-4.4
commit 9b3e8a81dcee ("Merge "qcom: smb2:
ensure QC adapter is at 5V at shutdown"")

Change-Id: Ib05d96f1c1e5327474c5cb5d5e97d3076bb0f547
Signed-off-by: default avatarLakshmi Sunkara <lakshmis@codeaurora.org>
parent 4a2c6023
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -38,6 +38,9 @@
#define PMI_CHG_SCALE_1		-138890
#define PMI_CHG_SCALE_2		391750000000
#define QPNP_VADC_HC_VREF_CODE		0x4000
#define QPNP_VADC_HC_VDD_REFERENCE_MV	1875
/* Clamp negative ADC code to 0 */
#define QPNP_VADC_HC_MAX_CODE		0x7FFF

/* Units for temperature below (on x axis) is in 0.1DegC as
   required by the battery driver. Note the resolution used
@@ -859,6 +862,8 @@ int32_t qpnp_adc_scale_pmic_therm(struct qpnp_vadc_chip *vadc,

	if (adc_properties->adc_hc) {
		/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
		if (adc_code > QPNP_VADC_HC_MAX_CODE)
			adc_code = 0;
		pmic_voltage = (int64_t) adc_code;
		pmic_voltage *= (int64_t) (adc_properties->adc_vdd_reference
							* 1000);
@@ -959,6 +964,8 @@ int32_t qpnp_adc_tdkntcg_therm(struct qpnp_vadc_chip *chip,

	if (adc_properties->adc_hc) {
		/* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
		if (adc_code > QPNP_VADC_HC_MAX_CODE)
			adc_code = 0;
		xo_thm_voltage = (int64_t) adc_code;
		xo_thm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
							* 1000);
@@ -1194,6 +1201,8 @@ int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *chip,

	if (adc_properties->adc_hc) {
		/* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
		if (adc_code > QPNP_VADC_HC_MAX_CODE)
			adc_code = 0;
		therm_voltage = (int64_t) adc_code;
		therm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
							* 1000);
@@ -1330,6 +1339,8 @@ int32_t qpnp_adc_scale_default(struct qpnp_vadc_chip *vadc,

	if (adc_properties->adc_hc) {
		/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
		if (adc_code > QPNP_VADC_HC_MAX_CODE)
			adc_code = 0;
		scale_voltage = (int64_t) adc_code;
		scale_voltage *= (adc_properties->adc_vdd_reference * 1000);
		scale_voltage = div64_s64(scale_voltage,
+383 −303
Original line number Diff line number Diff line
@@ -228,6 +228,8 @@ static struct qpnp_vadc_rscale_fn adc_vadc_rscale_fn[] = {
	[SCALE_RVADC_ABSOLUTE] = {qpnp_vadc_absolute_rthr},
};

static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc);

static int32_t qpnp_vadc_read_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
						u8 *data, int len)
{
@@ -258,6 +260,17 @@ static int32_t qpnp_vadc_write_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
	return 0;
}

static int qpnp_vadc_is_valid(struct qpnp_vadc_chip *vadc)
{
	struct qpnp_vadc_chip *vadc_chip = NULL;

	list_for_each_entry(vadc_chip, &qpnp_vadc_device_list, list)
		if (vadc == vadc_chip)
			return 0;

	return -EINVAL;
}

static int32_t qpnp_vadc_warm_rst_configure(struct qpnp_vadc_chip *vadc)
{
	int rc = 0;
@@ -372,6 +385,267 @@ static int32_t qpnp_vadc_status_debug(struct qpnp_vadc_chip *vadc)

	return 0;
}

static int qpnp_vadc_hc_check_conversion_status(struct qpnp_vadc_chip *vadc)
{
	int rc = 0, count = 0;
	u8 status1 = 0;

	while (status1 != QPNP_VADC_STATUS1_EOC) {
		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
		if (rc < 0)
			return rc;
		status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
		if (status1 == QPNP_VADC_STATUS1_EOC)
			break;
		usleep_range(QPNP_VADC_HC1_CONV_TIME_MIN_US,
				QPNP_VADC_HC1_CONV_TIME_MAX_US);
		count++;
		if (count > QPNP_VADC_HC1_ERR_COUNT) {
			pr_err("retry error exceeded\n");
			rc = qpnp_vadc_status_debug(vadc);
			if (rc < 0)
				pr_err("VADC disable failed with %d\n", rc);
			return -EINVAL;
		}
	}

	return rc;
}

static int qpnp_vadc_hc_read_data(struct qpnp_vadc_chip *vadc, int *data)
{
	int rc = 0;
	u8 buf = 0, rslt_lsb = 0, rslt_msb = 0;

	/* Set hold bit */
	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
	if (rc) {
		pr_err("debug register dump failed\n");
		return rc;
	}
	buf |= QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
	if (rc) {
		pr_err("debug register dump failed\n");
		return rc;
	}

	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA0, &rslt_lsb, 1);
	if (rc < 0) {
		pr_err("qpnp adc result read failed for data0\n");
		return rc;
	}

	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA1, &rslt_msb, 1);
	if (rc < 0) {
		pr_err("qpnp adc result read failed for data1\n");
		return rc;
	}

	*data = (rslt_msb << 8) | rslt_lsb;

	if (*data == QPNP_VADC_HC1_DATA_CHECK_USR) {
		pr_err("Invalid data :0x%x\n", *data);
		return -EINVAL;
	}

	rc = qpnp_vadc_enable(vadc, false);
	if (rc) {
		pr_err("VADC disable failed\n");
		return rc;
	}

	/* De-assert hold bit */
	buf &= ~QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
	if (rc)
		pr_err("de-asserting hold bit failed\n");

	return rc;
}

static void qpnp_vadc_hc_update_adc_dig_param(struct qpnp_vadc_chip *vadc,
			struct qpnp_adc_amux_properties *amux_prop, u8 *data)
{
	/* Update CAL value */
	*data &= ~QPNP_VADC_HC1_CAL_VAL;
	*data |= (amux_prop->cal_val << QPNP_VADC_HC1_CAL_VAL_SHIFT);

	/* Update CAL select */
	*data &= ~QPNP_VADC_HC1_CAL_SEL_MASK;
	*data |= (amux_prop->calib_type << QPNP_VADC_HC1_CAL_SEL_SHIFT);

	/* Update Decimation ratio select */
	*data &= ~QPNP_VADC_HC1_DEC_RATIO_SEL;
	*data |= (amux_prop->decimation << QPNP_VADC_HC1_DEC_RATIO_SHIFT);

	pr_debug("VADC_DIG_PARAM value:0x%x\n", *data);
}

static int qpnp_vadc_hc_configure(struct qpnp_vadc_chip *vadc,
				struct qpnp_adc_amux_properties *amux_prop)
{
	int rc = 0;
	u8 buf[6];

	/* Read registers 0x42 through 0x46 */
	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
	if (rc < 0) {
		pr_err("qpnp adc configure block read failed\n");
		return rc;
	}

	/* ADC Digital param selection */
	qpnp_vadc_hc_update_adc_dig_param(vadc, amux_prop, &buf[0]);

	/* Update fast average sample value */
	buf[1] &= (u8) ~QPNP_VADC_HC1_FAST_AVG_SAMPLES_MASK;
	buf[1] |= amux_prop->fast_avg_setup;

	/* Select ADC channel */
	buf[2] = amux_prop->amux_channel;

	/* Select hw settle delay for the channel */
	buf[3] &= (u8) ~QPNP_VADC_HC1_DELAY_CTL_MASK;
	buf[3] |= amux_prop->hw_settle_time;

	/* Select ADC enable */
	buf[4] |= QPNP_VADC_HC1_ADC_EN;

	/* Select CONV request */
	buf[5] |= QPNP_VADC_HC1_CONV_REQ_START;

	if (!vadc->vadc_poll_eoc)
		reinit_completion(&vadc->adc->adc_rslt_completion);

	pr_debug("dig:0x%x, fast_avg:0x%x, channel:0x%x, hw_settle:0x%x\n",
		buf[0], buf[1], buf[2], buf[3]);

	/* Block register write from 0x42 through 0x46 */
	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
	if (rc < 0) {
		pr_err("qpnp adc block register configure failed\n");
		return rc;
	}

	return 0;
}

int32_t qpnp_vadc_hc_read(struct qpnp_vadc_chip *vadc,
				enum qpnp_vadc_channels channel,
				struct qpnp_vadc_result *result)
{
	int rc = 0, scale_type, amux_prescaling, dt_index = 0, calib_type = 0;
	struct qpnp_adc_amux_properties amux_prop;

	if (qpnp_vadc_is_valid(vadc))
		return -EPROBE_DEFER;

	mutex_lock(&vadc->adc->adc_lock);

	while ((vadc->adc->adc_channels[dt_index].channel_num
		!= channel) && (dt_index < vadc->max_channels_available))
		dt_index++;

	if (dt_index >= vadc->max_channels_available) {
		pr_err("not a valid VADC channel:%d\n", channel);
		rc = -EINVAL;
		goto fail_unlock;
	}

	if (!vadc->vadc_init_calib) {
		rc = qpnp_vadc_calib_device(vadc);
		if (rc) {
			pr_err("Calibration failed\n");
			goto fail_unlock;
		} else {
			vadc->vadc_init_calib = true;
		}
	}

	calib_type = vadc->adc->adc_channels[dt_index].calib_type;
	if (calib_type >= ADC_HC_CAL_SEL_NONE) {
		pr_err("not a valid calib_type\n");
		rc = -EINVAL;
		goto fail_unlock;
	}

	amux_prop.decimation =
			vadc->adc->adc_channels[dt_index].adc_decimation;
	amux_prop.calib_type = vadc->adc->adc_channels[dt_index].calib_type;
	amux_prop.cal_val = vadc->adc->adc_channels[dt_index].cal_val;
	amux_prop.fast_avg_setup =
			vadc->adc->adc_channels[dt_index].fast_avg_setup;
	amux_prop.amux_channel = channel;
	amux_prop.hw_settle_time =
			vadc->adc->adc_channels[dt_index].hw_settle_time;

	rc = qpnp_vadc_hc_configure(vadc, &amux_prop);
	if (rc < 0) {
		pr_err("Configuring VADC channel failed with %d\n", rc);
		goto fail_unlock;
	}

	if (vadc->vadc_poll_eoc) {
		rc = qpnp_vadc_hc_check_conversion_status(vadc);
		if (rc < 0) {
			pr_err("polling mode conversion failed\n");
			goto fail_unlock;
		}
	} else {
		rc = wait_for_completion_timeout(
					&vadc->adc->adc_rslt_completion,
					QPNP_ADC_COMPLETION_TIMEOUT);
		if (!rc) {
			rc = qpnp_vadc_hc_check_conversion_status(vadc);
			if (rc < 0) {
				pr_err("interrupt mode conversion failed\n");
				goto fail_unlock;
			}
			pr_debug("End of conversion status set\n");
		}
	}

	rc = qpnp_vadc_hc_read_data(vadc, &result->adc_code);
	if (rc) {
		pr_err("qpnp vadc read adc code failed with %d\n", rc);
		goto fail_unlock;
	}

	amux_prescaling =
		vadc->adc->adc_channels[dt_index].chan_path_prescaling;

	if (amux_prescaling >= PATH_SCALING_NONE) {
		rc = -EINVAL;
		goto fail_unlock;
	}

	vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
		qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
	vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
		 qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;

	scale_type = vadc->adc->adc_channels[dt_index].adc_scale_fn;
	if (scale_type >= SCALE_NONE) {
		rc = -EBADF;
		goto fail_unlock;
	}

	/* Note: Scaling functions for VADC_HC do not need offset/gain */
	vadc_scale_fn[scale_type].chan(vadc, result->adc_code,
		vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);

	pr_debug("channel=0x%x, adc_code=0x%x adc_result=%lld\n",
			channel, result->adc_code, result->physical);

fail_unlock:
	mutex_unlock(&vadc->adc->adc_lock);

	return rc;
}
EXPORT_SYMBOL(qpnp_vadc_hc_read);

static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc,
			struct qpnp_adc_amux_properties *chan_prop)
{
@@ -554,17 +828,6 @@ static int32_t qpnp_vadc_read_status(struct qpnp_vadc_chip *vadc, int mode_sel)
	return 0;
}

static int qpnp_vadc_is_valid(struct qpnp_vadc_chip *vadc)
{
	struct qpnp_vadc_chip *vadc_chip = NULL;

	list_for_each_entry(vadc_chip, &qpnp_vadc_device_list, list)
		if (vadc == vadc_chip)
			return 0;

	return -EINVAL;
}

static void qpnp_vadc_work(struct work_struct *work)
{
	struct qpnp_vadc_chip *vadc = container_of(work,
@@ -1212,21 +1475,37 @@ int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
	int rc, count = 0, calib_read = 0;
	u8 status1 = 0;

	if (vadc->vadc_hc) {
		if (calib_type == ADC_HC_ABS_CAL)
			conv.amux_channel = VADC_CALIB_VREF_1P25;
		else if (calib_type == CALIB_RATIOMETRIC)
			conv.amux_channel = VADC_CALIB_VREF;
	} else {
		if (calib_type == CALIB_ABSOLUTE)
			conv.amux_channel = REF_125V;
		else if (calib_type == CALIB_RATIOMETRIC)
			conv.amux_channel = VDD_VADC;
	}

	conv.decimation = DECIMATION_TYPE2;
	conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
	conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
	conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
	conv.cal_val = calib_type;

	if (vadc->vadc_hc) {
		rc = qpnp_vadc_hc_configure(vadc, &conv);
		if (rc) {
			pr_err("qpnp_vadc configure failed with %d\n", rc);
			goto calib_fail;
		}
	} else {
		rc = qpnp_vadc_configure(vadc, &conv);
		if (rc) {
			pr_err("qpnp_vadc configure failed with %d\n", rc);
			goto calib_fail;
		}
	}

	while (status1 != QPNP_VADC_STATUS1_EOC) {
		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
@@ -1242,11 +1521,20 @@ int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
		}
	}

	if (vadc->vadc_hc) {
		rc = qpnp_vadc_hc_read_data(vadc, &calib_read);
		if (rc) {
			pr_err("qpnp vadc read adc code failed with %d\n", rc);
			goto calib_fail;
		}
	} else {
		rc = qpnp_vadc_read_conversion_result(vadc, &calib_read);
		if (rc) {
			pr_err("qpnp adc read adc failed with %d\n", rc);
			goto calib_fail;
		}
	}

	*calib_data = calib_read;
calib_fail:
	return rc;
@@ -1262,22 +1550,35 @@ int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
	u8 status1 = 0;
	uint32_t ref_channel_sel = 0;

	if (vadc->vadc_hc) {
		conv.amux_channel = VADC_VREF_GND;
	} else {
		if (calib_type == CALIB_ABSOLUTE) {
			qpnp_vadc_625mv_channel_sel(vadc, &ref_channel_sel);
			conv.amux_channel = ref_channel_sel;
		} else if (calib_type == CALIB_RATIOMETRIC)
			conv.amux_channel = GND_REF;
	}

	conv.decimation = DECIMATION_TYPE2;
	conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
	conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
	conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
	conv.cal_val = calib_type;

	if (vadc->vadc_hc) {
		rc = qpnp_vadc_hc_configure(vadc, &conv);
		if (rc) {
			pr_err("qpnp_vadc configure failed with %d\n", rc);
			goto calib_fail;
		}
	} else {
		rc = qpnp_vadc_configure(vadc, &conv);
		if (rc) {
			pr_err("qpnp_vadc configure failed with %d\n", rc);
			goto calib_fail;
		}
	}

	while (status1 != QPNP_VADC_STATUS1_EOC) {
		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
@@ -1293,11 +1594,19 @@ int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
		}
	}

	if (vadc->vadc_hc) {
		rc = qpnp_vadc_hc_read_data(vadc, &calib_read);
		if (rc) {
			pr_err("qpnp vadc read adc code failed with %d\n", rc);
			goto calib_fail;
		}
	} else {
		rc = qpnp_vadc_read_conversion_result(vadc, &calib_read);
		if (rc) {
			pr_err("qpnp adc read adc failed with %d\n", rc);
			goto calib_fail;
		}
	}
	*calib_data = calib_read;
calib_fail:
	return rc;
@@ -1306,35 +1615,45 @@ calib_fail:
static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
{
	int rc, calib_read_1 = 0, calib_read_2 = 0;
	enum qpnp_adc_calib_type calib_type;

	if (vadc->vadc_hc)
		calib_type = ADC_HC_ABS_CAL;
	else
		calib_type = CALIB_ABSOLUTE;

	rc = qpnp_vadc_calib_vref(vadc, CALIB_ABSOLUTE, &calib_read_1);
	rc = qpnp_vadc_calib_vref(vadc, calib_type, &calib_read_1);
	if (rc) {
		pr_err("qpnp adc absolute vref calib failed with %d\n", rc);
		goto calib_fail;
	}
	rc = qpnp_vadc_calib_gnd(vadc, CALIB_ABSOLUTE, &calib_read_2);
	rc = qpnp_vadc_calib_gnd(vadc, calib_type, &calib_read_2);
	if (rc) {
		pr_err("qpnp adc absolute gnd calib failed with %d\n", rc);
		goto calib_fail;
	}
	pr_debug("absolute reference raw: 625mV:0x%x 1.25V:0x%x\n",
				calib_read_2, calib_read_1);
	pr_debug("absolute reference raw: 1.25V:0x%x, 625mV/GND:0x%x\n",
				calib_read_1, calib_read_2);

	if (calib_read_1 == calib_read_2) {
		pr_err("absolute reference raw: 625mV:0x%x 1.25V:0x%x\n",
		pr_err("absolute reference raw: 1.25V:0x%x625mV:0x%x\n",
			calib_read_2, calib_read_1);
		rc = -EINVAL;
		goto calib_fail;
	}

	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy =
	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy =
				(calib_read_1 - calib_read_2);

	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dx
	if (calib_type == CALIB_ABSOLUTE)
		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx
						= QPNP_ADC_625_UV;
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_vref =
	else if (calib_type == ADC_HC_ABS_CAL)
		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx
						= QPNP_ADC_1P25_UV;
	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_vref =
					calib_read_1;
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_gnd =
	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd =
					calib_read_2;

	calib_read_1 = 0;
@@ -1384,12 +1703,21 @@ int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc,
		return rc;

	if (!vadc->vadc_init_calib) {
		if (vadc->vadc_hc) {
			rc = qpnp_vadc_hc_read(vadc, VADC_CALIB_VREF_1P25,
								&result);
			if (rc) {
				pr_debug("vadc read failed with rc = %d\n", rc);
				return rc;
			}
		} else {
			rc = qpnp_vadc_read(vadc, REF_125V, &result);
			if (rc) {
				pr_debug("vadc read failed with rc = %d\n", rc);
				return rc;
			}
		}
	}

	switch (calib_type) {
	case CALIB_RATIOMETRIC:
@@ -1402,13 +1730,14 @@ int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc,
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_gnd;
	break;
	case CALIB_ABSOLUTE:
	case ADC_HC_ABS_CAL:
	param->dy =
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy;
	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy;
	param->dx =
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dx;
	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx;
	param->adc_vref = vadc->adc->adc_prop->adc_vdd_reference;
	param->adc_gnd =
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_gnd;
	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd;
	break;
	default:
		rc = -EINVAL;
@@ -1441,7 +1770,8 @@ static int32_t qpnp_vadc_wait_for_req_sts_check(struct qpnp_vadc_chip *vadc)
	while ((status1 & QPNP_VADC_STATUS1_REQ_STS) && (count < QPNP_RETRY)) {
		/* Wait time is based on the optimum sampling rate
		 * and adding enough time buffer to account for ADC conversions
		 * occuring on different peripheral banks */
		 * occurring on different peripheral banks
		 */
		usleep_range(QPNP_MIN_TIME, QPNP_MAX_TIME);
		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
		if (rc < 0) {
@@ -2182,256 +2512,6 @@ int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip)
}
EXPORT_SYMBOL(qpnp_vadc_end_channel_monitor);

static int qpnp_vadc_hc_check_conversion_status(struct qpnp_vadc_chip *vadc)
{
	int rc = 0, count = 0;
	u8 status1 = 0;

	while (status1 != QPNP_VADC_STATUS1_EOC) {
		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
		if (rc < 0)
			return rc;
		status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
		if (status1 == QPNP_VADC_STATUS1_EOC)
			break;
		usleep_range(QPNP_VADC_HC1_CONV_TIME_MIN_US,
				QPNP_VADC_HC1_CONV_TIME_MAX_US);
		count++;
		if (count > QPNP_VADC_HC1_ERR_COUNT) {
			pr_err("retry error exceeded\n");
			rc = qpnp_vadc_status_debug(vadc);
			if (rc < 0)
				pr_err("VADC disable failed\n");
			return -EINVAL;
		}
	}

	return rc;
}

static int qpnp_vadc_hc_read_data(struct qpnp_vadc_chip *vadc, int *data)
{
	int rc = 0;
	u8 buf = 0, rslt_lsb = 0, rslt_msb = 0;

	/* Set hold bit */
	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
	if (rc) {
		pr_err("debug register dump failed\n");
		return rc;
	}
	buf |= QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
	if (rc) {
		pr_err("debug register dump failed\n");
		return rc;
	}

	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA0, &rslt_lsb, 1);
	if (rc < 0) {
		pr_err("qpnp adc result read failed for data0\n");
		return rc;
	}

	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA1, &rslt_msb, 1);
	if (rc < 0) {
		pr_err("qpnp adc result read failed for data1\n");
		return rc;
	}

	*data = (rslt_msb << 8) | rslt_lsb;

	if (*data == QPNP_VADC_HC1_DATA_CHECK_USR) {
		pr_err("Invalid data :0x%x\n", *data);
		return -EINVAL;
	}

	rc = qpnp_vadc_enable(vadc, false);
	if (rc) {
		pr_err("VADC disable failed\n");
		return rc;
	}

	/* De-assert hold bit */
	buf &= ~QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
	if (rc)
		pr_err("de-asserting hold bit failed\n");

	return rc;
}

static void qpnp_vadc_hc_update_adc_dig_param(struct qpnp_vadc_chip *vadc,
				struct qpnp_adc_amux *amux_prop, u8 *data)
{
	/* Update CAL value */
	*data &= ~QPNP_VADC_HC1_CAL_VAL;
	*data |= (amux_prop->cal_val << QPNP_VADC_HC1_CAL_VAL_SHIFT);

	/* Update CAL select */
	*data &= ~QPNP_VADC_HC1_CAL_SEL_MASK;
	*data |= (amux_prop->calib_type << QPNP_VADC_HC1_CAL_SEL_SHIFT);

	/* Update Decimation ratio select */
	*data &= ~QPNP_VADC_HC1_DEC_RATIO_SEL;
	*data |= (amux_prop->adc_decimation << QPNP_VADC_HC1_DEC_RATIO_SHIFT);

	pr_debug("VADC_DIG_PARAM value:0x%x\n", *data);
}

static int qpnp_vadc_hc_configure(struct qpnp_vadc_chip *vadc,
				struct qpnp_adc_amux *amux_prop)
{
	int rc = 0;
	u8 buf[6];

	/* Read registers 0x42 through 0x46 */
	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
	if (rc < 0) {
		pr_err("qpnp adc configure block read failed\n");
		return rc;
	}

	/* ADC Digital param selection */
	qpnp_vadc_hc_update_adc_dig_param(vadc, amux_prop, &buf[0]);

	/* Update fast average sample value */
	buf[1] &= (u8) ~QPNP_VADC_HC1_FAST_AVG_SAMPLES_MASK;
	buf[1] |= amux_prop->fast_avg_setup;

	/* Select ADC channel */
	buf[2] = amux_prop->channel_num;

	/* Select hw settle delay for the channel */
	buf[3] &= (u8) ~QPNP_VADC_HC1_DELAY_CTL_MASK;
	buf[3] |= amux_prop->hw_settle_time;

	/* Select ADC enable */
	buf[4] |= QPNP_VADC_HC1_ADC_EN;

	/* Select CONV request */
	buf[5] |= QPNP_VADC_HC1_CONV_REQ_START;

	if (!vadc->vadc_poll_eoc)
		reinit_completion(&vadc->adc->adc_rslt_completion);

	pr_debug("dig:0x%x, fast_avg:0x%x, channel:0x%x, hw_settle:0x%x\n",
		buf[0], buf[1], buf[2], buf[3]);

	/* Block register write from 0x42 through 0x46 */
	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
	if (rc < 0) {
		pr_err("qpnp adc block register configure failed\n");
		return rc;
	}

	return 0;
}

int32_t qpnp_vadc_hc_read(struct qpnp_vadc_chip *vadc,
				enum qpnp_vadc_channels channel,
				struct qpnp_vadc_result *result)
{
	int rc = 0, scale_type, amux_prescaling, dt_index = 0, calib_type = 0;
	struct qpnp_adc_amux amux_prop;

	if (qpnp_vadc_is_valid(vadc))
		return -EPROBE_DEFER;

	mutex_lock(&vadc->adc->adc_lock);

	while ((vadc->adc->adc_channels[dt_index].channel_num
		!= channel) && (dt_index < vadc->max_channels_available))
		dt_index++;

	if (dt_index >= vadc->max_channels_available) {
		pr_err("not a valid VADC channel:%d\n", channel);
		rc = -EINVAL;
		goto fail_unlock;
	}

	calib_type = vadc->adc->adc_channels[dt_index].calib_type;
	if (calib_type >= ADC_HC_CAL_SEL_NONE) {
		pr_err("not a valid calib_type\n");
		rc = -EINVAL;
		goto fail_unlock;
	}

	amux_prop.adc_decimation =
			vadc->adc->adc_channels[dt_index].adc_decimation;
	amux_prop.calib_type = vadc->adc->adc_channels[dt_index].calib_type;
	amux_prop.cal_val = vadc->adc->adc_channels[dt_index].cal_val;
	amux_prop.fast_avg_setup =
			vadc->adc->adc_channels[dt_index].fast_avg_setup;
	amux_prop.channel_num = channel;
	amux_prop.hw_settle_time =
			vadc->adc->adc_channels[dt_index].hw_settle_time;

	rc = qpnp_vadc_hc_configure(vadc, &amux_prop);
	if (rc < 0) {
		pr_err("Configuring VADC channel failed with %d\n", rc);
		goto fail_unlock;
	}

	if (vadc->vadc_poll_eoc) {
		rc = qpnp_vadc_hc_check_conversion_status(vadc);
		if (rc < 0) {
			pr_err("polling mode conversion failed\n");
			goto fail_unlock;
		}
	} else {
		rc = wait_for_completion_timeout(
					&vadc->adc->adc_rslt_completion,
					QPNP_ADC_COMPLETION_TIMEOUT);
		if (!rc) {
			rc = qpnp_vadc_hc_check_conversion_status(vadc);
			if (rc < 0) {
				pr_err("interrupt mode conversion failed\n");
				goto fail_unlock;
			}
			pr_debug("End of conversion status set\n");
		}
	}

	rc = qpnp_vadc_hc_read_data(vadc, &result->adc_code);
	if (rc) {
		pr_err("qpnp vadc read adc code failed with %d\n", rc);
		goto fail_unlock;
	}

	amux_prescaling =
		vadc->adc->adc_channels[dt_index].chan_path_prescaling;

	if (amux_prescaling >= PATH_SCALING_NONE) {
		rc = -EINVAL;
		goto fail_unlock;
	}

	vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
		qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
	vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
		 qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;

	scale_type = vadc->adc->adc_channels[dt_index].adc_scale_fn;
	if (scale_type >= SCALE_NONE) {
		rc = -EBADF;
		goto fail_unlock;
	}

	/* Note: Scaling functions for VADC_HC do not need offset/gain */
	vadc_scale_fn[scale_type].chan(vadc, result->adc_code,
		vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);

	pr_debug("channel=0x%x, adc_code=0x%x adc_result=%lld\n",
			channel, result->adc_code, result->physical);

fail_unlock:
	mutex_unlock(&vadc->adc->adc_lock);

	return rc;
}
EXPORT_SYMBOL(qpnp_vadc_hc_read);

static ssize_t qpnp_adc_show(struct device *dev,
			struct device_attribute *devattr, char *buf)
{
+8 −1
Original line number Diff line number Diff line
@@ -242,6 +242,7 @@ enum qpnp_iadc_channels {
#define QPNP_ADC_HWMON_NAME_LENGTH				64
#define QPNP_MAX_PROP_NAME_LEN					32
#define QPNP_THERMALNODE_NAME_LENGTH                            25
#define QPNP_ADC_1P25_UV					1250000

/* Structure device for qpnp vadc */
struct qpnp_vadc_chip;
@@ -1069,7 +1070,7 @@ struct qpnp_vadc_chan_properties {
	enum qpnp_adc_tm_channel_select		tm_channel_select;
	enum qpnp_state_request			state_request;
	enum qpnp_adc_calib_type		calib_type;
	struct qpnp_vadc_linear_graph	adc_graph[2];
	struct qpnp_vadc_linear_graph	adc_graph[ADC_HC_CAL_SEL_NONE];
};

/**
@@ -1249,6 +1250,10 @@ struct qpnp_adc_drv {
 * @fast_avg_setup - Ability to provide single result from the ADC
 *			that is an average of multiple measurements.
 * @trigger_channel - HW trigger channel for conversion sequencer.
 * @calib_type - Used to store the calibration type for the channel
 *		 absolute/ratiometric.
 * @cal_val - Used to determine if fresh calibration value or timer
 *	      updated calibration value is to be used.
 * @chan_prop - Represent the channel properties of the ADC.
 */
struct qpnp_adc_amux_properties {
@@ -1258,6 +1263,8 @@ struct qpnp_adc_amux_properties {
	uint32_t				hw_settle_time;
	uint32_t				fast_avg_setup;
	enum qpnp_vadc_trigger			trigger_channel;
	enum qpnp_adc_calib_type		calib_type;
	enum qpnp_adc_cal_val			cal_val;
	struct qpnp_vadc_chan_properties	chan_prop[0];
};