1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * hdc2010.c - Support for the TI HDC2010 and HDC2080 4 * temperature + relative humidity sensors 5 * 6 * Copyright (C) 2020 Norphonic AS 7 * Author: Eugene Zaikonnikov <ez@norphonic.com> 8 * 9 * Datasheet: https://www.ti.com/product/HDC2010/datasheet 10 * Datasheet: https://www.ti.com/product/HDC2080/datasheet 11 */ 12 13 #include <linux/module.h> 14 #include <linux/init.h> 15 #include <linux/i2c.h> 16 #include <linux/bitops.h> 17 18 #include <linux/iio/iio.h> 19 #include <linux/iio/sysfs.h> 20 21 #define HDC2010_REG_TEMP_LOW 0x00 22 #define HDC2010_REG_TEMP_HIGH 0x01 23 #define HDC2010_REG_HUMIDITY_LOW 0x02 24 #define HDC2010_REG_HUMIDITY_HIGH 0x03 25 #define HDC2010_REG_INTERRUPT_DRDY 0x04 26 #define HDC2010_REG_TEMP_MAX 0x05 27 #define HDC2010_REG_HUMIDITY_MAX 0x06 28 #define HDC2010_REG_INTERRUPT_EN 0x07 29 #define HDC2010_REG_TEMP_OFFSET_ADJ 0x08 30 #define HDC2010_REG_HUMIDITY_OFFSET_ADJ 0x09 31 #define HDC2010_REG_TEMP_THR_L 0x0a 32 #define HDC2010_REG_TEMP_THR_H 0x0b 33 #define HDC2010_REG_RH_THR_L 0x0c 34 #define HDC2010_REG_RH_THR_H 0x0d 35 #define HDC2010_REG_RESET_DRDY_INT_CONF 0x0e 36 #define HDC2010_REG_MEASUREMENT_CONF 0x0f 37 38 #define HDC2010_MEAS_CONF GENMASK(2, 1) 39 #define HDC2010_MEAS_TRIG BIT(0) 40 #define HDC2010_HEATER_EN BIT(3) 41 #define HDC2010_AMM GENMASK(6, 4) 42 43 struct hdc2010_data { 44 struct i2c_client *client; 45 struct mutex lock; 46 u8 measurement_config; 47 u8 interrupt_config; 48 u8 drdy_config; 49 }; 50 51 enum hdc2010_addr_groups { 52 HDC2010_GROUP_TEMP = 0, 53 HDC2010_GROUP_HUMIDITY, 54 }; 55 56 struct hdc2010_reg_record { 57 unsigned long primary; 58 unsigned long peak; 59 }; 60 61 static const struct hdc2010_reg_record hdc2010_reg_translation[] = { 62 [HDC2010_GROUP_TEMP] = { 63 .primary = HDC2010_REG_TEMP_LOW, 64 .peak = HDC2010_REG_TEMP_MAX, 65 }, 66 [HDC2010_GROUP_HUMIDITY] = { 67 .primary = HDC2010_REG_HUMIDITY_LOW, 68 .peak = HDC2010_REG_HUMIDITY_MAX, 69 }, 70 }; 71 72 static IIO_CONST_ATTR(out_current_heater_raw_available, "0 1"); 73 74 static struct attribute *hdc2010_attributes[] = { 75 &iio_const_attr_out_current_heater_raw_available.dev_attr.attr, 76 NULL 77 }; 78 79 static const struct attribute_group hdc2010_attribute_group = { 80 .attrs = hdc2010_attributes, 81 }; 82 83 static const struct iio_chan_spec hdc2010_channels[] = { 84 { 85 .type = IIO_TEMP, 86 .address = HDC2010_GROUP_TEMP, 87 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 88 BIT(IIO_CHAN_INFO_PEAK) | 89 BIT(IIO_CHAN_INFO_OFFSET) | 90 BIT(IIO_CHAN_INFO_SCALE), 91 }, 92 { 93 .type = IIO_HUMIDITYRELATIVE, 94 .address = HDC2010_GROUP_HUMIDITY, 95 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 96 BIT(IIO_CHAN_INFO_PEAK) | 97 BIT(IIO_CHAN_INFO_SCALE), 98 }, 99 { 100 .type = IIO_CURRENT, 101 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 102 .extend_name = "heater", 103 .output = 1, 104 }, 105 }; 106 107 static int hdc2010_update_drdy_config(struct hdc2010_data *data, 108 char mask, char val) 109 { 110 u8 tmp = (~mask & data->drdy_config) | val; 111 int ret; 112 113 ret = i2c_smbus_write_byte_data(data->client, 114 HDC2010_REG_RESET_DRDY_INT_CONF, tmp); 115 if (ret) 116 return ret; 117 118 data->drdy_config = tmp; 119 120 return 0; 121 } 122 123 static int hdc2010_get_prim_measurement_word(struct hdc2010_data *data, 124 struct iio_chan_spec const *chan) 125 { 126 struct i2c_client *client = data->client; 127 s32 ret; 128 129 ret = i2c_smbus_read_word_data(client, 130 hdc2010_reg_translation[chan->address].primary); 131 132 if (ret < 0) 133 dev_err(&client->dev, "Could not read sensor measurement word\n"); 134 135 return ret; 136 } 137 138 static int hdc2010_get_peak_measurement_byte(struct hdc2010_data *data, 139 struct iio_chan_spec const *chan) 140 { 141 struct i2c_client *client = data->client; 142 s32 ret; 143 144 ret = i2c_smbus_read_byte_data(client, 145 hdc2010_reg_translation[chan->address].peak); 146 147 if (ret < 0) 148 dev_err(&client->dev, "Could not read sensor measurement byte\n"); 149 150 return ret; 151 } 152 153 static int hdc2010_get_heater_status(struct hdc2010_data *data) 154 { 155 return !!(data->drdy_config & HDC2010_HEATER_EN); 156 } 157 158 static int hdc2010_read_raw(struct iio_dev *indio_dev, 159 struct iio_chan_spec const *chan, int *val, 160 int *val2, long mask) 161 { 162 struct hdc2010_data *data = iio_priv(indio_dev); 163 164 switch (mask) { 165 case IIO_CHAN_INFO_RAW: { 166 int ret; 167 168 if (chan->type == IIO_CURRENT) { 169 *val = hdc2010_get_heater_status(data); 170 return IIO_VAL_INT; 171 } 172 if (!iio_device_claim_direct(indio_dev)) 173 return -EBUSY; 174 mutex_lock(&data->lock); 175 ret = hdc2010_get_prim_measurement_word(data, chan); 176 mutex_unlock(&data->lock); 177 iio_device_release_direct(indio_dev); 178 if (ret < 0) 179 return ret; 180 *val = ret; 181 return IIO_VAL_INT; 182 } 183 case IIO_CHAN_INFO_PEAK: { 184 int ret; 185 186 if (!iio_device_claim_direct(indio_dev)) 187 return -EBUSY; 188 mutex_lock(&data->lock); 189 ret = hdc2010_get_peak_measurement_byte(data, chan); 190 mutex_unlock(&data->lock); 191 iio_device_release_direct(indio_dev); 192 if (ret < 0) 193 return ret; 194 /* Scaling up the value so we can use same offset as RAW */ 195 *val = ret * 256; 196 return IIO_VAL_INT; 197 } 198 case IIO_CHAN_INFO_SCALE: 199 *val2 = 65536; 200 if (chan->type == IIO_TEMP) 201 *val = 165000; 202 else 203 *val = 100000; 204 return IIO_VAL_FRACTIONAL; 205 case IIO_CHAN_INFO_OFFSET: 206 *val = -15887; 207 *val2 = 515151; 208 return IIO_VAL_INT_PLUS_MICRO; 209 default: 210 return -EINVAL; 211 } 212 } 213 214 static int hdc2010_write_raw(struct iio_dev *indio_dev, 215 struct iio_chan_spec const *chan, 216 int val, int val2, long mask) 217 { 218 struct hdc2010_data *data = iio_priv(indio_dev); 219 int new, ret; 220 221 switch (mask) { 222 case IIO_CHAN_INFO_RAW: 223 if (chan->type != IIO_CURRENT || val2 != 0) 224 return -EINVAL; 225 226 switch (val) { 227 case 1: 228 new = HDC2010_HEATER_EN; 229 break; 230 case 0: 231 new = 0; 232 break; 233 default: 234 return -EINVAL; 235 } 236 237 mutex_lock(&data->lock); 238 ret = hdc2010_update_drdy_config(data, HDC2010_HEATER_EN, new); 239 mutex_unlock(&data->lock); 240 return ret; 241 default: 242 return -EINVAL; 243 } 244 } 245 246 static const struct iio_info hdc2010_info = { 247 .read_raw = hdc2010_read_raw, 248 .write_raw = hdc2010_write_raw, 249 .attrs = &hdc2010_attribute_group, 250 }; 251 252 static int hdc2010_probe(struct i2c_client *client) 253 { 254 struct iio_dev *indio_dev; 255 struct hdc2010_data *data; 256 u8 tmp; 257 int ret; 258 259 if (!i2c_check_functionality(client->adapter, 260 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) 261 return -EOPNOTSUPP; 262 263 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 264 if (!indio_dev) 265 return -ENOMEM; 266 267 data = iio_priv(indio_dev); 268 i2c_set_clientdata(client, indio_dev); 269 data->client = client; 270 mutex_init(&data->lock); 271 272 /* 273 * As DEVICE ID register does not differentiate between 274 * HDC2010 and HDC2080, we have the name hardcoded 275 */ 276 indio_dev->name = "hdc2010"; 277 indio_dev->modes = INDIO_DIRECT_MODE; 278 indio_dev->info = &hdc2010_info; 279 280 indio_dev->channels = hdc2010_channels; 281 indio_dev->num_channels = ARRAY_SIZE(hdc2010_channels); 282 283 /* Enable Automatic Measurement Mode at 5Hz */ 284 ret = hdc2010_update_drdy_config(data, HDC2010_AMM, HDC2010_AMM); 285 if (ret) 286 return ret; 287 288 /* 289 * We enable both temp and humidity measurement. 290 * However the measurement won't start even in AMM until triggered. 291 */ 292 tmp = (data->measurement_config & ~HDC2010_MEAS_CONF) | 293 HDC2010_MEAS_TRIG; 294 295 ret = i2c_smbus_write_byte_data(client, HDC2010_REG_MEASUREMENT_CONF, tmp); 296 if (ret) { 297 dev_warn(&client->dev, "Unable to set up measurement\n"); 298 if (hdc2010_update_drdy_config(data, HDC2010_AMM, 0)) 299 dev_warn(&client->dev, "Unable to restore default AMM\n"); 300 return ret; 301 } 302 303 data->measurement_config = tmp; 304 305 return iio_device_register(indio_dev); 306 } 307 308 static void hdc2010_remove(struct i2c_client *client) 309 { 310 struct iio_dev *indio_dev = i2c_get_clientdata(client); 311 struct hdc2010_data *data = iio_priv(indio_dev); 312 313 iio_device_unregister(indio_dev); 314 315 /* Disable Automatic Measurement Mode */ 316 if (hdc2010_update_drdy_config(data, HDC2010_AMM, 0)) 317 dev_warn(&client->dev, "Unable to restore default AMM\n"); 318 } 319 320 static const struct i2c_device_id hdc2010_id[] = { 321 { "hdc2010" }, 322 { "hdc2080" }, 323 { } 324 }; 325 MODULE_DEVICE_TABLE(i2c, hdc2010_id); 326 327 static const struct of_device_id hdc2010_dt_ids[] = { 328 { .compatible = "ti,hdc2010" }, 329 { .compatible = "ti,hdc2080" }, 330 { } 331 }; 332 MODULE_DEVICE_TABLE(of, hdc2010_dt_ids); 333 334 static struct i2c_driver hdc2010_driver = { 335 .driver = { 336 .name = "hdc2010", 337 .of_match_table = hdc2010_dt_ids, 338 }, 339 .probe = hdc2010_probe, 340 .remove = hdc2010_remove, 341 .id_table = hdc2010_id, 342 }; 343 module_i2c_driver(hdc2010_driver); 344 345 MODULE_AUTHOR("Eugene Zaikonnikov <ez@norphonic.com>"); 346 MODULE_DESCRIPTION("TI HDC2010 humidity and temperature sensor driver"); 347 MODULE_LICENSE("GPL"); 348