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

Commit 02b3f658 authored by Dipen Parmar's avatar Dipen Parmar
Browse files

hwmon: qpnp-adc: Fix incorrect ADC reads during MPP current sink



MPP2 and MPP4 current sink configuration results in
incorrect ADC readings due to common ground problem
on PM8916.

Fix the issue by using recalibration method if
calibration value found deviated from permitted value.

Change-Id: I1a4065478c00a9819673e1396651d25cb88f527f
Signed-off-by: default avatarDipen Parmar <dipenp@codeaurora.org>
parent 2d3171d7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ Optional properties:
-qcom,vadc-meas-int-mode : Enable VADC_USR to handle requests to perform recurring measurements
			   for any one supported channel along with supporting single conversion
			   requests.
- qcom,vadc-recalib-check: Add this property to check if recalibration required due to inaccuracy.

Client required property:
- qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ Optional properties:
		0 : Select Timer 1 for a measurement polling interval of 3.9 milliseconds.
		1 : Select Timer 2 for a measurement polling interval of 1 second.
		2 : Select Timer 3 for a measurement polling interval of 4 seconds.
- qcom,adc-tm-recalib-check: Add this property to check if recalibration required due to inaccuracy.

Client required property:
- qcom,<consumer name>-adc_tm : The phandle to the corresponding adc_tm device.
+188 −113
Original line number Diff line number Diff line
@@ -96,8 +96,8 @@
#define QPNP_VADC_DATA0						0x60
#define QPNP_VADC_DATA1						0x61
#define QPNP_VADC_CONV_TIMEOUT_ERR				2
#define QPNP_VADC_CONV_TIME_MIN					2000
#define QPNP_VADC_CONV_TIME_MAX					2100
#define QPNP_VADC_CONV_TIME_MIN					1000
#define QPNP_VADC_CONV_TIME_MAX					1100
#define QPNP_ADC_COMPLETION_TIMEOUT				HZ
#define QPNP_VADC_ERR_COUNT					20
#define QPNP_OP_MODE_SHIFT					3
@@ -107,6 +107,9 @@
#define QPNP_MIN_TIME						2000
#define QPNP_MAX_TIME						2000
#define QPNP_RETRY						100
#define QPNP_VADC_ABSOLUTE_RECALIB_OFFSET			8
#define QPNP_VADC_RATIOMETRIC_RECALIB_OFFSET			12
#define QPNP_VADC_RECALIB_MAXCNT				10

struct qpnp_vadc_mode_state {
	bool				meas_int_mode;
@@ -128,6 +131,7 @@ struct qpnp_vadc_chip {
	u8				id;
	struct work_struct		trigger_completion_work;
	bool				vadc_poll_eoc;
	bool				vadc_recalib_check;
	u8				revision_ana_minor;
	u8				revision_dig_major;
	struct workqueue_struct		*high_thr_wq;
@@ -198,7 +202,8 @@ static int32_t qpnp_vadc_warm_rst_configure(struct qpnp_vadc_chip *vadc)
	int rc = 0;
	u8 data = 0;

	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ACCESS, QPNP_VADC_ACCESS_DATA);
	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ACCESS,
						QPNP_VADC_ACCESS_DATA);
	if (rc < 0) {
		pr_err("VADC write access failed\n");
		return rc;
@@ -210,7 +215,8 @@ static int32_t qpnp_vadc_warm_rst_configure(struct qpnp_vadc_chip *vadc)
		return rc;
	}

	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ACCESS, QPNP_VADC_ACCESS_DATA);
	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ACCESS,
						QPNP_VADC_ACCESS_DATA);
	if (rc < 0) {
		pr_err("VADC write access failed\n");
		return rc;
@@ -1055,14 +1061,19 @@ static void qpnp_vadc_625mv_channel_sel(struct qpnp_vadc_chip *vadc,
	}
}

