From 99a0378de9f887fd4d501f1baa50aaf16d01a8e8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 12 Apr 2011 15:34:36 -0400 Subject: hwmon: (sht15) general code clean-up * Add a documentation file for the device. * Respect a bit more the kernel-doc syntax. * Rename some variables for clarity. * Use bool type for flags. * Use an enum for states (actions being done). Signed-off-by: Vivien Didelot Acked-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- include/linux/sht15.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sht15.h b/include/linux/sht15.h index 046bce05ecab..1e302146bdc8 100644 --- a/include/linux/sht15.h +++ b/include/linux/sht15.h @@ -8,14 +8,18 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * For further information, see the Documentation/hwmon/sht15 file. */ /** * struct sht15_platform_data - sht15 connectivity info - * @gpio_data: no. of gpio to which bidirectional data line is connected - * @gpio_sck: no. of gpio to which the data clock is connected. - * @supply_mv: supply voltage in mv. Overridden by regulator if available. - **/ + * @gpio_data: no. of gpio to which bidirectional data line is + * connected. + * @gpio_sck: no. of gpio to which the data clock is connected. + * @supply_mv: supply voltage in mv. Overridden by regulator if + * available. + */ struct sht15_platform_data { int gpio_data; int gpio_sck; -- cgit v1.2.3 From cc15c7ebb424e45ba2c5ceecbe52d025219ee970 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 12 Apr 2011 15:34:38 -0400 Subject: hwmon: (sht15) add support for the status register * 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 Acked-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- Documentation/hwmon/sht15 | 29 +++++- drivers/hwmon/sht15.c | 247 +++++++++++++++++++++++++++++++++++++++------- include/linux/sht15.h | 4 + 3 files changed, 245 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/Documentation/hwmon/sht15 b/Documentation/hwmon/sht15 index 2919c516fdbc..d1939b25eb16 100644 --- a/Documentation/hwmon/sht15 +++ b/Documentation/hwmon/sht15 @@ -4,6 +4,7 @@ Kernel driver sht15 Authors: * Wouter Horre * Jonathan Cameron + * Vivien Didelot Supported chips: * Sensirion SHT10 @@ -30,13 +31,37 @@ 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 3182b3f578e2..49da089b5de9 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 + * * 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,10 +261,105 @@ 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 @@ -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,18 +466,85 @@ 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. @@ -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 1e302146bdc8..d836ca617323 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; }; -- cgit v1.2.3 From 82c7465b4dd013d19858bfeac084849ae17682c0 Mon Sep 17 00:00:00 2001 From: Jerome Oufella Date: Tue, 12 Apr 2011 15:34:39 -0400 Subject: hwmon: (sht15) add support for CRC validation The sht15 sensor allows validating exchanges to and from the device using a crc8 function. An utility function to reverse a byte has also been added. Signed-off-by: Jerome Oufella Acked-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- Documentation/hwmon/sht15 | 9 ++- drivers/hwmon/sht15.c | 192 +++++++++++++++++++++++++++++++++++++++++----- include/linux/sht15.h | 2 + 3 files changed, 183 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/Documentation/hwmon/sht15 b/Documentation/hwmon/sht15 index d1939b25eb16..02850bdfac18 100644 --- a/Documentation/hwmon/sht15 +++ b/Documentation/hwmon/sht15 @@ -5,6 +5,7 @@ Authors: * Wouter Horre * Jonathan Cameron * Vivien Didelot + * Jerome Oufella Supported chips: * Sensirion SHT10 @@ -44,11 +45,17 @@ 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". +Notes: + * The regulator supply name is set to "vcc". + * If a CRC validation fails, a soft reset command is sent, which resets + status register to its hardware default value, but the driver will try to + restore the previous device configuration. Platform data ------------- +* checksum: + set it to true to enable CRC validation of the readings (default to false). * no_otp_reload: flag to indicate not to reload from OTP (default to false). * low_resolution: diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 49da089b5de9..cf4330b352ef 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -2,6 +2,7 @@ * sht15.c - support for the SHT15 Temperature and Humidity Sensor * * Portions Copyright (c) 2010-2011 Savoir-faire Linux Inc. + * Jerome Oufella * Vivien Didelot * * Copyright (c) 2009 Jonathan Cameron @@ -78,6 +79,42 @@ static const struct sht15_temppair temppoints[] = { { 5000000, -40100 }, }; +/* Table from CRC datasheet, section 2.4 */ +static const u8 sht15_crc8_table[] = { + 0, 49, 98, 83, 196, 245, 166, 151, + 185, 136, 219, 234, 125, 76, 31, 46, + 67, 114, 33, 16, 135, 182, 229, 212, + 250, 203, 152, 169, 62, 15, 92, 109, + 134, 183, 228, 213, 66, 115, 32, 17, + 63, 14, 93, 108, 251, 202, 153, 168, + 197, 244, 167, 150, 1, 48, 99, 82, + 124, 77, 30, 47, 184, 137, 218, 235, + 61, 12, 95, 110, 249, 200, 155, 170, + 132, 181, 230, 215, 64, 113, 34, 19, + 126, 79, 28, 45, 186, 139, 216, 233, + 199, 246, 165, 148, 3, 50, 97, 80, + 187, 138, 217, 232, 127, 78, 29, 44, + 2, 51, 96, 81, 198, 247, 164, 149, + 248, 201, 154, 171, 60, 13, 94, 111, + 65, 112, 35, 18, 133, 180, 231, 214, + 122, 75, 24, 41, 190, 143, 220, 237, + 195, 242, 161, 144, 7, 54, 101, 84, + 57, 8, 91, 106, 253, 204, 159, 174, + 128, 177, 226, 211, 68, 117, 38, 23, + 252, 205, 158, 175, 56, 9, 90, 107, + 69, 116, 39, 22, 129, 176, 227, 210, + 191, 142, 221, 236, 123, 74, 25, 40, + 6, 55, 100, 85, 194, 243, 160, 145, + 71, 118, 37, 20, 131, 178, 225, 208, + 254, 207, 156, 173, 58, 11, 88, 105, + 4, 53, 102, 87, 192, 241, 162, 147, + 189, 140, 223, 238, 121, 72, 27, 42, + 193, 240, 163, 146, 5, 52, 103, 86, + 120, 73, 26, 43, 188, 141, 222, 239, + 130, 179, 224, 209, 70, 119, 36, 21, + 59, 10, 89, 104, 255, 206, 157, 172 +}; + /** * struct sht15_data - device instance specific data * @pdata: platform data (gpio's etc). @@ -86,6 +123,8 @@ static const struct sht15_temppair temppoints[] = { * @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. + * @checksum_ok: last value read from the device passed CRC validation. + * @checksumming: flag used to enable the data validation with CRC. * @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). @@ -112,6 +151,8 @@ struct sht15_data { uint16_t val_temp; uint16_t val_humid; u8 val_status; + bool checksum_ok; + bool checksumming; enum sht15_state state; bool measurements_valid; bool status_valid; @@ -128,6 +169,40 @@ struct sht15_data { atomic_t interrupt_handled; }; +/** + * sht15_reverse() - reverse a byte + * @byte: byte to reverse. + */ +static u8 sht15_reverse(u8 byte) +{ + u8 i, c; + + for (c = 0, i = 0; i < 8; i++) + c |= (!!(byte & (1 << i))) << (7 - i); + return c; +} + +/** + * sht15_crc8() - compute crc8 + * @data: sht15 specific data. + * @value: sht15 retrieved data. + * + * This implements section 2 of the CRC datasheet. + */ +static u8 sht15_crc8(struct sht15_data *data, + const u8 *value, + int len) +{ + u8 crc = sht15_reverse(data->val_status & 0x0F); + + while (len--) { + crc = sht15_crc8_table[*value ^ crc]; + value++; + } + + return crc; +} + /** * sht15_connection_reset() - reset the comms interface * @data: sht15 specific data @@ -267,6 +342,26 @@ static int sht15_soft_reset(struct sht15_data *data) return ret; } +/** + * sht15_ack() - send a ack + * @data: sht15 specific data. + * + * Each byte of data is acknowledged by pulling the data line + * low for one clock pulse. + */ +static void sht15_ack(struct sht15_data *data) +{ + gpio_direction_output(data->pdata->gpio_data, 0); + ndelay(SHT15_TSU); + gpio_set_value(data->pdata->gpio_sck, 1); + ndelay(SHT15_TSU); + gpio_set_value(data->pdata->gpio_sck, 0); + ndelay(SHT15_TSU); + gpio_set_value(data->pdata->gpio_data, 1); + + gpio_direction_input(data->pdata->gpio_data); +} + /** * sht15_end_transmission() - notify device of end of transmission * @data: device state. @@ -338,6 +433,9 @@ static int sht15_update_status(struct sht15_data *data) { int ret = 0; u8 status; + u8 previous_config; + u8 dev_checksum = 0; + u8 checksum_vals[2]; int timeout = HZ; mutex_lock(&data->read_lock); @@ -348,8 +446,40 @@ static int sht15_update_status(struct sht15_data *data) goto error_ret; status = sht15_read_byte(data); + if (data->checksumming) { + sht15_ack(data); + dev_checksum = sht15_reverse(sht15_read_byte(data)); + checksum_vals[0] = SHT15_READ_STATUS; + checksum_vals[1] = status; + data->checksum_ok = (sht15_crc8(data, checksum_vals, 2) + == dev_checksum); + } + sht15_end_transmission(data); + /* + * Perform checksum validation on the received data. + * Specification mentions that in case a checksum verification + * fails, a soft reset command must be sent to the device. + */ + if (data->checksumming && !data->checksum_ok) { + previous_config = data->val_status & 0x07; + ret = sht15_soft_reset(data); + if (ret) + goto error_ret; + if (previous_config) { + ret = sht15_send_status(data, previous_config); + if (ret) { + dev_err(data->dev, + "CRC validation failed, unable " + "to restore device settings\n"); + goto error_ret; + } + } + ret = -EAGAIN; + goto error_ret; + } + data->val_status = status; data->status_valid = true; data->last_status = jiffies; @@ -372,6 +502,7 @@ static int sht15_measurement(struct sht15_data *data, int timeout_msecs) { int ret; + u8 previous_config; ret = sht15_send_cmd(data, command); if (ret) @@ -395,6 +526,29 @@ static int sht15_measurement(struct sht15_data *data, sht15_connection_reset(data); return -ETIME; } + + /* + * Perform checksum validation on the received data. + * Specification mentions that in case a checksum verification fails, + * a soft reset command must be sent to the device. + */ + if (data->checksumming && !data->checksum_ok) { + previous_config = data->val_status & 0x07; + ret = sht15_soft_reset(data); + if (ret) + return ret; + if (previous_config) { + ret = sht15_send_status(data, previous_config); + if (ret) { + dev_err(data->dev, + "CRC validation failed, unable " + "to restore device settings\n"); + return ret; + } + } + return -EAGAIN; + } + return 0; } @@ -635,28 +789,11 @@ static irqreturn_t sht15_interrupt_fired(int irq, void *d) return IRQ_HANDLED; } -/** - * sht15_ack() - Send an ack to the device - * - * Each byte of data is acknowledged by pulling the data line - * low for one clock pulse. - */ -static void sht15_ack(struct sht15_data *data) -{ - gpio_direction_output(data->pdata->gpio_data, 0); - ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 1); - ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 0); - ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_data, 1); - - gpio_direction_input(data->pdata->gpio_data); -} - static void sht15_bh_read_data(struct work_struct *work_s) { uint16_t val = 0; + u8 dev_checksum = 0; + u8 checksum_vals[3]; struct sht15_data *data = container_of(work_s, struct sht15_data, read_work); @@ -681,6 +818,21 @@ static void sht15_bh_read_data(struct work_struct *work_s) sht15_ack(data); val |= sht15_read_byte(data); + if (data->checksumming) { + /* + * Ask the device for a checksum and read it back. + * Note: the device sends the checksum byte reversed. + */ + sht15_ack(data); + dev_checksum = sht15_reverse(sht15_read_byte(data)); + checksum_vals[0] = (data->state == SHT15_READING_TEMP) ? + SHT15_MEASURE_TEMP : SHT15_MEASURE_RH; + checksum_vals[1] = (u8) (val >> 8); + checksum_vals[2] = (u8) val; + data->checksum_ok + = (sht15_crc8(data, checksum_vals, 3) == dev_checksum); + } + /* Tell the device we are done */ sht15_end_transmission(data); @@ -754,6 +906,8 @@ 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->checksum) + data->checksumming = true; if (data->pdata->no_otp_reload) status |= SHT15_STATUS_NO_OTP_RELOAD; if (data->pdata->low_resolution) diff --git a/include/linux/sht15.h b/include/linux/sht15.h index d836ca617323..f85c7c523da0 100644 --- a/include/linux/sht15.h +++ b/include/linux/sht15.h @@ -19,6 +19,7 @@ * @gpio_sck: no. of gpio to which the data clock is connected. * @supply_mv: supply voltage in mv. Overridden by regulator if * available. + * @checksum: flag to indicate the checksum should be validated. * @no_otp_reload: flag to indicate no reload from OTP. * @low_resolution: flag to indicate the temp/humidity resolution to use. */ @@ -26,6 +27,7 @@ struct sht15_platform_data { int gpio_data; int gpio_sck; int supply_mv; + bool checksum; bool no_otp_reload; bool low_resolution; }; -- cgit v1.2.3