Merge "hwmon: qpnp-vadc: Add EOC polling option"
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index 7f34a8f..ae7d039 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -20,6 +20,9 @@
 Channel nodes
 NOTE: Atleast one Channel node is required.
 
+Optional properties:
+- qcom,vadc-poll-eoc: Use polling instead of interrupts for End of Conversion completion.
+
 Client required property:
 - qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
 			The consumer name passed to the driver when calling
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 2fe69fb..c4a2567 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -99,7 +99,7 @@
 #define QPNP_VADC_CONV_TIME_MIN					2000
 #define QPNP_VADC_CONV_TIME_MAX					2100
 #define QPNP_ADC_COMPLETION_TIMEOUT				HZ
-#define QPNP_VADC_ERR_COUNT					5
+#define QPNP_VADC_ERR_COUNT					20
 
 struct qpnp_vadc_chip {
 	struct device			*dev;
@@ -112,6 +112,7 @@
 	bool				vadc_iadc_sync_lock;
 	u8				id;
 	struct work_struct		trigger_completion_work;
+	bool				vadc_poll_eoc;
 	struct sensor_device_attribute	sens_attr[0];
 };
 
@@ -346,7 +347,8 @@
 		}
 	}
 
-	INIT_COMPLETION(vadc->adc->adc_rslt_completion);
+	if (!vadc->vadc_poll_eoc)
+		INIT_COMPLETION(vadc->adc->adc_rslt_completion);
 
 	rc = qpnp_vadc_enable(vadc, true);
 	if (rc)
@@ -799,13 +801,19 @@
 					struct qpnp_vadc_result *result)
 {
 	int rc = 0, scale_type, amux_prescaling, dt_index = 0;
-	uint32_t ref_channel;
+	uint32_t ref_channel, count = 0;
+	u8 status1 = 0;
 
 	if (qpnp_vadc_is_valid(vadc))
 		return -EPROBE_DEFER;
 
 	mutex_lock(&vadc->adc->adc_lock);
 
+	if (vadc->vadc_poll_eoc) {
+		pr_debug("requesting vadc eoc stay awake\n");
+		pm_stay_awake(vadc->dev);
+	}
+
 	if (!vadc->vadc_init_calib) {
 		rc = qpnp_vadc_version_check(vadc);
 		if (rc)
@@ -862,22 +870,44 @@
 		goto fail_unlock;
 	}
 
-	rc = wait_for_completion_timeout(&vadc->adc->adc_rslt_completion,
-					QPNP_ADC_COMPLETION_TIMEOUT);
-	if (!rc) {
-		u8 status1 = 0;
-		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1);
-		if (rc < 0)
-			goto fail_unlock;
-		status1 &= (QPNP_VADC_STATUS1_REQ_STS | QPNP_VADC_STATUS1_EOC);
-		if (status1 == QPNP_VADC_STATUS1_EOC)
-			pr_debug("End of conversion status set\n");
-		else {
-			rc = qpnp_vadc_status_debug(vadc);
+	if (vadc->vadc_poll_eoc) {
+		while (status1 != QPNP_VADC_STATUS1_EOC) {
+			rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1,
+								&status1);
 			if (rc < 0)
-				pr_err("VADC disable failed\n");
-			rc = -EINVAL;
-			goto fail_unlock;
+				goto fail_unlock;
+			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) {
+				pr_err("retry error exceeded\n");
+				rc = qpnp_vadc_status_debug(vadc);
+				if (rc < 0)
+					pr_err("VADC disable failed\n");
+				rc = -EINVAL;
+				goto fail_unlock;
+			}
+		}
+	} else {
+		rc = wait_for_completion_timeout(
+					&vadc->adc->adc_rslt_completion,
+					QPNP_ADC_COMPLETION_TIMEOUT);
+		if (!rc) {
+			rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1,
+								&status1);
+			if (rc < 0)
+				goto fail_unlock;
+			status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
+			if (status1 == QPNP_VADC_STATUS1_EOC)
+				pr_debug("End of conversion status set\n");
+			else {
+				rc = qpnp_vadc_status_debug(vadc);
+				if (rc < 0)
+					pr_err("VADC disable failed\n");
+				rc = -EINVAL;
+				goto fail_unlock;
+			}
 		}
 	}
 
@@ -917,6 +947,11 @@
 		vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);
 
 fail_unlock:
+	if (vadc->vadc_poll_eoc) {
+		pr_debug("requesting vadc eoc stay awake\n");
+		pm_relax(vadc->dev);
+	}
+
 	mutex_unlock(&vadc->adc->adc_lock);
 
 	return rc;
@@ -1168,15 +1203,19 @@
 	}
 	mutex_init(&vadc->adc->adc_lock);
 
-	rc = devm_request_irq(&spmi->dev, vadc->adc->adc_irq_eoc,
+	vadc->vadc_poll_eoc = of_property_read_bool(node,
+						"qcom,vadc-poll-eoc");
+	if (!vadc->vadc_poll_eoc) {
+		rc = devm_request_irq(&spmi->dev, vadc->adc->adc_irq_eoc,
 				qpnp_vadc_isr, IRQF_TRIGGER_RISING,
 				"qpnp_vadc_interrupt", vadc);
-	if (rc) {
-		dev_err(&spmi->dev,
+		if (rc) {
+			dev_err(&spmi->dev,
 			"failed to request adc irq with error %d\n", rc);
-		return rc;
-	} else {
-		enable_irq_wake(vadc->adc->adc_irq_eoc);
+			return rc;
+		} else {
+			enable_irq_wake(vadc->adc->adc_irq_eoc);
+		}
 	}
 
 	rc = qpnp_vadc_init_hwmon(vadc, spmi);
@@ -1201,6 +1240,9 @@
 	}
 
 	INIT_WORK(&vadc->trigger_completion_work, qpnp_vadc_work);
+	if (vadc->vadc_poll_eoc)
+		device_init_wakeup(vadc->dev, 1);
+
 	vadc->vadc_iadc_sync_lock = false;
 
 	dev_set_drvdata(&spmi->dev, vadc);
@@ -1233,6 +1275,8 @@
 	}
 	hwmon_device_unregister(vadc->vadc_hwmon);
 	list_del(&vadc->list);
+	if (vadc->vadc_poll_eoc)
+		pm_relax(vadc->dev);
 	dev_set_drvdata(&spmi->dev, NULL);
 
 	return 0;