static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
					enum qpnp_adc_calib_type calib_type,
					int *calib_data)
{
	struct qpnp_adc_amux_properties conv;
	int rc, calib_read_1, calib_read_2, count = 0;
	int rc, count = 0, calib_read = 0;
	u8 status1 = 0;
	uint32_t ref_channel_sel = 0;

	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;
@@ -1088,26 +1099,43 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
		}
	}

	rc = qpnp_vadc_read_conversion_result(vadc, &calib_read_1);
	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;
}


int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
					enum qpnp_adc_calib_type calib_type,
					int *calib_data)
{
	struct qpnp_adc_amux_properties conv;
	int rc, count = 0, calib_read = 0;
	u8 status1 = 0;
	uint32_t ref_channel_sel = 0;

	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;

	rc = qpnp_vadc_configure(vadc, &conv);
	if (rc) {
		pr_err("qpnp adc configure failed with %d\n", rc);
		pr_err("qpnp_vadc configure failed with %d\n", rc);
		goto calib_fail;
	}

	status1 = 0;
	count = 0;
	while (status1 != QPNP_VADC_STATUS1_EOC) {
		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1);
		if (rc < 0)
@@ -1122,12 +1150,30 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
		}
	}

	rc = qpnp_vadc_read_conversion_result(vadc, &calib_read_2);
	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;
}

static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
{
	int rc, calib_read_1 = 0, calib_read_2 = 0;

	rc = qpnp_vadc_calib_vref(vadc, CALIB_ABSOLUTE, &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);
	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);

@@ -1147,73 +1193,19 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
					calib_read_1;
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_gnd =
					calib_read_2;
	/* Ratiometric Calibration */
	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;
	rc = qpnp_vadc_configure(vadc, &conv);
	if (rc) {
		pr_err("qpnp adc configure failed with %d\n", rc);
		goto calib_fail;
	}

	status1 = 0;
	count = 0;
	while (status1 != QPNP_VADC_STATUS1_EOC) {
		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1);
		if (rc < 0)
			return rc;
		status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
		usleep_range(QPNP_VADC_CONV_TIME_MIN,
					QPNP_VADC_CONV_TIME_MAX);
		count++;
		if (count > QPNP_VADC_ERR_COUNT) {
			rc = -ENODEV;
			goto calib_fail;
		}
	}

	rc = qpnp_vadc_read_conversion_result(vadc, &calib_read_1);
	if (rc) {
		pr_err("qpnp adc read adc failed with %d\n", rc);
		goto calib_fail;
	}

	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;
	rc = qpnp_vadc_configure(vadc, &conv);
	calib_read_1 = 0;
	calib_read_2 = 0;
	rc = qpnp_vadc_calib_vref(vadc, CALIB_RATIOMETRIC, &calib_read_1);
	if (rc) {
		pr_err("qpnp adc configure failed with %d\n", rc);
		pr_err("qpnp adc ratiometric vref calib failed with %d\n", rc);
		goto calib_fail;
	}

	status1 = 0;
	count = 0;
	while (status1 != QPNP_VADC_STATUS1_EOC) {
		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1);
		if (rc < 0)
			return rc;
		status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
		usleep_range(QPNP_VADC_CONV_TIME_MIN,
					QPNP_VADC_CONV_TIME_MAX);
		count++;
		if (count > QPNP_VADC_ERR_COUNT) {
			rc = -ENODEV;
			goto calib_fail;
		}
	}

	rc = qpnp_vadc_read_conversion_result(vadc, &calib_read_2);
	rc = qpnp_vadc_calib_gnd(vadc, CALIB_RATIOMETRIC, &calib_read_2);
	if (rc) {
		pr_err("qpnp adc read adc failed with %d\n", rc);
		pr_err("qpnp adc ratiometric gnd calib failed with %d\n", rc);
		goto calib_fail;
	}

	pr_debug("ratiometric reference raw: VDD:0x%x GND:0x%x\n",
				calib_read_1, calib_read_2);

