hwmon: qpnp-current: Add configurable EOC option.

Provide a DT property qcom,iadc-poll-eoc option to
allow clients to choose between using interrupt or
polling method for end of conversion interrupt.
When the optional property is present, use polling
when an ADC conversion is requested.

Change-Id: I2c62a9f8e30f5c1200d439343f0548212977b731
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 8e6afc1..490a5ce 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -31,6 +31,7 @@
 #include <linux/hwmon-sysfs.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <linux/platform_device.h>
+#include <linux/wakelock.h>
 
 /* QPNP IADC register definition */
 #define QPNP_IADC_REVISION1				0x0
@@ -48,6 +49,7 @@
 #define QPNP_STATUS1_MEAS_INTERVAL_EN_STS		BIT(2)
 #define QPNP_STATUS1_REQ_STS				BIT(1)
 #define QPNP_STATUS1_EOC				BIT(0)
+#define QPNP_STATUS1_REQ_STS_EOC_MASK			0x3
 #define QPNP_STATUS2					0x9
 #define QPNP_STATUS2_CONV_SEQ_STATE_SHIFT		4
 #define QPNP_STATUS2_FIFO_NOT_EMPTY_FLAG		BIT(1)
@@ -110,8 +112,9 @@
 #define QPNP_IADC_DATA0					0x60
 #define QPNP_IADC_DATA1					0x61
 
-#define QPNP_ADC_CONV_TIME_MIN				8000
-#define QPNP_ADC_CONV_TIME_MAX				8200
+#define QPNP_ADC_CONV_TIME_MIN				2000
+#define QPNP_ADC_CONV_TIME_MAX				2100
+#define QPNP_ADC_ERR_COUNT				20
 
 #define QPNP_ADC_GAIN_NV				17857
 #define QPNP_OFFSET_CALIBRATION_SHORT_CADC_LEADS_IDEAL	0
@@ -135,6 +138,7 @@
 };
 
 struct qpnp_iadc_chip {
+	struct device				*dev;
 	struct qpnp_adc_drv			*adc;
 	int32_t					rsense;
 	bool					external_rsense;
@@ -147,8 +151,9 @@
 	struct qpnp_iadc_comp			iadc_comp;
 	struct qpnp_vadc_chip			*vadc_dev;
 	struct work_struct			trigger_completion_work;
-	struct sensor_device_attribute		sens_attr[0];
 	bool					skip_auto_calibrations;
+	bool					iadc_poll_eoc;
+	struct sensor_device_attribute		sens_attr[0];
 };
 
 LIST_HEAD(qpnp_iadc_device_list);
@@ -426,6 +431,8 @@
 {
 	u8 qpnp_iadc_mode_reg = 0, qpnp_iadc_ch_sel_reg = 0;
 	u8 qpnp_iadc_conv_req = 0, qpnp_iadc_dig_param_reg = 0;
+	u8 status1 = 0;
+	uint32_t count = 0;
 	int32_t rc = 0;
 
 	qpnp_iadc_ch_sel_reg = channel;
@@ -473,7 +480,8 @@
 		return rc;
 	}
 
-	INIT_COMPLETION(iadc->adc->adc_rslt_completion);
+	if (!iadc->iadc_poll_eoc)
+		INIT_COMPLETION(iadc->adc->adc_rslt_completion);
 
 	rc = qpnp_iadc_enable(iadc, true);
 	if (rc)
@@ -485,23 +493,43 @@
 		return rc;
 	}
 
