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