@@ -1228,10 +1220,10 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
					(calib_read_1 - calib_read_2);
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dx =
					vadc->adc->adc_prop->adc_vdd_reference;
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_vref =
					calib_read_1;
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_gnd =
					calib_read_2;
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_vref
					= calib_read_1;
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_gnd
					= calib_read_2;

calib_fail:
	return rc;
@@ -1246,6 +1238,7 @@ int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc,
	rc = qpnp_vadc_is_valid(vadc);
	if (rc < 0)
		return rc;
	mutex_lock(&vadc->adc->adc_lock);

	switch (calib_type) {
	case CALIB_RATIOMETRIC:
@@ -1267,10 +1260,11 @@ int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc,
	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_gnd;
	break;
	default:
		return -EINVAL;
		rc = -EINVAL;
	}

	return 0;
	mutex_unlock(&vadc->adc->adc_lock);
	return rc;
}
EXPORT_SYMBOL(qpnp_get_vadc_gain_and_offset);

@@ -1415,8 +1409,10 @@ int32_t qpnp_vadc_conv_seq_request(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;
	uint32_t ref_channel, count = 0;
	int rc = 0, scale_type, amux_prescaling, dt_index = 0, calib_type = 0;
	uint32_t ref_channel, count = 0, local_idx = 0;
	int32_t vref_calib = 0, gnd_calib = 0, new_vref_calib = 0, offset = 0;
	int32_t calib_offset = 0;
	u8 status1 = 0;

	if (qpnp_vadc_is_valid(vadc))
@@ -1427,19 +1423,6 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc,
	if (vadc->state_copy->vadc_meas_int_enable)
		qpnp_vadc_manage_meas_int_requests(vadc);

	if (!vadc->vadc_init_calib) {
		rc = qpnp_vadc_version_check(vadc);
		if (rc)
			goto fail_unlock;

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

	if (channel == REF_625MV) {
		qpnp_vadc_625mv_channel_sel(vadc, &ref_channel);
		channel = ref_channel;
@@ -1457,6 +1440,30 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc,
		goto fail_unlock;
	}

	calib_type = vadc->adc->adc_channels[dt_index].calib_type;
	calib_offset = (calib_type == CALIB_ABSOLUTE) ?
		QPNP_VADC_ABSOLUTE_RECALIB_OFFSET :
		QPNP_VADC_RATIOMETRIC_RECALIB_OFFSET;
	rc = qpnp_vadc_version_check(vadc);
	if (rc)
		goto fail_unlock;
	if (vadc->vadc_recalib_check) {
		rc = qpnp_vadc_calib_vref(vadc, calib_type, &vref_calib);
		if (rc) {
			pr_err("Calibration failed\n");
			goto fail_unlock;
		}
	} else 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;
		}
	}

recalibrate:
	vadc->adc->amux_prop->decimation =
			vadc->adc->adc_channels[dt_index].adc_decimation;
	vadc->adc->amux_prop->hw_settle_time =
@@ -1490,6 +1497,8 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc,
			if (rc < 0)
				goto fail_unlock;
			status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
			if (status1 == QPNP_VADC_STATUS1_EOC)
				break;
			usleep_range(QPNP_VADC_CONV_TIME_MIN,
					QPNP_VADC_CONV_TIME_MAX);
			count++;
