hwmon: qpnp-adc-current: Fix potential race condition

There should be no synchronous Voltage and Current readings
initiated on IADC_USR when a single VADC_USR conversion is
in progress. If a synchronous Voltage and Current read
occures while a VADC_USR is in progress it results in an
invalid state for that single conversion on the VADC_USR.
There is a potential race condition where the above scenario
can occur that is due to an IADC conversion occuring before
the VADC_IADC USR sync conversion is complete and ends up
using the mode select as a VADC_IADC sync request.
Therefore use a single lock for both IADC and
synchronous VADC_IADC request.

Change-Id: Idb451b813bc5f08b7239d371fc0f8fa5e17429b1
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index adaff41..9839595 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -147,7 +147,6 @@
 	struct list_head			list;
 	int64_t					die_temp;
 	struct delayed_work			iadc_work;
-	struct mutex				iadc_vadc_lock;
 	bool					iadc_mode_sel;
 	struct qpnp_iadc_comp			iadc_comp;
 	struct qpnp_vadc_chip			*vadc_dev;
@@ -1012,12 +1011,10 @@
 	if (qpnp_iadc_is_valid(iadc) < 0)
 		return -EPROBE_DEFER;
 
-	if (!iadc->iadc_mode_sel) {
-		rc = qpnp_check_pmic_temp(iadc);
-		if (rc) {
-			pr_err("Error checking pmic therm temp\n");
-			return rc;
-		}
+	rc = qpnp_check_pmic_temp(iadc);
+	if (rc) {
+		pr_err("Error checking pmic therm temp\n");
+		return rc;
 	}
 
 	mutex_lock(&iadc->adc->adc_lock);
@@ -1121,24 +1118,21 @@
 	enum qpnp_iadc_channels i_channel, struct qpnp_iadc_result *i_result,
 	enum qpnp_vadc_channels v_channel, struct qpnp_vadc_result *v_result)
 {
-	int rc = 0;
+	int rc = 0, mode_sel = 0, num = 0, rsense_n_ohms = 0, sign = 0;
+	uint16_t raw_data;
+	int32_t rsense_u_ohms = 0;
+	int64_t result_current;
 
 	if (qpnp_iadc_is_valid(iadc) < 0)
 		return -EPROBE_DEFER;
 
-	mutex_lock(&iadc->iadc_vadc_lock);
+	mutex_lock(&iadc->adc->adc_lock);
 
 	if (iadc->iadc_poll_eoc) {
 		pr_debug("acquiring iadc eoc wakelock\n");
 		pm_stay_awake(iadc->dev);
 	}
 
-	rc = qpnp_check_pmic_temp(iadc);
-	if (rc) {
-		pr_err("PMIC die temp check failed\n");
-		goto fail;
-	}
-
 	iadc->iadc_mode_sel = true;
 
 	rc = qpnp_vadc_iadc_sync_request(iadc->vadc_dev, v_channel);
@@ -1147,11 +1141,43 @@
 		goto fail;
 	}
 
-	rc = qpnp_iadc_read(iadc, i_channel, i_result);
-	if (rc)
-		pr_err("Configuring IADC failed\n");
-	/* Intentional fall through to release VADC */
+	rc = qpnp_iadc_configure(iadc, i_channel, &raw_data, mode_sel);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		goto fail_release_vadc;
+	}
 
+	rc = qpnp_iadc_get_rsense(iadc, &rsense_n_ohms);
+	pr_debug("current raw:0%x and rsense:%d\n",
+			raw_data, rsense_n_ohms);
+	rsense_u_ohms = rsense_n_ohms/1000;
+	num = raw_data - iadc->adc->calib.offset_raw;
+	if (num < 0) {
+		sign = 1;
+		num = -num;
+	}
+
+	i_result->result_uv = (num * QPNP_ADC_GAIN_NV)/
+		(iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
+	result_current = i_result->result_uv;
+	result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
+	/* Intentional fall through. Process the result w/o comp */
+	do_div(result_current, rsense_u_ohms);
+
+	if (sign) {
+		i_result->result_uv = -i_result->result_uv;
+		result_current = -result_current;
+	}
+	result_current *= -1;
+	rc = qpnp_iadc_comp_result(iadc, &result_current);
+	if (rc < 0)
+		pr_err("Error during compensating the IADC\n");
+	rc = 0;
+	result_current *= -1;
+
+	i_result->result_ua = (int32_t) result_current;
+
+fail_release_vadc:
 	rc = qpnp_vadc_iadc_sync_complete_request(iadc->vadc_dev, v_channel,
 							v_result);
 	if (rc)
@@ -1163,7 +1189,7 @@
 		pr_debug("releasing iadc eoc wakelock\n");
 		pm_relax(iadc->dev);
 	}
-	mutex_unlock(&iadc->iadc_vadc_lock);
+	mutex_unlock(&iadc->adc->adc_lock);
 
 	return rc;
 }
@@ -1306,7 +1332,6 @@
 		goto fail;
 	}
 
-	mutex_init(&iadc->iadc_vadc_lock);
 	INIT_WORK(&iadc->trigger_completion_work, qpnp_iadc_trigger_completion);
 	INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
 	rc = qpnp_iadc_comp_info(iadc);
@@ -1347,7 +1372,6 @@
 	int i = 0;
 
 	cancel_delayed_work(&iadc->iadc_work);
-	mutex_destroy(&iadc->iadc_vadc_lock);
 	for_each_child_of_node(node, child) {
 		device_remove_file(&spmi->dev,
 			&iadc->sens_attr[i].dev_attr);