1/* 2 * si7005.c - Support for Silabs Si7005 humidity and temperature sensor 3 * 4 * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net> 5 * 6 * This file is subject to the terms and conditions of version 2 of 7 * the GNU General Public License. See the file COPYING in the main 8 * directory of this archive for more details. 9 * 10 * (7-bit I2C slave address 0x40) 11 * 12 * TODO: heater, fast mode, processed mode (temp. / linearity compensation) 13 */ 14 15#include <linux/err.h> 16#include <linux/i2c.h> 17#include <linux/delay.h> 18#include <linux/module.h> 19#include <linux/pm.h> 20 21#include <linux/iio/iio.h> 22#include <linux/iio/sysfs.h> 23 24#define SI7005_STATUS 0x00 25#define SI7005_DATA 0x01 /* 16-bit, MSB */ 26#define SI7005_CONFIG 0x03 27#define SI7005_ID 0x11 28 29#define SI7005_STATUS_NRDY BIT(0) 30#define SI7005_CONFIG_TEMP BIT(4) 31#define SI7005_CONFIG_START BIT(0) 32 33#define SI7005_ID_7005 0x50 34#define SI7005_ID_7015 0xf0 35 36struct si7005_data { 37 struct i2c_client *client; 38 struct mutex lock; 39 u8 config; 40}; 41 42static int si7005_read_measurement(struct si7005_data *data, bool temp) 43{ 44 int tries = 50; 45 int ret; 46 47 mutex_lock(&data->lock); 48 49 ret = i2c_smbus_write_byte_data(data->client, SI7005_CONFIG, 50 data->config | SI7005_CONFIG_START | 51 (temp ? SI7005_CONFIG_TEMP : 0)); 52 if (ret < 0) 53 goto done; 54 55 while (tries-- > 0) { 56 msleep(20); 57 ret = i2c_smbus_read_byte_data(data->client, SI7005_STATUS); 58 if (ret < 0) 59 goto done; 60 if (!(ret & SI7005_STATUS_NRDY)) 61 break; 62 } 63 if (tries < 0) { 64 ret = -EIO; 65 goto done; 66 } 67 68 ret = i2c_smbus_read_word_swapped(data->client, SI7005_DATA); 69 70done: 71 mutex_unlock(&data->lock); 72 73 return ret; 74} 75 76static int si7005_read_raw(struct iio_dev *indio_dev, 77 struct iio_chan_spec const *chan, int *val, 78 int *val2, long mask) 79{ 80 struct si7005_data *data = iio_priv(indio_dev); 81 int ret; 82 83 switch (mask) { 84 case IIO_CHAN_INFO_RAW: 85 ret = si7005_read_measurement(data, chan->type == IIO_TEMP); 86 if (ret < 0) 87 return ret; 88 *val = ret; 89 return IIO_VAL_INT; 90 case IIO_CHAN_INFO_SCALE: 91 if (chan->type == IIO_TEMP) { 92 *val = 7; 93 *val2 = 812500; 94 } else { 95 *val = 3; 96 *val2 = 906250; 97 } 98 return IIO_VAL_INT_PLUS_MICRO; 99 case IIO_CHAN_INFO_OFFSET: 100 if (chan->type == IIO_TEMP) 101 *val = -50 * 32 * 4; 102 else 103 *val = -24 * 16 * 16; 104 return IIO_VAL_INT; 105 default: 106 break; 107 } 108 109 return -EINVAL; 110} 111 112static const struct iio_chan_spec si7005_channels[] = { 113 { 114 .type = IIO_HUMIDITYRELATIVE, 115 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 116 BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 117 }, 118 { 119 .type = IIO_TEMP, 120 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 121 BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 122 } 123}; 124 125static const struct iio_info si7005_info = { 126 .read_raw = si7005_read_raw, 127 .driver_module = THIS_MODULE, 128}; 129 130static int si7005_probe(struct i2c_client *client, 131 const struct i2c_device_id *id) 132{ 133 struct iio_dev *indio_dev; 134 struct si7005_data *data; 135 int ret; 136 137 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) 138 return -ENODEV; 139 140 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 141 if (!indio_dev) 142 return -ENOMEM; 143 144 data = iio_priv(indio_dev); 145 i2c_set_clientdata(client, indio_dev); 146 data->client = client; 147 mutex_init(&data->lock); 148 149 indio_dev->dev.parent = &client->dev; 150 indio_dev->name = dev_name(&client->dev); 151 indio_dev->modes = INDIO_DIRECT_MODE; 152 indio_dev->info = &si7005_info; 153 154 indio_dev->channels = si7005_channels; 155 indio_dev->num_channels = ARRAY_SIZE(si7005_channels); 156 157 ret = i2c_smbus_read_byte_data(client, SI7005_ID); 158 if (ret < 0) 159 return ret; 160 if (ret != SI7005_ID_7005 && ret != SI7005_ID_7015) 161 return -ENODEV; 162 163 ret = i2c_smbus_read_byte_data(client, SI7005_CONFIG); 164 if (ret < 0) 165 return ret; 166 data->config = ret; 167 168 return devm_iio_device_register(&client->dev, indio_dev); 169} 170 171static const struct i2c_device_id si7005_id[] = { 172 { "si7005", 0 }, 173 { } 174}; 175MODULE_DEVICE_TABLE(i2c, si7005_id); 176 177static struct i2c_driver si7005_driver = { 178 .driver = { 179 .name = "si7005", 180 .owner = THIS_MODULE, 181 }, 182 .probe = si7005_probe, 183 .id_table = si7005_id, 184}; 185module_i2c_driver(si7005_driver); 186 187MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 188MODULE_DESCRIPTION("Silabs Si7005 humidity and temperature sensor driver"); 189MODULE_LICENSE("GPL"); 190