-	rc = wait_for_completion_timeout(&iadc->adc->adc_rslt_completion,
-				QPNP_ADC_COMPLETION_TIMEOUT);
-	if (!rc) {
-		u8 status1 = 0;
-		rc = qpnp_iadc_read_reg(iadc, QPNP_STATUS1, &status1);
-		if (rc < 0)
-			return rc;
-		status1 &= (QPNP_STATUS1_REQ_STS | QPNP_STATUS1_EOC);
-		if (status1 == QPNP_STATUS1_EOC)
-			pr_debug("End of conversion status set\n");
-		else {
-			rc = qpnp_iadc_status_debug(iadc);
-			if (rc < 0) {
-				pr_err("status1 read failed with %d\n", rc);
+	if (iadc->iadc_poll_eoc) {
+		while (status1 != QPNP_STATUS1_EOC) {
+			rc = qpnp_iadc_read_reg(iadc, QPNP_STATUS1, &status1);
+			if (rc < 0)
+				return rc;
+			status1 &= QPNP_STATUS1_REQ_STS_EOC_MASK;
+			usleep_range(QPNP_ADC_CONV_TIME_MIN,
+					QPNP_ADC_CONV_TIME_MAX);
+			count++;
+			if (count > QPNP_ADC_ERR_COUNT) {
+				pr_err("retry error exceeded\n");
+				rc = qpnp_iadc_status_debug(iadc);
+				if (rc < 0)
+					pr_err("IADC status debug failed\n");
+				rc = -EINVAL;
 				return rc;
 			}
-			return -EINVAL;
+		}
+	} else {
+		rc = wait_for_completion_timeout(
+				&iadc->adc->adc_rslt_completion,
+				QPNP_ADC_COMPLETION_TIMEOUT);
+		if (!rc) {
+			rc = qpnp_iadc_read_reg(iadc, QPNP_STATUS1, &status1);
+			if (rc < 0)
+				return rc;
+			status1 &= QPNP_STATUS1_REQ_STS_EOC_MASK;
+			if (status1 == QPNP_STATUS1_EOC)
+				pr_debug("End of conversion status set\n");
+			else {
+				rc = qpnp_iadc_status_debug(iadc);
+				if (rc < 0) {
+					pr_err("status debug failed %d\n", rc);
+					return rc;
+				}
+				return -EINVAL;
+			}
 		}
 	}
 
@@ -556,6 +584,11 @@
 
 	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_iadc_configure(iadc, GAIN_CALIBRATION_17P857MV,
 						&raw_data, mode_sel);
 	if (rc < 0) {
@@ -639,6 +672,10 @@
 		goto fail;
 	}
 fail:
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("releasing iadc eoc wakelock\n");
+		pm_relax(iadc->dev);
+	}
 	mutex_unlock(&iadc->adc->adc_lock);
 	return rc;
 }
@@ -786,6 +823,11 @@
 
 	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_iadc_configure(iadc, channel, &raw_data, mode_sel);
 	if (rc < 0) {
 		pr_err("qpnp adc result read failed with %d\n", rc);
@@ -820,6 +862,10 @@
 
 	result->result_ua = (int32_t) result_current;
 fail:
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("releasing iadc eoc wakelock\n");
+		pm_relax(iadc->dev);
+	}
 	mutex_unlock(&iadc->adc->adc_lock);
 
 	return rc;
@@ -883,6 +929,11 @@
 
 	mutex_lock(&iadc->iadc_vadc_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");
@@ -909,6 +960,10 @@
 fail:
 	iadc->iadc_mode_sel = false;
 
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("releasing iadc eoc wakelock\n");
+		pm_relax(iadc->dev);
+	}
 	mutex_unlock(&iadc->iadc_vadc_lock);
 
 	return rc;
@@ -999,6 +1054,7 @@
 		goto fail;
 	}
 
+	iadc->dev = &(spmi->dev);
 	iadc->adc = adc_qpnp;
 
 	rc = qpnp_adc_get_devicetree_data(spmi, iadc->adc);
@@ -1026,14 +1082,18 @@
 		iadc->external_rsense = true;
 	}
 
-	rc = devm_request_irq(&spmi->dev, iadc->adc->adc_irq_eoc,
-				qpnp_iadc_isr,
-	IRQF_TRIGGER_RISING, "qpnp_iadc_interrupt", iadc);
-	if (rc) {
-		dev_err(&spmi->dev, "failed to request adc irq\n");
-		return rc;
-	} else
-		enable_irq_wake(iadc->adc->adc_irq_eoc);
+	iadc->iadc_poll_eoc = of_property_read_bool(node,
+						"qcom,iadc-poll-eoc");
+	if (!iadc->iadc_poll_eoc) {
+		rc = devm_request_irq(&spmi->dev, iadc->adc->adc_irq_eoc,
+				qpnp_iadc_isr, IRQF_TRIGGER_RISING,
+				"qpnp_iadc_interrupt", iadc);
+		if (rc) {
+			dev_err(&spmi->dev, "failed to request adc irq\n");
+			return rc;
+		} else
+			enable_irq_wake(iadc->adc->adc_irq_eoc);
+	}
 
 	rc = qpnp_iadc_init_hwmon(iadc, spmi);
 	if (rc) {
@@ -1063,6 +1123,9 @@
 	if (rc)
 		dev_err(&spmi->dev, "failed to calibrate for USR trim\n");
 
+	if (iadc->iadc_poll_eoc)
+		device_init_wakeup(iadc->dev, 1);
+
 	schedule_delayed_work(&iadc->iadc_work,
 			round_jiffies_relative(msecs_to_jiffies
 					(QPNP_IADC_CALIB_SECONDS)));
@@ -1093,6 +1156,8 @@
 		i++;
 	}
 	hwmon_device_unregister(iadc->iadc_hwmon);
+	if (iadc->iadc_poll_eoc)
+		pm_relax(iadc->dev);
 	dev_set_drvdata(&spmi->dev, NULL);
 
 	return 0;