@@ -1537,6 +1546,64 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc,
		goto fail_unlock;
	}

	if (vadc->vadc_recalib_check) {
		rc = qpnp_vadc_calib_gnd(vadc, calib_type, &gnd_calib);
		if (rc) {
			pr_err("Calibration failed\n");
			goto fail_unlock;
		}
		rc = qpnp_vadc_calib_vref(vadc, calib_type, &new_vref_calib);
		if (rc < 0) {
			pr_err("qpnp vadc calib read failed with %d\n", rc);
			goto fail_unlock;
		}

		if (local_idx >= QPNP_VADC_RECALIB_MAXCNT) {
			pr_err("invalid recalib count=%d\n", local_idx);
			rc = -EINVAL;
			goto fail_unlock;
		}
		pr_debug(
			"chan=%d, calib=%s, vref_calib=0x%x, gnd_calib=0x%x, new_vref_calib=0x%x\n",
			channel,
			((calib_type == CALIB_ABSOLUTE) ?
			"ABSOLUTE" : "RATIOMETRIC"),
			vref_calib, gnd_calib, new_vref_calib);

		offset = (new_vref_calib - vref_calib);
		if (offset < 0)
			offset = -offset;
		if (offset <= calib_offset) {
			pr_debug(
				"qpnp vadc recalibration not required,offset:%d\n",
								offset);
			local_idx = 0;
		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy =
						(vref_calib - gnd_calib);
		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx =
			(calib_type == CALIB_ABSOLUTE) ? QPNP_ADC_625_UV :
					vadc->adc->adc_prop->adc_vdd_reference;
		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_vref
								= vref_calib;
		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd
								= gnd_calib;
		} else {
			offset = 0;
			vref_calib = new_vref_calib;
			local_idx = local_idx + 1;
			if (local_idx >= QPNP_VADC_RECALIB_MAXCNT) {
				pr_err(
				"qpnp_vadc recalibration failed, count=%d",
								local_idx);
			} else {
				pr_debug(
				"qpnp vadc recalibration requested,offset:%d\n",
								offset);
				goto recalibrate;
			}
		}
	}

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

@@ -1561,6 +1628,9 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc,
	vadc_scale_fn[scale_type].chan(vadc, result->adc_code,
		vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);

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

fail_unlock:
	if (vadc->state_copy->vadc_meas_int_enable)
		qpnp_vadc_manage_meas_int_requests(vadc);
@@ -1618,25 +1688,13 @@ static void qpnp_vadc_unlock(struct qpnp_vadc_chip *vadc)
int32_t qpnp_vadc_iadc_sync_request(struct qpnp_vadc_chip *vadc,
				enum qpnp_vadc_channels channel)
{
	int rc = 0, dt_index = 0;
	int rc = 0, dt_index = 0, calib_type = 0;

	if (qpnp_vadc_is_valid(vadc))
		return -EPROBE_DEFER;

	qpnp_vadc_lock(vadc);

