1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors 4 * Copyright (c) 2013,2014 Uplogix, Inc. 5 * David Barksdale <dbarksdale@uplogix.com> 6 */ 7 8 /* 9 * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors 10 * are i2c devices which have an identical programming interface for 11 * measuring relative humidity and temperature. The Si7013 has an additional 12 * temperature input which this driver does not support. 13 * 14 * Data Sheets: 15 * Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf 16 * Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf 17 * Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf 18 */ 19 20 #include <linux/delay.h> 21 #include <linux/i2c.h> 22 #include <linux/module.h> 23 #include <linux/mod_devicetable.h> 24 #include <linux/slab.h> 25 #include <linux/sysfs.h> 26 #include <linux/stat.h> 27 28 #include <linux/iio/iio.h> 29 #include <linux/iio/sysfs.h> 30 31 /* Measure Relative Humidity, Hold Master Mode */ 32 #define SI7020CMD_RH_HOLD 0xE5 33 /* Measure Temperature, Hold Master Mode */ 34 #define SI7020CMD_TEMP_HOLD 0xE3 35 /* Software Reset */ 36 #define SI7020CMD_RESET 0xFE 37 #define SI7020CMD_USR_WRITE 0xE6 38 /* "Heater Enabled" bit in the User Register */ 39 #define SI7020_USR_HEATER_EN BIT(2) 40 #define SI7020CMD_HEATER_WRITE 0x51 41 /* Heater current configuration bits */ 42 #define SI7020_HEATER_VAL GENMASK(3, 0) 43 44 struct si7020_data { 45 struct i2c_client *client; 46 /* Lock for cached register values */ 47 struct mutex lock; 48 u8 user_reg; 49 u8 heater_reg; 50 }; 51 52 static const int si7020_heater_vals[] = { 0, 1, 0xF }; 53 54 static int si7020_read_raw(struct iio_dev *indio_dev, 55 struct iio_chan_spec const *chan, int *val, 56 int *val2, long mask) 57 { 58 struct si7020_data *data = iio_priv(indio_dev); 59 int ret; 60 61 switch (mask) { 62 case IIO_CHAN_INFO_RAW: 63 if (chan->type == IIO_CURRENT) { 64 *val = data->heater_reg; 65 return IIO_VAL_INT; 66 } 67 68 ret = i2c_smbus_read_word_swapped(data->client, 69 chan->type == IIO_TEMP ? 70 SI7020CMD_TEMP_HOLD : 71 SI7020CMD_RH_HOLD); 72 if (ret < 0) 73 return ret; 74 *val = ret >> 2; 75 /* 76 * Humidity values can slightly exceed the 0-100%RH 77 * range and should be corrected by software 78 */ 79 if (chan->type == IIO_HUMIDITYRELATIVE) 80 *val = clamp_val(*val, 786, 13893); 81 return IIO_VAL_INT; 82 case IIO_CHAN_INFO_SCALE: 83 if (chan->type == IIO_TEMP) 84 *val = 175720; /* = 175.72 * 1000 */ 85 else 86 *val = 125 * 1000; 87 *val2 = 65536 >> 2; 88 return IIO_VAL_FRACTIONAL; 89 case IIO_CHAN_INFO_OFFSET: 90 /* 91 * Since iio_convert_raw_to_processed_unlocked assumes offset 92 * is an integer we have to round these values and lose 93 * accuracy. 94 * Relative humidity will be 0.0032959% too high and 95 * temperature will be 0.00277344 degrees too high. 96 * This is no big deal because it's within the accuracy of the 97 * sensor. 98 */ 99 if (chan->type == IIO_TEMP) 100 *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ 101 else 102 *val = -786; /* = -6 * (65536 >> 2) / 125 */ 103 return IIO_VAL_INT; 104 default: 105 break; 106 } 107 108 return -EINVAL; 109 } 110 111 static const struct iio_chan_spec si7020_channels[] = { 112 { 113 .type = IIO_HUMIDITYRELATIVE, 114 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 115 BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 116 }, 117 { 118 .type = IIO_TEMP, 119 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 120 BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 121 }, 122 { 123 .type = IIO_CURRENT, 124 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 125 .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), 126 .extend_name = "heater", 127 } 128 }; 129 130 static int si7020_update_reg(struct si7020_data *data, 131 u8 *reg, u8 cmd, u8 mask, u8 val) 132 { 133 u8 new = (*reg & ~mask) | val; 134 int ret; 135 136 ret = i2c_smbus_write_byte_data(data->client, cmd, new); 137 if (ret) 138 return ret; 139 140 *reg = new; 141 142 return 0; 143 } 144 145 static int si7020_write_raw(struct iio_dev *indio_dev, 146 struct iio_chan_spec const *chan, 147 int val, int val2, long mask) 148 { 149 struct si7020_data *data = iio_priv(indio_dev); 150 int ret; 151 152 switch (mask) { 153 case IIO_CHAN_INFO_RAW: 154 if (chan->type != IIO_CURRENT || val2 != 0 || 155 val < si7020_heater_vals[0] || val > si7020_heater_vals[2]) 156 return -EINVAL; 157 158 scoped_guard(mutex, &data->lock) 159 ret = si7020_update_reg(data, &data->heater_reg, 160 SI7020CMD_HEATER_WRITE, SI7020_HEATER_VAL, val); 161 return ret; 162 default: 163 return -EINVAL; 164 } 165 } 166 167 static int si7020_read_available(struct iio_dev *indio_dev, 168 struct iio_chan_spec const *chan, 169 const int **vals, 170 int *type, int *length, long mask) 171 { 172 if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_CURRENT) 173 return -EINVAL; 174 175 *vals = si7020_heater_vals; 176 *type = IIO_VAL_INT; 177 178 return IIO_AVAIL_RANGE; 179 } 180 181 static ssize_t si7020_show_heater_en(struct device *dev, 182 struct device_attribute *attr, char *buf) 183 { 184 struct iio_dev *indio_dev = dev_to_iio_dev(dev); 185 struct si7020_data *data = iio_priv(indio_dev); 186 187 return sysfs_emit(buf, "%d\n", !!(data->user_reg & SI7020_USR_HEATER_EN)); 188 } 189 190 static ssize_t si7020_store_heater_en(struct device *dev, 191 struct device_attribute *attr, 192 const char *buf, size_t len) 193 { 194 struct iio_dev *indio_dev = dev_to_iio_dev(dev); 195 struct si7020_data *data = iio_priv(indio_dev); 196 int ret; 197 bool val; 198 199 ret = kstrtobool(buf, &val); 200 if (ret) 201 return ret; 202 203 scoped_guard(mutex, &data->lock) 204 ret = si7020_update_reg(data, &data->user_reg, SI7020CMD_USR_WRITE, 205 SI7020_USR_HEATER_EN, val ? SI7020_USR_HEATER_EN : 0); 206 207 return ret < 0 ? ret : len; 208 } 209 210 static IIO_DEVICE_ATTR(heater_enable, 0644, 211 si7020_show_heater_en, si7020_store_heater_en, 0); 212 213 static struct attribute *si7020_attributes[] = { 214 &iio_dev_attr_heater_enable.dev_attr.attr, 215 NULL 216 }; 217 218 static const struct attribute_group si7020_attribute_group = { 219 .attrs = si7020_attributes, 220 }; 221 222 static const struct iio_info si7020_info = { 223 .read_raw = si7020_read_raw, 224 .write_raw = si7020_write_raw, 225 .read_avail = si7020_read_available, 226 .attrs = &si7020_attribute_group, 227 }; 228 229 static int si7020_probe(struct i2c_client *client) 230 { 231 struct iio_dev *indio_dev; 232 struct si7020_data *data; 233 int ret; 234 235 if (!i2c_check_functionality(client->adapter, 236 I2C_FUNC_SMBUS_WRITE_BYTE | 237 I2C_FUNC_SMBUS_READ_WORD_DATA)) 238 return -EOPNOTSUPP; 239 240 /* Reset device, loads default settings. */ 241 ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); 242 if (ret < 0) 243 return ret; 244 /* Wait the maximum power-up time after software reset. */ 245 msleep(15); 246 247 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 248 if (!indio_dev) 249 return -ENOMEM; 250 251 data = iio_priv(indio_dev); 252 i2c_set_clientdata(client, indio_dev); 253 data->client = client; 254 mutex_init(&data->lock); 255 256 indio_dev->name = dev_name(&client->dev); 257 indio_dev->modes = INDIO_DIRECT_MODE; 258 indio_dev->info = &si7020_info; 259 indio_dev->channels = si7020_channels; 260 indio_dev->num_channels = ARRAY_SIZE(si7020_channels); 261 262 /* All the "reserved" bits in the User Register are 1s by default */ 263 data->user_reg = 0x3A; 264 data->heater_reg = 0x0; 265 266 return devm_iio_device_register(&client->dev, indio_dev); 267 } 268 269 static const struct i2c_device_id si7020_id[] = { 270 { "si7020" }, 271 { "th06" }, 272 { } 273 }; 274 MODULE_DEVICE_TABLE(i2c, si7020_id); 275 276 static const struct of_device_id si7020_dt_ids[] = { 277 { .compatible = "silabs,si7020" }, 278 { } 279 }; 280 MODULE_DEVICE_TABLE(of, si7020_dt_ids); 281 282 static struct i2c_driver si7020_driver = { 283 .driver = { 284 .name = "si7020", 285 .of_match_table = si7020_dt_ids, 286 }, 287 .probe = si7020_probe, 288 .id_table = si7020_id, 289 }; 290 291 module_i2c_driver(si7020_driver); 292 MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); 293 MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); 294 MODULE_LICENSE("GPL"); 295