[PATCH v2 3/4] hwmon: (sht15) add support for the status register

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

 



* Add support for:
  - Heater.
  - End of battery notice.
  - Ability not to reload from OTP.
  - Low resolution (12bit temp, 8bit humidity).
* Add an utility function to read individual bytes from the device.

Signed-off-by: Vivien Didelot <vivien.didelot@xxxxxxxxxxxxxxxxxxxx>
---
 Documentation/hwmon/sht15 |   29 +++++-
 drivers/hwmon/sht15.c     |  247 +++++++++++++++++++++++++++++++++++++++------
 include/linux/sht15.h     |    4 +
 3 files changed, 245 insertions(+), 35 deletions(-)

diff --git a/Documentation/hwmon/sht15 b/Documentation/hwmon/sht15
index 50c07f5..a9e36e3 100644
--- a/Documentation/hwmon/sht15
+++ b/Documentation/hwmon/sht15
@@ -4,6 +4,7 @@ Kernel driver sht15
 Authors:
   * Wouter Horre
   * Jonathan Cameron
+  * Vivien Didelot <vivien.didelot@xxxxxxxxxxxxxxxxxxxx>
 
 Supported chips:
   * Sensirion SHT10
@@ -30,14 +31,38 @@ Description
 The SHT10, SHT11, SHT15, SHT71, and SHT75 are humidity and temperature
 sensors.
 
-The devices communicate using two GPIO lines and use the default
-resolution settings of 14 bits for temperature and 12 bits for humidity.
+The devices communicate using two GPIO lines.
+
+Supported resolutions for the measurements are 14 bits for temperature and 12
+bits for humidity, or 12 bits for temperature and 8 bits for humidity.
+
+The humidity calibration coefficients are programmed into an OTP memory on the
+chip. These coefficients are used to internally calibrate the signals from the
+sensors. Disabling the reload of those coefficients allows saving 10ms for each
+measurement and decrease power consumption, while loosing on precision.
+
+Some options may be set directly in the sht15_platform_data structure
+or via sysfs attributes.
 
 Note: The regulator supply name is set to "vcc".
 
+Platform data
+-------------
+
+* no_otp_reload:
+  flag to indicate not to reload from OTP (default to false).
+* low_resolution:
+  flag to indicate the temp/humidity resolution to use (default to false).
+
 Sysfs interface
 ---------------
 
 * temp1_input:     temperature input
 * humidity1_input: humidity input
+* heater_enable:   write 1 in this attribute to enable the on-chip heater,
+                   0 to disable it. Be careful not to enable the heater
+                   for too long.
+* temp1_fault:     if 1, this means that the voltage is low (below 2.47V) and
+                   measurement may be invalid.
+* humidity1_fault: same as temp1_fault.
 
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index 42992fe..77d25d3 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -1,6 +1,9 @@
 /*
  * sht15.c - support for the SHT15 Temperature and Humidity Sensor
  *
+ * Portions Copyright (c) 2010-2011 Savoir-faire Linux Inc.
+ *          Vivien Didelot <vivien.didelot@xxxxxxxxxxxxxxxxxxxx>
+ *
  * Copyright (c) 2009 Jonathan Cameron
  *
  * Copyright (c) 2007 Wouter Horre
@@ -33,6 +36,8 @@
 /* Commands */
 #define SHT15_MEASURE_TEMP		0x03
 #define SHT15_MEASURE_RH		0x05
+#define SHT15_WRITE_STATUS		0x06
+#define SHT15_READ_STATUS		0x07
 #define SHT15_SOFT_RESET		0x1E
 
 /* Min timings */
@@ -41,6 +46,12 @@
 #define SHT15_TSU			150	/* (nsecs) data setup time */
 #define SHT15_TSRST			11	/* (msecs) soft reset time */
 