	if (!vadc->vadc_init_calib) {
		rc = qpnp_vadc_version_check(vadc);
		if (rc)
			goto fail;

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

	vadc->adc->amux_prop->amux_channel = channel;

@@ -1650,6 +1708,20 @@ int32_t qpnp_vadc_iadc_sync_request(struct qpnp_vadc_chip *vadc,
		goto fail;
	}

	calib_type = vadc->adc->adc_channels[dt_index].calib_type;
	if (!vadc->vadc_init_calib) {
		rc = qpnp_vadc_version_check(vadc);
		if (rc)
			goto fail;

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

	vadc->adc->amux_prop->decimation =
			vadc->adc->adc_channels[dt_index].adc_decimation;
	vadc->adc->amux_prop->hw_settle_time =
@@ -2032,6 +2104,9 @@ static int qpnp_vadc_probe(struct spmi_device *spmi)

	INIT_WORK(&vadc->trigger_completion_work, qpnp_vadc_work);

	vadc->vadc_recalib_check = of_property_read_bool(node,
						"qcom,vadc-recalib-check");

	vadc->vadc_poll_eoc = of_property_read_bool(node,
						"qcom,vadc-poll-eoc");
	if (!vadc->vadc_poll_eoc) {
+213 −2
Original line number Diff line number Diff line
@@ -207,6 +207,7 @@ struct qpnp_adc_tm_chip {
	struct qpnp_adc_drv		*adc;
	struct list_head		list;
	bool				adc_tm_initialized;
	bool				adc_tm_recalib_check;
	int				max_channels_available;
	atomic_t			wq_cnt;
	struct qpnp_vadc_chip		*vadc_dev;
@@ -679,6 +680,62 @@ static int32_t qpnp_adc_tm_reg_update(struct qpnp_adc_tm_chip *chip,
	return rc;
}

static int32_t qpnp_adc_tm_read_thr_value(struct qpnp_adc_tm_chip *chip,
			uint32_t btm_chan)
{
	int rc = 0;
	u8 data_lsb = 0, data_msb = 0;
	uint32_t btm_chan_idx = 0;
	int32_t low_thr = 0, high_thr = 0;

	rc = qpnp_adc_tm_get_btm_idx(btm_chan, &btm_chan_idx);
	if (rc < 0) {
		pr_err("Invalid btm channel idx\n");
		return rc;
	}

	rc = qpnp_adc_tm_read_reg(chip,
			adc_tm_data[btm_chan_idx].low_thr_lsb_addr,
			&data_lsb);
	if (rc < 0) {
		pr_err("low threshold lsb setting failed\n");
		return rc;
	}

	rc = qpnp_adc_tm_read_reg(chip,
		adc_tm_data[btm_chan_idx].low_thr_msb_addr,
		&data_msb);
	if (rc < 0) {
		pr_err("low threshold msb setting failed\n");
		return rc;
	}

	low_thr = (data_msb << 8) | data_lsb;

	rc = qpnp_adc_tm_read_reg(chip,
		adc_tm_data[btm_chan_idx].high_thr_lsb_addr,
		&data_lsb);
	if (rc < 0) {
		pr_err("high threshold lsb setting failed\n");
		return rc;
	}

	rc = qpnp_adc_tm_read_reg(chip,
		adc_tm_data[btm_chan_idx].high_thr_msb_addr,
		&data_msb);
	if (rc < 0) {
		pr_err("high threshold msb setting failed\n");
		return rc;
	}

	high_thr = (data_msb << 8) | data_lsb;

	pr_debug("configured thresholds high:0x%x and low:0x%x\n",
		high_thr, low_thr);

	return rc;
}

static int32_t qpnp_adc_tm_thr_update(struct qpnp_adc_tm_chip *chip,
			uint32_t btm_chan, int32_t high_thr, int32_t low_thr)
{
@@ -1410,10 +1467,148 @@ static int qpnp_adc_tm_activate_trip_type(struct thermal_zone_device *thermal,
	return rc;
}

static int qpnp_adc_tm_recalib_request_check(struct qpnp_adc_tm_chip *chip,
			int sensor_num, u8 status_high, u8 *notify_check)
{
	int rc = 0;
	u8 sensor_mask = 0, mode_ctl = 0;
	int32_t old_thr = 0, new_thr = 0;
	uint32_t channel, btm_chan_num, scale_type;
	struct qpnp_vadc_result result;
	struct qpnp_adc_thr_client_info *client_info = NULL;
	struct list_head *thr_list;
	bool status = false;

	if (!chip->adc_tm_recalib_check) {
		*notify_check = 1;
		return rc;
	}

	list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
		client_info = list_entry(thr_list,
				struct qpnp_adc_thr_client_info, list);
		channel = client_info->btm_param->channel;
		btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
		sensor_mask = 1 << sensor_num;

		rc = qpnp_vadc_read(chip->vadc_dev, channel, &result);
		if (rc < 0) {
			pr_err("failure to read vadc channel=%d\n",
					client_info->btm_param->channel);
			goto fail;
		}
		new_thr = result.physical;

		if (status_high)
			old_thr = client_info->btm_param->high_thr;
		else
			old_thr = client_info->btm_param->low_thr;

		if (new_thr > old_thr)
			status = (status_high) ? true : false;
		else
			status = (status_high) ? false : true;

		pr_debug(
			"recalib:sen=%d, new_thr=%d, new_thr_adc_code=0x%x, old_thr=%d status=%d valid_status=%d\n",
			sensor_num, new_thr, result.adc_code,
			old_thr, status_high, status);

		rc = qpnp_adc_tm_read_thr_value(chip, btm_chan_num);
		if (rc < 0) {
			pr_err("adc-tm thresholds read failed\n");
			goto fail;
		}

		if (status) {
			*notify_check = 1;
			pr_debug("Client can be notify\n");
			return rc;
		}

		pr_debug("Client can not be notify, restart measurement\n");
		/* Set measurement in single measurement mode */
		mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
		rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
		if (rc < 0) {
			pr_err("adc-tm single mode select failed\n");
			goto fail;
		}

		/* Disable bank */
		rc = qpnp_adc_tm_disable(chip);
		if (rc < 0) {
			pr_err("adc-tm disable failed\n");
			goto fail;
		}

		/* Check if a conversion is in progress */
		rc = qpnp_adc_tm_req_sts_check(chip);
		if (rc < 0) {
			pr_err("adc-tm req_sts check failed\n");
			goto fail;
		}

		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
							sensor_mask, false);
		if (rc < 0) {
			pr_err("low threshold int write failed\n");
			goto fail;
		}

		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
							sensor_mask, false);
		if (rc < 0) {
			pr_err("high threshold int enable failed\n");
			goto fail;
		}

		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
							sensor_mask, false);
		if (rc < 0) {
			pr_err("multi measurement en failed\n");
			goto fail;
		}

		/* restart measurement */
		scale_type = chip->sensor[sensor_num].scale_type;
		chip->adc->amux_prop->amux_channel = channel;
		chip->adc->amux_prop->decimation =
			chip->adc->adc_channels[sensor_num].adc_decimation;
		chip->adc->amux_prop->hw_settle_time =
			chip->adc->adc_channels[sensor_num].hw_settle_time;
		chip->adc->amux_prop->fast_avg_setup =
			chip->adc->adc_channels[sensor_num].fast_avg_setup;
		chip->adc->amux_prop->mode_sel =
			ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
		adc_tm_rscale_fn[scale_type].chan(chip->vadc_dev,
				client_info->btm_param,
				&chip->adc->amux_prop->chan_prop->low_thr,
				&chip->adc->amux_prop->chan_prop->high_thr);
		qpnp_adc_tm_add_to_list(chip, sensor_num,
				client_info->btm_param,
				chip->adc->amux_prop->chan_prop);
		chip->adc->amux_prop->chan_prop->tm_channel_select =
				chip->sensor[sensor_num].btm_channel_num;
		chip->adc->amux_prop->chan_prop->state_request =
				client_info->btm_param->state_request;

		rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop);
		if (rc) {
			pr_err("adc-tm configure failed with %d\n", rc);
			goto fail;
		}
		*notify_check = 0;
		pr_debug("BTM channel reconfigured for measuremnt\n");
	}
fail:
	return rc;
}

static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip)
{
	u8 status_low = 0, status_high = 0, qpnp_adc_tm_meas_en = 0;
	u8 adc_tm_low_enable = 0, adc_tm_high_enable = 0;
	u8 adc_tm_low_enable = 0, adc_tm_high_enable = 0, notify_check = 0;
	u8 sensor_mask = 0, adc_tm_low_thr_set = 0, adc_tm_high_thr_set = 0;
	int rc = 0, sensor_notify_num = 0, i = 0, sensor_num = 0;
	uint32_t btm_chan_num = 0;
@@ -1489,6 +1684,13 @@ static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip)
				such as usb_id, vbatt, pmic_therm */
			sensor_mask = 1 << sensor_num;
			pr_debug("non thermal node - mask:%x\n", sensor_mask);
			rc = qpnp_adc_tm_recalib_request_check(chip,
					sensor_num, true, &notify_check);
			if (rc < 0 || !notify_check) {
				pr_debug("Calib recheck re-armed rc=%d\n", rc);
				adc_tm_high_enable = 0;
				goto fail;
			}
			rc = qpnp_adc_tm_reg_update(chip,
				QPNP_ADC_TM_HIGH_THR_INT_EN,
				sensor_mask, false);
