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

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

 



On 04/12/11 20:34, Vivien Didelot wrote:
> * 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>
Acked-by: Jonathan Cameron <jic23@xxxxxxxxx>
> ---
>  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;
>  };
>  


_______________________________________________
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