+/* Status Register Bits */
+#define SHT15_STATUS_LOW_RESOLUTION	0x01
+#define SHT15_STATUS_NO_OTP_RELOAD	0x02
+#define SHT15_STATUS_HEATER		0x04
+#define SHT15_STATUS_LOW_BATTERY	0x40
+
 /* Actions the driver may be doing */
 enum sht15_state {
 	SHT15_READING_NOTHING,
@@ -74,9 +85,12 @@ static const struct sht15_temppair temppoints[] = {
  * @wait_queue:		wait queue for getting values from device.
  * @val_temp:		last temperature value read from device.
  * @val_humid:		last humidity value read from device.
+ * @val_status:		last status register value read from device.
  * @state:		state identifying the action the driver is doing.
  * @measurements_valid:	are the current stored measures valid (start condition).
+ * @status_valid:	is the current stored status valid (start condition).
  * @last_measurement:	time of last measure.
+ * @last_status:	time of last status reading.
  * @read_lock:		mutex to ensure only one read in progress at a time.
  * @dev:		associate device structure.
  * @hwmon_dev:		device associated with hwmon subsystem.
@@ -97,9 +111,12 @@ struct sht15_data {
 	wait_queue_head_t		wait_queue;
 	uint16_t			val_temp;
 	uint16_t			val_humid;
+	u8				val_status;
 	enum sht15_state		state;
 	bool				measurements_valid;
+	bool				status_valid;
 	unsigned long			last_measurement;
+	unsigned long			last_status;
 	struct mutex			read_lock;
 	struct device			*dev;
 	struct device			*hwmon_dev;
@@ -244,11 +261,106 @@ static int sht15_soft_reset(struct sht15_data *data)
 	if (ret)
 		return ret;
 	msleep(SHT15_TSRST);
+	/* device resets default hardware status register value */
+	data->val_status = 0;
+
+	return ret;
+}
+
+/**
+ * sht15_end_transmission() - notify device of end of transmission
+ * @data:	device state.
+ *
+ * This is basically a NAK (single clock pulse, data high).
+ */
+static void sht15_end_transmission(struct sht15_data *data)
+{
+	gpio_direction_output(data->pdata->gpio_data, 1);
+	ndelay(SHT15_TSU);
+	gpio_set_value(data->pdata->gpio_sck, 1);
+	ndelay(SHT15_TSCKH);
+	gpio_set_value(data->pdata->gpio_sck, 0);
+	ndelay(SHT15_TSCKL);
+}
+
+/**
+ * sht15_read_byte() - Read a byte back from the device
+ * @data:	device state.
+ */
+static u8 sht15_read_byte(struct sht15_data *data)
+{
+	int i;
+	u8 byte = 0;
+
+	for (i = 0; i < 8; ++i) {
+		byte <<= 1;
+		gpio_set_value(data->pdata->gpio_sck, 1);
+		ndelay(SHT15_TSCKH);
+		byte |= !!gpio_get_value(data->pdata->gpio_data);
+		gpio_set_value(data->pdata->gpio_sck, 0);
+		ndelay(SHT15_TSCKL);
+	}
+	return byte;
+}
+
+/**
+ * sht15_send_status() - write the status register byte
+ * @data:	sht15 specific data.
+ * @status:	the byte to set the status register with.
+ *
+ * As described in figure 14 and table 5 of the datasheet.
+ */
+static int sht15_send_status(struct sht15_data *data, u8 status)
+{
+	int ret;
+
+	ret = sht15_send_cmd(data, SHT15_WRITE_STATUS);
+	if (ret)
+		return ret;
+	gpio_direction_output(data->pdata->gpio_data, 1);
+	ndelay(SHT15_TSU);
+	sht15_send_byte(data, status);
+	ret = sht15_wait_for_response(data);
+	if (ret)
+		return ret;
 
+	data->val_status = status;
 	return 0;
 }
 
 /**
+ * sht15_update_status() - get updated status register from device if too old
+ * @data:	device instance specific data.
+ *
+ * As described in figure 15 and table 5 of the datasheet.
+ */
+static int sht15_update_status(struct sht15_data *data)
+{
+	int ret = 0;
+	u8 status;
+	int timeout = HZ;
+
+	mutex_lock(&data->read_lock);
+	if (time_after(jiffies, data->last_status + timeout)
+			|| !data->status_valid) {
+		ret = sht15_send_cmd(data, SHT15_READ_STATUS);
+		if (ret)
+			goto error_ret;
+		status = sht15_read_byte(data);
+
+		sht15_end_transmission(data);
+
+		data->val_status = status;
+		data->status_valid = true;
+		data->last_status = jiffies;
+	}
+error_ret:
+	mutex_unlock(&data->read_lock);
+
+	return ret;
+}
+
+/**
  * sht15_measurement() - get a new value from device
  * @data:		device instance specific data
  * @command:		command sent to request value
@@ -324,6 +436,7 @@ error_ret:
 static inline int sht15_calc_temp(struct sht15_data *data)
 {
 	int d1 = temppoints[0].d1;
+	int d2 = (data->val_status & SHT15_STATUS_LOW_RESOLUTION) ? 40 : 10;
 	int i;
 
 	for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--)
@@ -336,7 +449,7 @@ static inline int sht15_calc_temp(struct sht15_data *data)
 			break;
 		}
 
-	return data->val_temp * 10 + d1;
+	return data->val_temp * d2 + d1;
 }
 
 /**
@@ -353,19 +466,86 @@ static inline int sht15_calc_humid(struct sht15_data *data)
 {
 	int rh_linear; /* milli percent */
 	int temp = sht15_calc_temp(data);
-
+	int c2, c3;
+	int t2;
 	const int c1 = -4;
-	const int c2 = 40500; /* x 10 ^ -6 */
-	const int c3 = -28;   /* x 10 ^ -7 */
+
+	if (data->val_status & SHT15_STATUS_LOW_RESOLUTION) {
+		c2 = 648000; /* x 10 ^ -6 */
+		c3 = -7200;  /* x 10 ^ -7 */
+		t2 = 1280;
+	} else {
+		c2 = 40500;  /* x 10 ^ -6 */
+		c3 = -28;    /* x 10 ^ -7 */
+		t2 = 80;
+	}
 
 	rh_linear = c1 * 1000
 		+ c2 * data->val_humid / 1000
 		+ (data->val_humid * data->val_humid * c3) / 10000;
-	return (temp - 25000) * (10000 + 80 * data->val_humid)
+	return (temp - 25000) * (10000 + t2 * data->val_humid)
 		/ 1000000 + rh_linear;
 }
 
 /**
+ * sht15_show_status() - show status information in sysfs
+ * @dev:	device.
+ * @attr:	device attribute.
+ * @buf:	sysfs buffer where information is written to.
+ *
+ * Will be called on read access to temp1_fault, humidity1_fault
+ * and heater_enable sysfs attributes.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t sht15_show_status(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	int ret;
+	struct sht15_data *data = dev_get_drvdata(dev);
+	u8 bit = to_sensor_dev_attr(attr)->index;
+
+	ret = sht15_update_status(data);
+
+	return ret ? ret : sprintf(buf, "%d\n", !!(data->val_status & bit));
+}
+
+/**
+ * sht15_store_heater() - change heater state via sysfs
+ * @dev:	device.
+ * @attr:	device attribute.
+ * @buf:	sysfs buffer to read the new heater state from.
+ * @count:	length of the data.
+ *
+ * Will be called on read access to heater_enable sysfs attribute.
+ * Returns number of bytes actually decoded, negative errno on error.
+ */
+static ssize_t sht15_store_heater(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	int ret;
+	struct sht15_data *data = dev_get_drvdata(dev);
+	long value;
+	u8 status;
+
+	if (strict_strtol(buf, 10, &value))
+		return -EINVAL;
+
+	mutex_lock(&data->read_lock);
+	status = data->val_status & 0x07;
+	if (!!value)
+		status |= SHT15_STATUS_HEATER;
+	else
+		status &= ~SHT15_STATUS_HEATER;
+
+	ret = sht15_send_status(data, status);
+	mutex_unlock(&data->read_lock);
+
+	return ret ? ret : count;
+}
+
+/**
  * sht15_show_temp() - show temperature measurement value in sysfs
  * @dev:	device.
  * @attr:	device attribute.
@@ -407,7 +587,6 @@ static ssize_t sht15_show_humidity(struct device *dev,
 	ret = sht15_update_measurements(data);
 
 	return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data));
-
 }
 
 static ssize_t show_name(struct device *dev,
@@ -422,10 +601,19 @@ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
 			  sht15_show_temp, NULL, 0);
 static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO,
 			  sht15_show_humidity, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, sht15_show_status, NULL,
+			  SHT15_STATUS_LOW_BATTERY);
+static SENSOR_DEVICE_ATTR(humidity1_fault, S_IRUGO, sht15_show_status, NULL,
+			  SHT15_STATUS_LOW_BATTERY);
+static SENSOR_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, sht15_show_status,
+			  sht15_store_heater, SHT15_STATUS_HEATER);
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
 static struct attribute *sht15_attrs[] = {
 	&sensor_dev_attr_temp1_input.dev_attr.attr,
 	&sensor_dev_attr_humidity1_input.dev_attr.attr,
+	&sensor_dev_attr_temp1_fault.dev_attr.attr,
+	&sensor_dev_attr_humidity1_fault.dev_attr.attr,
+	&sensor_dev_attr_heater_enable.dev_attr.attr,
 	&dev_attr_name.attr,
 	NULL,
 };
@@ -466,25 +654,8 @@ static void sht15_ack(struct sht15_data *data)
 	gpio_direction_input(data->pdata->gpio_data);
 }
 
-/**
- * sht15_end_transmission() - notify device of end of transmission
- * @data:	device state
- *
- * This is basically a NAK. (single clock pulse, data high)
- */
-static void sht15_end_transmission(struct sht15_data *data)
-{
-	gpio_direction_output(data->pdata->gpio_data, 1);
-	ndelay(SHT15_TSU);
-	gpio_set_value(data->pdata->gpio_sck, 1);
-	ndelay(SHT15_TSCKH);
-	gpio_set_value(data->pdata->gpio_sck, 0);
-	ndelay(SHT15_TSCKL);
-}
-
 static void sht15_bh_read_data(struct work_struct *work_s)
 {
-	int i;
 	uint16_t val = 0;
 	struct sht15_data *data
 		= container_of(work_s, struct sht15_data,
@@ -505,16 +676,10 @@ static void sht15_bh_read_data(struct work_struct *work_s)
 	}
 
 	/* Read the data back from the device */
-	for (i = 0; i < 16; ++i) {
-		val <<= 1;
-		gpio_set_value(data->pdata->gpio_sck, 1);
-		ndelay(SHT15_TSCKH);
-		val |= !!gpio_get_value(data->pdata->gpio_data);
-		gpio_set_value(data->pdata->gpio_sck, 0);
-		ndelay(SHT15_TSCKL);
-		if (i == 7)
-			sht15_ack(data);
-	}
+	val = sht15_read_byte(data);
+	val <<= 8;
+	sht15_ack(data);
+	val |= sht15_read_byte(data);
 
 	/* Tell the device we are done */
 	sht15_end_transmission(data);
@@ -568,6 +733,7 @@ static int __devinit sht15_probe(struct platform_device *pdev)
 {
 	int ret = 0;
 	struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
+	u8 status = 0;
 
 	if (!data) {
 		ret = -ENOMEM;
@@ -588,6 +754,10 @@ static int __devinit sht15_probe(struct platform_device *pdev)
 	}
 	data->pdata = pdev->dev.platform_data;
 	data->supply_uV = data->pdata->supply_mv * 1000;
+	if (data->pdata->no_otp_reload)
+		status |= SHT15_STATUS_NO_OTP_RELOAD;
+	if (data->pdata->low_resolution)
+		status |= SHT15_STATUS_LOW_RESOLUTION;
 
 	/*
 	 * If a regulator is available,
@@ -646,6 +816,13 @@ static int __devinit sht15_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_release_irq;
 
+	/* write status with platform data options */
+	if (status) {
+		ret = sht15_send_status(data, status);
+		if (ret)
+			goto err_release_irq;
+	}
+
 	ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
 	if (ret) {
 		dev_err(&pdev->dev, "sysfs create failed\n");
@@ -689,6 +866,10 @@ static int __devexit sht15_remove(struct platform_device *pdev)
 	 * prevent new ones beginning
 	 */
 	mutex_lock(&data->read_lock);
+	if (sht15_soft_reset(data)) {
+		mutex_unlock(&data->read_lock);
+		return -EFAULT;
+	}
 	hwmon_device_unregister(data->hwmon_dev);
 	sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
 	if (!IS_ERR(data->reg)) {
diff --git a/include/linux/sht15.h b/include/linux/sht15.h
index 1e30214..d836ca6 100644
--- a/include/linux/sht15.h
+++ b/include/linux/sht15.h
@@ -19,10 +19,14 @@
  * @gpio_sck:		no. of gpio to which the data clock is connected.
  * @supply_mv:		supply voltage in mv. Overridden by regulator if
  *			available.
+ * @no_otp_reload:	flag to indicate no reload from OTP.
+ * @low_resolution:	flag to indicate the temp/humidity resolution to use.
  */
 struct sht15_platform_data {
 	int gpio_data;
 	int gpio_sck;
 	int supply_mv;
+	bool no_otp_reload;
+	bool low_resolution;
 };
 
-- 
1.7.1


_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors


[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux