FILE
si7020.c
File preview with syntax highlighting.
linux-6.19.14/drivers/iio/humidity/si7020.c
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 44struct 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 52static const int si7020_heater_vals[] = { 0, 1, 0xF }; 53 54static 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 111static 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 130static 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 145static 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 167static 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 181static 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 190static 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 210static IIO_DEVICE_ATTR(heater_enable, 0644, 211 si7020_show_heater_en, si7020_store_heater_en, 0); 212 213static struct attribute *si7020_attributes[] = { 214 &iio_dev_attr_heater_enable.dev_attr.attr, 215 NULL 216}; 217 218static const struct attribute_group si7020_attribute_group = { 219 .attrs = si7020_attributes, 220}; 221 222static 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 229static 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 269static const struct i2c_device_id si7020_id[] = { 270 { "si7020" }, 271 { "th06" }, 272 { } 273}; 274MODULE_DEVICE_TABLE(i2c, si7020_id); 275 276static const struct of_device_id si7020_dt_ids[] = { 277 { .compatible = "silabs,si7020" }, 278 { } 279}; 280MODULE_DEVICE_TABLE(of, si7020_dt_ids); 281 282static 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 291module_i2c_driver(si7020_driver); 292MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); 293MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); 294MODULE_LICENSE("GPL");
~/
Read-only file preview.