Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: humidity: add HDC100x support

Add support for the HDC100x temperature and humidity sensors
including the resistive heater element.

Signed-off-by: Matt Ranostay <mranostay@gmail.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>

authored by

Matt Ranostay and committed by
Jonathan Cameron
4839367d 7db75fd6

+339
+9
Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x
··· 1 + What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw 2 + What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw_available 3 + KernelVersion: 4.3 4 + Contact: linux-iio@vger.kernel.org 5 + Description: 6 + Controls the heater device within the humidity sensor to get 7 + rid of excess condensation. 8 + 9 + Valid control values are 0 = OFF, and 1 = ON.
+10
drivers/iio/humidity/Kconfig
··· 12 12 Other sensors should work as well as long as they speak the 13 13 same protocol. 14 14 15 + config HDC100X 16 + tristate "TI HDC100x relative humidity and temperature sensor" 17 + depends on I2C 18 + help 19 + Say yes here to build support for the TI HDC100x series of 20 + relative humidity and temperature sensors. 21 + 22 + To compile this driver as a module, choose M here: the module 23 + will be called hdc100x. 24 + 15 25 config SI7005 16 26 tristate "SI7005 relative humidity and temperature sensor" 17 27 depends on I2C
+1
drivers/iio/humidity/Makefile
··· 3 3 # 4 4 5 5 obj-$(CONFIG_DHT11) += dht11.o 6 + obj-$(CONFIG_HDC100X) += hdc100x.o 6 7 obj-$(CONFIG_SI7005) += si7005.o 7 8 obj-$(CONFIG_SI7020) += si7020.o
+319
drivers/iio/humidity/hdc100x.c
··· 1 + /* 2 + * hdc100x.c - Support for the TI HDC100x temperature + humidity sensors 3 + * 4 + * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + */ 17 + 18 + #include <linux/delay.h> 19 + #include <linux/module.h> 20 + #include <linux/init.h> 21 + #include <linux/i2c.h> 22 + 23 + #include <linux/iio/iio.h> 24 + #include <linux/iio/sysfs.h> 25 + 26 + #define HDC100X_REG_TEMP 0x00 27 + #define HDC100X_REG_HUMIDITY 0x01 28 + 29 + #define HDC100X_REG_CONFIG 0x02 30 + #define HDC100X_REG_CONFIG_HEATER_EN BIT(13) 31 + 32 + struct hdc100x_data { 33 + struct i2c_client *client; 34 + struct mutex lock; 35 + u16 config; 36 + 37 + /* integration time of the sensor */ 38 + int adc_int_us[2]; 39 + }; 40 + 41 + /* integration time in us */ 42 + static const int hdc100x_int_time[][3] = { 43 + { 6350, 3650, 0 }, /* IIO_TEMP channel*/ 44 + { 6500, 3850, 2500 }, /* IIO_HUMIDITYRELATIVE channel */ 45 + }; 46 + 47 + /* HDC100X_REG_CONFIG shift and mask values */ 48 + static const struct { 49 + int shift; 50 + int mask; 51 + } hdc100x_resolution_shift[2] = { 52 + { /* IIO_TEMP channel */ 53 + .shift = 10, 54 + .mask = 1 55 + }, 56 + { /* IIO_HUMIDITYRELATIVE channel */ 57 + .shift = 8, 58 + .mask = 2, 59 + }, 60 + }; 61 + 62 + static IIO_CONST_ATTR(temp_integration_time_available, 63 + "0.00365 0.00635"); 64 + 65 + static IIO_CONST_ATTR(humidityrelative_integration_time_available, 66 + "0.0025 0.00385 0.0065"); 67 + 68 + static IIO_CONST_ATTR(out_current_heater_raw_available, 69 + "0 1"); 70 + 71 + static struct attribute *hdc100x_attributes[] = { 72 + &iio_const_attr_temp_integration_time_available.dev_attr.attr, 73 + &iio_const_attr_humidityrelative_integration_time_available.dev_attr.attr, 74 + &iio_const_attr_out_current_heater_raw_available.dev_attr.attr, 75 + NULL 76 + }; 77 + 78 + static struct attribute_group hdc100x_attribute_group = { 79 + .attrs = hdc100x_attributes, 80 + }; 81 + 82 + static const struct iio_chan_spec hdc100x_channels[] = { 83 + { 84 + .type = IIO_TEMP, 85 + .address = HDC100X_REG_TEMP, 86 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 87 + BIT(IIO_CHAN_INFO_SCALE) | 88 + BIT(IIO_CHAN_INFO_INT_TIME) | 89 + BIT(IIO_CHAN_INFO_OFFSET), 90 + }, 91 + { 92 + .type = IIO_HUMIDITYRELATIVE, 93 + .address = HDC100X_REG_HUMIDITY, 94 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 95 + BIT(IIO_CHAN_INFO_SCALE) | 96 + BIT(IIO_CHAN_INFO_INT_TIME) 97 + }, 98 + { 99 + .type = IIO_CURRENT, 100 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 101 + .extend_name = "heater", 102 + .output = 1, 103 + }, 104 + }; 105 + 106 + static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val) 107 + { 108 + int tmp = (~mask & data->config) | val; 109 + int ret; 110 + 111 + ret = i2c_smbus_write_word_swapped(data->client, 112 + HDC100X_REG_CONFIG, tmp); 113 + if (!ret) 114 + data->config = tmp; 115 + 116 + return ret; 117 + } 118 + 119 + static int hdc100x_set_it_time(struct hdc100x_data *data, int chan, int val2) 120 + { 121 + int shift = hdc100x_resolution_shift[chan].shift; 122 + int ret = -EINVAL; 123 + int i; 124 + 125 + for (i = 0; i < ARRAY_SIZE(hdc100x_int_time[chan]); i++) { 126 + if (val2 && val2 == hdc100x_int_time[chan][i]) { 127 + ret = hdc100x_update_config(data, 128 + hdc100x_resolution_shift[chan].mask << shift, 129 + i << shift); 130 + if (!ret) 131 + data->adc_int_us[chan] = val2; 132 + break; 133 + } 134 + } 135 + 136 + return ret; 137 + } 138 + 139 + static int hdc100x_get_measurement(struct hdc100x_data *data, 140 + struct iio_chan_spec const *chan) 141 + { 142 + struct i2c_client *client = data->client; 143 + int delay = data->adc_int_us[chan->address]; 144 + int ret; 145 + int val; 146 + 147 + /* start measurement */ 148 + ret = i2c_smbus_write_byte(client, chan->address); 149 + if (ret < 0) { 150 + dev_err(&client->dev, "cannot start measurement"); 151 + return ret; 152 + } 153 + 154 + /* wait for integration time to pass */ 155 + usleep_range(delay, delay + 1000); 156 + 157 + /* 158 + * i2c_smbus_read_word_data cannot() be used here due to the command 159 + * value not being understood and causes NAKs preventing any reading 160 + * from being accessed. 161 + */ 162 + ret = i2c_smbus_read_byte(client); 163 + if (ret < 0) { 164 + dev_err(&client->dev, "cannot read high byte measurement"); 165 + return ret; 166 + } 167 + val = ret << 6; 168 + 169 + ret = i2c_smbus_read_byte(client); 170 + if (ret < 0) { 171 + dev_err(&client->dev, "cannot read low byte measurement"); 172 + return ret; 173 + } 174 + val |= ret >> 2; 175 + 176 + return val; 177 + } 178 + 179 + static int hdc100x_get_heater_status(struct hdc100x_data *data) 180 + { 181 + return !!(data->config & HDC100X_REG_CONFIG_HEATER_EN); 182 + } 183 + 184 + static int hdc100x_read_raw(struct iio_dev *indio_dev, 185 + struct iio_chan_spec const *chan, int *val, 186 + int *val2, long mask) 187 + { 188 + struct hdc100x_data *data = iio_priv(indio_dev); 189 + 190 + switch (mask) { 191 + case IIO_CHAN_INFO_RAW: { 192 + int ret; 193 + 194 + mutex_lock(&data->lock); 195 + if (chan->type == IIO_CURRENT) { 196 + *val = hdc100x_get_heater_status(data); 197 + ret = IIO_VAL_INT; 198 + } else { 199 + ret = hdc100x_get_measurement(data, chan); 200 + if (ret >= 0) { 201 + *val = ret; 202 + ret = IIO_VAL_INT; 203 + } 204 + } 205 + mutex_unlock(&data->lock); 206 + return ret; 207 + } 208 + case IIO_CHAN_INFO_INT_TIME: 209 + *val = 0; 210 + *val2 = data->adc_int_us[chan->address]; 211 + return IIO_VAL_INT_PLUS_MICRO; 212 + case IIO_CHAN_INFO_SCALE: 213 + if (chan->type == IIO_TEMP) { 214 + *val = 165; 215 + *val2 = 65536 >> 2; 216 + return IIO_VAL_FRACTIONAL; 217 + } else { 218 + *val = 0; 219 + *val2 = 10000; 220 + return IIO_VAL_INT_PLUS_MICRO; 221 + } 222 + break; 223 + case IIO_CHAN_INFO_OFFSET: 224 + *val = -40; 225 + return IIO_VAL_INT; 226 + default: 227 + return -EINVAL; 228 + } 229 + } 230 + 231 + static int hdc100x_write_raw(struct iio_dev *indio_dev, 232 + struct iio_chan_spec const *chan, 233 + int val, int val2, long mask) 234 + { 235 + struct hdc100x_data *data = iio_priv(indio_dev); 236 + int ret = -EINVAL; 237 + 238 + switch (mask) { 239 + case IIO_CHAN_INFO_INT_TIME: 240 + if (val != 0) 241 + return -EINVAL; 242 + 243 + mutex_lock(&data->lock); 244 + ret = hdc100x_set_it_time(data, chan->address, val2); 245 + mutex_unlock(&data->lock); 246 + return ret; 247 + case IIO_CHAN_INFO_RAW: 248 + if (chan->type != IIO_CURRENT || val2 != 0) 249 + return -EINVAL; 250 + 251 + mutex_lock(&data->lock); 252 + ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN, 253 + val ? HDC100X_REG_CONFIG_HEATER_EN : 0); 254 + mutex_unlock(&data->lock); 255 + return ret; 256 + default: 257 + return -EINVAL; 258 + } 259 + } 260 + 261 + static const struct iio_info hdc100x_info = { 262 + .read_raw = hdc100x_read_raw, 263 + .write_raw = hdc100x_write_raw, 264 + .attrs = &hdc100x_attribute_group, 265 + .driver_module = THIS_MODULE, 266 + }; 267 + 268 + static int hdc100x_probe(struct i2c_client *client, 269 + const struct i2c_device_id *id) 270 + { 271 + struct iio_dev *indio_dev; 272 + struct hdc100x_data *data; 273 + 274 + if (!i2c_check_functionality(client->adapter, 275 + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) 276 + return -ENODEV; 277 + 278 + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 279 + if (!indio_dev) 280 + return -ENOMEM; 281 + 282 + data = iio_priv(indio_dev); 283 + i2c_set_clientdata(client, indio_dev); 284 + data->client = client; 285 + mutex_init(&data->lock); 286 + 287 + indio_dev->dev.parent = &client->dev; 288 + indio_dev->name = dev_name(&client->dev); 289 + indio_dev->modes = INDIO_DIRECT_MODE; 290 + indio_dev->info = &hdc100x_info; 291 + 292 + indio_dev->channels = hdc100x_channels; 293 + indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels); 294 + 295 + /* be sure we are in a known state */ 296 + hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]); 297 + hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]); 298 + 299 + return devm_iio_device_register(&client->dev, indio_dev); 300 + } 301 + 302 + static const struct i2c_device_id hdc100x_id[] = { 303 + { "hdc100x", 0 }, 304 + { } 305 + }; 306 + MODULE_DEVICE_TABLE(i2c, hdc100x_id); 307 + 308 + static struct i2c_driver hdc100x_driver = { 309 + .driver = { 310 + .name = "hdc100x", 311 + }, 312 + .probe = hdc100x_probe, 313 + .id_table = hdc100x_id, 314 + }; 315 + module_i2c_driver(hdc100x_driver); 316 + 317 + MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>"); 318 + MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver"); 319 + MODULE_LICENSE("GPL");