@@ -1545,6 +1747,13 @@ static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip)
			/* For non thermal registered clients
				such as usb_id, vbatt, pmic_therm */
			pr_debug("non thermal node - mask:%x\n", sensor_mask);
			rc = qpnp_adc_tm_recalib_request_check(chip,
					sensor_num, false, &notify_check);
			if (rc < 0 || !notify_check) {
				pr_debug("Calib recheck re-armed rc=%d\n", rc);
				adc_tm_low_enable = 0;
				goto fail;
			}
			sensor_mask = 1 << sensor_num;
			rc = qpnp_adc_tm_reg_update(chip,
				QPNP_ADC_TM_LOW_THR_INT_EN,
@@ -1611,7 +1820,7 @@ fail:
	if (adc_tm_high_enable || adc_tm_low_enable)
		queue_work(chip->sensor[sensor_num].req_wq,
				&chip->sensor[sensor_num].work);
	if (rc < 0)
	if (rc < 0 || (!adc_tm_high_enable && !adc_tm_low_enable))
		atomic_dec(&chip->wq_cnt);

	return rc;
@@ -1971,6 +2180,8 @@ static int qpnp_adc_tm_probe(struct spmi_device *spmi)
			pr_err("vadc property missing, rc=%d\n", rc);
		goto fail;
	}
	chip->adc_tm_recalib_check = of_property_read_bool(node,
				"qcom,adc-tm-recalib-check");

	for_each_child_of_node(node, child) {
		char name[25];
+46 −0
Original line number Diff line number Diff line
@@ -150,6 +150,24 @@ struct qpnp_iadc_chip;
/* Structure device for qpnp adc tm */
struct qpnp_adc_tm_chip;

/**
 * enum qpnp_adc_clk_type - Clock rate supported.
 * %CLK_TYPE1: 2P4MHZ
 * %CLK_TYPE2: 4P8MHZ
 * %CLK_TYPE3: 9P6MHZ
 * %CLK_TYPE4: 19P2MHZ
 * %CLK_NONE: Do not use this Clk type.
 *
 * The Clock rate is specific to each channel of the QPNP ADC arbiter.
 */
enum qpnp_adc_clk_type {
	CLK_TYPE1 = 0,
	CLK_TYPE2,
	CLK_TYPE3,
	CLK_TYPE4,
	CLK_NONE,
};

/**
 * enum qpnp_adc_decimation_type - Sampling rate supported.
 * %DECIMATION_TYPE1: 512
@@ -1542,6 +1560,25 @@ int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip,
 * @param:	device instance for the VADC
 */
int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip);
/**
 * qpnp_vadc_calib_vref() - Read calibration channel REF_125V/VDD_VADC
 * @dev:	Structure device for qpnp vadc
 * @calib_type:	absolute or ratiometric calib type.
 * returns calibration channel adc code.
 */
int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
				enum qpnp_adc_calib_type calib_type,
				int *calib_data);
/**
 * qpnp_vadc_calib_gnd() - Read calibration channel REF_625MV/GND_REF
 * @dev:	Structure device for qpnp vadc
 * @calib_type:	absolute or ratiometric calib type.
 * returns calibration channel adc code.
 */
int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
				enum qpnp_adc_calib_type calib_type,
				int *calib_data);

#else
static inline int32_t qpnp_vadc_read(struct qpnp_vadc_chip *dev,
				uint32_t channel,
@@ -1692,6 +1729,15 @@ static inline int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip,
{ return -ENXIO; }
static inline int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip)
{ return -ENXIO; }
static int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
					enum qpnp_adc_calib_type calib_type,
					int *calib_data)
{ return -ENXIO; }
static int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
					enum qpnp_adc_calib_type calib_type,
					int *calib_data)
{ return -ENXIO; }

#endif

/* Public API */