[PATCH 2/2] [RFC] iio: dht11: Add optional support for supply control via regulator framework

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




If a supply is provided via DT, release the supply whenever we don't read
the sensor. Also use notifications to track the actual status of the
supply to ensure that we meet the timing requirements stated in the
datasheet.

This change is motivated by the hope that these sensors will work more
reliably if power-cycled regularly.

Signed-off-by: Harald Geyer <harald@xxxxxxxxx>
---
This is an RFC this time, because I want to run extensive long term tests
with DHT11 and DHT22 before officially proposing this for inclusion. I
have learned my lessons with this quirky bits of HW... :(

However since this will take a lot of time (mostly physical, but also
man hours), I'd like to get review comments now instead of risking a
second iteration of long term tests.

 .../devicetree/bindings/iio/humidity/dht11.txt     | 10 ++++
 drivers/iio/humidity/dht11.c                       | 57 ++++++++++++++++++++--
 2 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/iio/humidity/dht11.txt b/Documentation/devicetree/bindings/iio/humidity/dht11.txt
index ecc24c19..ab9ea18 100644
--- a/Documentation/devicetree/bindings/iio/humidity/dht11.txt
+++ b/Documentation/devicetree/bindings/iio/humidity/dht11.txt
@@ -6,9 +6,19 @@ Required properties:
     line, see "gpios property" in
     Documentation/devicetree/bindings/gpio/gpio.txt.
 
+Optional properties:
+  - vdd-supply: phandle to the regulator node, for details see
+    Documentation/devicetree/bindings/regulator/regulator.txt
+    On Linux the driver disables the regulator after 4 seconds of
+    inactivity, to save power and to ensure that the hardware is
+    reset regularly, because it was found to randomly stop responding
+    otherwise. However for this to work all other consumers of the
+    regulator must cooperate (disable the regulator when possible too).
+
 Example:
 
 humidity_sensor {
 	compatible = "dht11";
 	gpios = <&gpio0 6 0>;
+	vdd-supply = <&sensor_supply>;
 }
diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c
index 2a22ad9..5bce712 100644
--- a/drivers/iio/humidity/dht11.c
+++ b/drivers/iio/humidity/dht11.c
@@ -34,12 +34,16 @@
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/timekeeping.h>
+#include <linux/regulator/consumer.h>
+#include <linux/notifier.h>
 
 #include <linux/iio/iio.h>
 
 #define DRIVER_NAME	"dht11"
 
 #define DHT11_DATA_VALID_TIME	2000000000  /* 2s in ns */
+#define DHT11_POWER_ON_TIME 2000000000 /* must be less the MAX_INT */
+#define DHT11_POWEROFF_DELAY 4000 /* ms */
 
 #define DHT11_EDGES_PREAMBLE 2
 #define DHT11_BITS_PER_READ 40
@@ -84,6 +88,10 @@ struct dht11 {
 	int				gpio;
 	int				irq;
 
+	struct regulator		*supply;
+	struct notifier_block		nb;
+	s64				timestamp_enabled;
+
 	struct completion		completion;
 	/* The iio sysfs interface doesn't prevent concurrent reads: */
 	struct mutex			lock;
@@ -97,6 +105,21 @@ struct dht11 {
 	struct {s64 ts; int value; }	edges[DHT11_EDGES_PER_READ];
 };
 
+static int dht11_regulator_update(struct notifier_block *nb,
+				  unsigned long event,
+				  void *ignored)
+{
+	struct dht11 *dht11 = container_of(nb, struct dht11, nb);
+
+	if (event & REGULATOR_EVENT_DISABLE)
+		dht11->timestamp_enabled = -1;
+	else if (event & REGULATOR_EVENT_ENABLE)
+		if (dht11->timestamp_enabled == -1)
+			dht11->timestamp_enabled = ktime_get_boot_ns();
+
+	return NOTIFY_OK;
+}
+
 #ifdef CONFIG_DYNAMIC_DEBUG
 /*
  * dht11_edges_print: show the data as actually received by the
@@ -203,9 +226,22 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
 {
 	struct dht11 *dht11 = iio_priv(iio_dev);
 	int ret, timeres, offset;
+	s64 time;
 
 	mutex_lock(&dht11->lock);
-	if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_boot_ns()) {
+	time = ktime_get_boot_ns();
+	if (dht11->timestamp + DHT11_DATA_VALID_TIME < time) {
+		if (dht11->supply) {
+			ret = regulator_enable(dht11->supply);
+			if (ret) {
+				dev_err(dht11->dev, "Can't enable supply\n");
+				goto err_reg;
+			}
+			time -= dht11->timestamp_enabled + DHT11_POWER_ON_TIME;
+			if (time < 0)
+				msleep(((int)(-time)) / 1000000);
+		}
+
 		timeres = ktime_get_resolution_ns();
 		dev_dbg(dht11->dev, "current timeresolution: %dns\n", timeres);
 		if (timeres > DHT11_MIN_TIMERES) {
@@ -266,8 +302,13 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
 				break;
 		}
 
+err:
+		if (dht11->supply)
+			regulator_disable_deferred(dht11->supply,
+						   DHT11_POWEROFF_DELAY);
+
 		if (ret)
-			goto err;
+			goto err_reg;
 	}
 
 	ret = IIO_VAL_INT;
@@ -277,7 +318,8 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
 		*val = dht11->humidity;
 	else
 		ret = -EINVAL;
-err:
+
+err_reg:
 	dht11->num_edges = -1;
 	mutex_unlock(&dht11->lock);
 	return ret;
@@ -332,6 +374,15 @@ static int dht11_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
+	dht11->timestamp_enabled = -1;
+	dht11->supply = devm_regulator_get(dev, "vdd");
+	if (IS_ERR(dht11->supply)) {
+		dht11->supply = NULL;
+	} else {
+		dht11->nb.notifier_call = &dht11_regulator_update;
+		devm_regulator_register_notifier(dht11->supply, &dht11->nb);
+	}
+
 	dht11->timestamp = ktime_get_boot_ns() - DHT11_DATA_VALID_TIME - 1;
 	dht11->num_edges = -1;
 
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux