hdc2010.c (8583B)
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 43struct 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 51enum hdc2010_addr_groups { 52 HDC2010_GROUP_TEMP = 0, 53 HDC2010_GROUP_HUMIDITY, 54}; 55 56struct hdc2010_reg_record { 57 unsigned long primary; 58 unsigned long peak; 59}; 60 61static 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 72static IIO_CONST_ATTR(out_current_heater_raw_available, "0 1"); 73 74static struct attribute *hdc2010_attributes[] = { 75 &iio_const_attr_out_current_heater_raw_available.dev_attr.attr, 76 NULL 77}; 78 79static const struct attribute_group hdc2010_attribute_group = { 80 .attrs = hdc2010_attributes, 81}; 82 83static 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 107static 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 123static 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 138static 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 153static int hdc2010_get_heater_status(struct hdc2010_data *data) 154{ 155 return !!(data->drdy_config & HDC2010_HEATER_EN); 156} 157 158static 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 ret = iio_device_claim_direct_mode(indio_dev); 173 if (ret) 174 return ret; 175 mutex_lock(&data->lock); 176 ret = hdc2010_get_prim_measurement_word(data, chan); 177 mutex_unlock(&data->lock); 178 iio_device_release_direct_mode(indio_dev); 179 if (ret < 0) 180 return ret; 181 *val = ret; 182 return IIO_VAL_INT; 183 } 184 case IIO_CHAN_INFO_PEAK: { 185 int ret; 186 187 ret = iio_device_claim_direct_mode(indio_dev); 188 if (ret) 189 return ret; 190 mutex_lock(&data->lock); 191 ret = hdc2010_get_peak_measurement_byte(data, chan); 192 mutex_unlock(&data->lock); 193 iio_device_release_direct_mode(indio_dev); 194 if (ret < 0) 195 return ret; 196 /* Scaling up the value so we can use same offset as RAW */ 197 *val = ret * 256; 198 return IIO_VAL_INT; 199 } 200 case IIO_CHAN_INFO_SCALE: 201 *val2 = 65536; 202 if (chan->type == IIO_TEMP) 203 *val = 165000; 204 else 205 *val = 100000; 206 return IIO_VAL_FRACTIONAL; 207 case IIO_CHAN_INFO_OFFSET: 208 *val = -15887; 209 *val2 = 515151; 210 return IIO_VAL_INT_PLUS_MICRO; 211 default: 212 return -EINVAL; 213 } 214} 215 216static int hdc2010_write_raw(struct iio_dev *indio_dev, 217 struct iio_chan_spec const *chan, 218 int val, int val2, long mask) 219{ 220 struct hdc2010_data *data = iio_priv(indio_dev); 221 int new, ret; 222 223 switch (mask) { 224 case IIO_CHAN_INFO_RAW: 225 if (chan->type != IIO_CURRENT || val2 != 0) 226 return -EINVAL; 227 228 switch (val) { 229 case 1: 230 new = HDC2010_HEATER_EN; 231 break; 232 case 0: 233 new = 0; 234 break; 235 default: 236 return -EINVAL; 237 } 238 239 mutex_lock(&data->lock); 240 ret = hdc2010_update_drdy_config(data, HDC2010_HEATER_EN, new); 241 mutex_unlock(&data->lock); 242 return ret; 243 default: 244 return -EINVAL; 245 } 246} 247 248static const struct iio_info hdc2010_info = { 249 .read_raw = hdc2010_read_raw, 250 .write_raw = hdc2010_write_raw, 251 .attrs = &hdc2010_attribute_group, 252}; 253 254static int hdc2010_probe(struct i2c_client *client, 255 const struct i2c_device_id *id) 256{ 257 struct iio_dev *indio_dev; 258 struct hdc2010_data *data; 259 u8 tmp; 260 int ret; 261 262 if (!i2c_check_functionality(client->adapter, 263 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) 264 return -EOPNOTSUPP; 265 266 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 267 if (!indio_dev) 268 return -ENOMEM; 269 270 data = iio_priv(indio_dev); 271 i2c_set_clientdata(client, indio_dev); 272 data->client = client; 273 mutex_init(&data->lock); 274 275 /* 276 * As DEVICE ID register does not differentiate between 277 * HDC2010 and HDC2080, we have the name hardcoded 278 */ 279 indio_dev->name = "hdc2010"; 280 indio_dev->modes = INDIO_DIRECT_MODE; 281 indio_dev->info = &hdc2010_info; 282 283 indio_dev->channels = hdc2010_channels; 284 indio_dev->num_channels = ARRAY_SIZE(hdc2010_channels); 285 286 /* Enable Automatic Measurement Mode at 5Hz */ 287 ret = hdc2010_update_drdy_config(data, HDC2010_AMM, HDC2010_AMM); 288 if (ret) 289 return ret; 290 291 /* 292 * We enable both temp and humidity measurement. 293 * However the measurement won't start even in AMM until triggered. 294 */ 295 tmp = (data->measurement_config & ~HDC2010_MEAS_CONF) | 296 HDC2010_MEAS_TRIG; 297 298 ret = i2c_smbus_write_byte_data(client, HDC2010_REG_MEASUREMENT_CONF, tmp); 299 if (ret) { 300 dev_warn(&client->dev, "Unable to set up measurement\n"); 301 if (hdc2010_update_drdy_config(data, HDC2010_AMM, 0)) 302 dev_warn(&client->dev, "Unable to restore default AMM\n"); 303 return ret; 304 } 305 306 data->measurement_config = tmp; 307 308 return iio_device_register(indio_dev); 309} 310 311static int hdc2010_remove(struct i2c_client *client) 312{ 313 struct iio_dev *indio_dev = i2c_get_clientdata(client); 314 struct hdc2010_data *data = iio_priv(indio_dev); 315 316 iio_device_unregister(indio_dev); 317 318 /* Disable Automatic Measurement Mode */ 319 if (hdc2010_update_drdy_config(data, HDC2010_AMM, 0)) 320 dev_warn(&client->dev, "Unable to restore default AMM\n"); 321 322 return 0; 323} 324 325static const struct i2c_device_id hdc2010_id[] = { 326 { "hdc2010" }, 327 { "hdc2080" }, 328 { } 329}; 330MODULE_DEVICE_TABLE(i2c, hdc2010_id); 331 332static const struct of_device_id hdc2010_dt_ids[] = { 333 { .compatible = "ti,hdc2010" }, 334 { .compatible = "ti,hdc2080" }, 335 { } 336}; 337MODULE_DEVICE_TABLE(of, hdc2010_dt_ids); 338 339static struct i2c_driver hdc2010_driver = { 340 .driver = { 341 .name = "hdc2010", 342 .of_match_table = hdc2010_dt_ids, 343 }, 344 .probe = hdc2010_probe, 345 .remove = hdc2010_remove, 346 .id_table = hdc2010_id, 347}; 348module_i2c_driver(hdc2010_driver); 349 350MODULE_AUTHOR("Eugene Zaikonnikov <ez@norphonic.com>"); 351MODULE_DESCRIPTION("TI HDC2010 humidity and temperature sensor driver"); 352MODULE_LICENSE("GPL");