1049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald/*
2049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald * si7005.c - Support for Silabs Si7005 humidity and temperature sensor
3049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald *
4049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
5049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald *
6049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald * This file is subject to the terms and conditions of version 2 of
7049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald * the GNU General Public License.  See the file COPYING in the main
8049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald * directory of this archive for more details.
9049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald *
10049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald * (7-bit I2C slave address 0x40)
11049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald *
12049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald * TODO: heater, fast mode, processed mode (temp. / linearity compensation)
13049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald */
14049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
15049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#include <linux/err.h>
16049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#include <linux/i2c.h>
17049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#include <linux/delay.h>
18049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#include <linux/module.h>
19049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#include <linux/pm.h>
20049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
21049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#include <linux/iio/iio.h>
22049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#include <linux/iio/sysfs.h>
23049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
24049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_STATUS 0x00
25049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_DATA 0x01 /* 16-bit, MSB */
26049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_CONFIG 0x03
27049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_ID 0x11
28049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
29049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_STATUS_NRDY BIT(0)
30049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_CONFIG_TEMP BIT(4)
31049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_CONFIG_START BIT(0)
32049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
33049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_ID_7005 0x50
34049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald#define SI7005_ID_7015 0xf0
35049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
36049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldstruct si7005_data {
37049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	struct i2c_client *client;
38049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	struct mutex lock;
39049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	u8 config;
40049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald};
41049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
42049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldstatic int si7005_read_measurement(struct si7005_data *data, bool temp)
43049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald{
44049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	int tries = 50;
45049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	int ret;
46049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
47049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	mutex_lock(&data->lock);
48049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
49049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	ret = i2c_smbus_write_byte_data(data->client, SI7005_CONFIG,
50049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		data->config | SI7005_CONFIG_START |
51049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		(temp ? SI7005_CONFIG_TEMP : 0));
52049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	if (ret < 0)
53049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		goto done;
54049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
55049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	while (tries-- > 0) {
56049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		msleep(20);
57049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		ret = i2c_smbus_read_byte_data(data->client, SI7005_STATUS);
58049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		if (ret < 0)
59049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			goto done;
60049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		if (!(ret & SI7005_STATUS_NRDY))
61049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			break;
62049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	}
63049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	if (tries < 0) {
64049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		ret = -EIO;
65049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		goto done;
66049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	}
67049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
68049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	ret = i2c_smbus_read_word_swapped(data->client, SI7005_DATA);
69049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
70049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwalddone:
71049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	mutex_unlock(&data->lock);
72049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
73049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	return ret;
74049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald}
75049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
76049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldstatic int si7005_read_raw(struct iio_dev *indio_dev,
77049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			    struct iio_chan_spec const *chan, int *val,
78049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			    int *val2, long mask)
79049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald{
80049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	struct si7005_data *data = iio_priv(indio_dev);
81049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	int ret;
82049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
83049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	switch (mask) {
84049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	case IIO_CHAN_INFO_RAW:
85049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		ret = si7005_read_measurement(data, chan->type == IIO_TEMP);
86049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		if (ret < 0)
87049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			return ret;
88049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		*val = ret;
89049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		return IIO_VAL_INT;
90049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	case IIO_CHAN_INFO_SCALE:
91049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		if (chan->type == IIO_TEMP) {
92049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			*val = 7;
93049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			*val2 = 812500;
94049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		} else {
95049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			*val = 3;
96049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			*val2 = 906250;
97049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		}
98049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		return IIO_VAL_INT_PLUS_MICRO;
99049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	case IIO_CHAN_INFO_OFFSET:
100049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		if (chan->type == IIO_TEMP)
101049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			*val = -50 * 32 * 4;
102049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		else
103049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			*val = -24 * 16 * 16;
104049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		return IIO_VAL_INT;
105049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	default:
106049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		break;
107049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	}
108049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
109049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	return -EINVAL;
110049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald}
111049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
112049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldstatic const struct iio_chan_spec si7005_channels[] = {
113049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	{
114049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		.type = IIO_HUMIDITYRELATIVE,
115049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
116049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
117049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	},
118049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	{
119049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		.type = IIO_TEMP,
120049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
121049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
122049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	}
123049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald};
124049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
125049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldstatic const struct iio_info si7005_info = {
126049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	.read_raw = si7005_read_raw,
127049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	.driver_module = THIS_MODULE,
128049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald};
129049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
130049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldstatic int si7005_probe(struct i2c_client *client,
131049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald			 const struct i2c_device_id *id)
132049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald{
133049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	struct iio_dev *indio_dev;
134049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	struct si7005_data *data;
135049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	int ret;
136049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
137049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
138049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		return -ENODEV;
139049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
140049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
141049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	if (!indio_dev)
142049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		return -ENOMEM;
143049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
144049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	data = iio_priv(indio_dev);
145049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	i2c_set_clientdata(client, indio_dev);
146049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	data->client = client;
147049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	mutex_init(&data->lock);
148049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
149049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	indio_dev->dev.parent = &client->dev;
150049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	indio_dev->name = dev_name(&client->dev);
151049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	indio_dev->modes = INDIO_DIRECT_MODE;
152049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	indio_dev->info = &si7005_info;
153049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
154049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	indio_dev->channels = si7005_channels;
155049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	indio_dev->num_channels = ARRAY_SIZE(si7005_channels);
156049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
157049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	ret = i2c_smbus_read_byte_data(client, SI7005_ID);
158049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	if (ret < 0)
159049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		return ret;
160049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	if (ret != SI7005_ID_7005 && ret != SI7005_ID_7015)
161049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		return -ENODEV;
162049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
163049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	ret = i2c_smbus_read_byte_data(client, SI7005_CONFIG);
164049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	if (ret < 0)
165049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		return ret;
166049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	data->config = ret;
167049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
168049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	return devm_iio_device_register(&client->dev, indio_dev);
169049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald}
170049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
171049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldstatic const struct i2c_device_id si7005_id[] = {
172049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	{ "si7005", 0 },
173049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	{ }
174049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald};
175049973b23a6c77186dae45f708cdc0aa52b3a09ePeter MeerwaldMODULE_DEVICE_TABLE(i2c, si7005_id);
176049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
177049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldstatic struct i2c_driver si7005_driver = {
178049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	.driver = {
179049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		.name	= "si7005",
180049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald		.owner	= THIS_MODULE,
181049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	},
182049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	.probe = si7005_probe,
183049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald	.id_table = si7005_id,
184049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald};
185049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwaldmodule_i2c_driver(si7005_driver);
186049973b23a6c77186dae45f708cdc0aa52b3a09ePeter Meerwald
187049973b23a6c77186dae45f708cdc0aa52b3a09ePeter MeerwaldMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
188049973b23a6c77186dae45f708cdc0aa52b3a09ePeter MeerwaldMODULE_DESCRIPTION("Silabs Si7005 humidity and temperature sensor driver");
189049973b23a6c77186dae45f708cdc0aa52b3a09ePeter MeerwaldMODULE_LICENSE("GPL");
190