xref: /linux/drivers/iio/humidity/si7020.c (revision 064a74637ccdee608eb898ea3e3847b142b261c6)
1*064a7463SDavid Barksdale /*
2*064a7463SDavid Barksdale  * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors
3*064a7463SDavid Barksdale  * Copyright (c) 2013,2014  Uplogix, Inc.
4*064a7463SDavid Barksdale  * David Barksdale <dbarksdale@uplogix.com>
5*064a7463SDavid Barksdale  *
6*064a7463SDavid Barksdale  * This program is free software; you can redistribute it and/or modify it
7*064a7463SDavid Barksdale  * under the terms and conditions of the GNU General Public License,
8*064a7463SDavid Barksdale  * version 2, as published by the Free Software Foundation.
9*064a7463SDavid Barksdale  *
10*064a7463SDavid Barksdale  * This program is distributed in the hope that it will be useful,
11*064a7463SDavid Barksdale  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*064a7463SDavid Barksdale  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*064a7463SDavid Barksdale  * GNU General Public License for more details.
14*064a7463SDavid Barksdale  */
15*064a7463SDavid Barksdale 
16*064a7463SDavid Barksdale /*
17*064a7463SDavid Barksdale  * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors
18*064a7463SDavid Barksdale  * are i2c devices which have an identical programming interface for
19*064a7463SDavid Barksdale  * measuring relative humidity and temperature. The Si7013 has an additional
20*064a7463SDavid Barksdale  * temperature input which this driver does not support.
21*064a7463SDavid Barksdale  *
22*064a7463SDavid Barksdale  * Data Sheets:
23*064a7463SDavid Barksdale  *   Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf
24*064a7463SDavid Barksdale  *   Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf
25*064a7463SDavid Barksdale  *   Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf
26*064a7463SDavid Barksdale  */
27*064a7463SDavid Barksdale 
28*064a7463SDavid Barksdale #include <linux/delay.h>
29*064a7463SDavid Barksdale #include <linux/i2c.h>
30*064a7463SDavid Barksdale #include <linux/module.h>
31*064a7463SDavid Barksdale #include <linux/slab.h>
32*064a7463SDavid Barksdale #include <linux/sysfs.h>
33*064a7463SDavid Barksdale 
34*064a7463SDavid Barksdale #include <linux/iio/iio.h>
35*064a7463SDavid Barksdale #include <linux/iio/sysfs.h>
36*064a7463SDavid Barksdale 
37*064a7463SDavid Barksdale /* Measure Relative Humidity, Hold Master Mode */
38*064a7463SDavid Barksdale #define SI7020CMD_RH_HOLD	0xE5
39*064a7463SDavid Barksdale /* Measure Temperature, Hold Master Mode */
40*064a7463SDavid Barksdale #define SI7020CMD_TEMP_HOLD	0xE3
41*064a7463SDavid Barksdale /* Software Reset */
42*064a7463SDavid Barksdale #define SI7020CMD_RESET		0xFE
43*064a7463SDavid Barksdale 
44*064a7463SDavid Barksdale static int si7020_read_raw(struct iio_dev *indio_dev,
45*064a7463SDavid Barksdale 			   struct iio_chan_spec const *chan, int *val,
46*064a7463SDavid Barksdale 			   int *val2, long mask)
47*064a7463SDavid Barksdale {
48*064a7463SDavid Barksdale 	struct i2c_client **client = iio_priv(indio_dev);
49*064a7463SDavid Barksdale 	int ret;
50*064a7463SDavid Barksdale 
51*064a7463SDavid Barksdale 	switch (mask) {
52*064a7463SDavid Barksdale 	case IIO_CHAN_INFO_RAW:
53*064a7463SDavid Barksdale 		ret = i2c_smbus_read_word_data(*client,
54*064a7463SDavid Barksdale 					       chan->type == IIO_TEMP ?
55*064a7463SDavid Barksdale 					       SI7020CMD_TEMP_HOLD :
56*064a7463SDavid Barksdale 					       SI7020CMD_RH_HOLD);
57*064a7463SDavid Barksdale 		if (ret < 0)
58*064a7463SDavid Barksdale 			return ret;
59*064a7463SDavid Barksdale 		if (chan->type == IIO_TEMP)
60*064a7463SDavid Barksdale 			*val = ret >> 2;
61*064a7463SDavid Barksdale 		else
62*064a7463SDavid Barksdale 			*val = (ret & 0x3FFF) >> 2;
63*064a7463SDavid Barksdale 		return IIO_VAL_INT;
64*064a7463SDavid Barksdale 	case IIO_CHAN_INFO_SCALE:
65*064a7463SDavid Barksdale 		if (chan->type == IIO_TEMP)
66*064a7463SDavid Barksdale 			*val = 175720; /* = 175.72 * 1000 */
67*064a7463SDavid Barksdale 		else
68*064a7463SDavid Barksdale 			*val = 125 * 1000;
69*064a7463SDavid Barksdale 		*val2 = 65536 >> 2;
70*064a7463SDavid Barksdale 		return IIO_VAL_FRACTIONAL;
71*064a7463SDavid Barksdale 	case IIO_CHAN_INFO_OFFSET:
72*064a7463SDavid Barksdale 		/*
73*064a7463SDavid Barksdale 		 * Since iio_convert_raw_to_processed_unlocked assumes offset
74*064a7463SDavid Barksdale 		 * is an integer we have to round these values and lose
75*064a7463SDavid Barksdale 		 * accuracy.
76*064a7463SDavid Barksdale 		 * Relative humidity will be 0.0032959% too high and
77*064a7463SDavid Barksdale 		 * temperature will be 0.00277344 degrees too high.
78*064a7463SDavid Barksdale 		 * This is no big deal because it's within the accuracy of the
79*064a7463SDavid Barksdale 		 * sensor.
80*064a7463SDavid Barksdale 		 */
81*064a7463SDavid Barksdale 		if (chan->type == IIO_TEMP)
82*064a7463SDavid Barksdale 			*val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */
83*064a7463SDavid Barksdale 		else
84*064a7463SDavid Barksdale 			*val = -786; /* = -6 * (65536 >> 2) / 125 */
85*064a7463SDavid Barksdale 		return IIO_VAL_INT;
86*064a7463SDavid Barksdale 	default:
87*064a7463SDavid Barksdale 		break;
88*064a7463SDavid Barksdale 	}
89*064a7463SDavid Barksdale 
90*064a7463SDavid Barksdale 	return -EINVAL;
91*064a7463SDavid Barksdale }
92*064a7463SDavid Barksdale 
93*064a7463SDavid Barksdale static const struct iio_chan_spec si7020_channels[] = {
94*064a7463SDavid Barksdale 	{
95*064a7463SDavid Barksdale 		.type = IIO_HUMIDITYRELATIVE,
96*064a7463SDavid Barksdale 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
97*064a7463SDavid Barksdale 			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
98*064a7463SDavid Barksdale 	},
99*064a7463SDavid Barksdale 	{
100*064a7463SDavid Barksdale 		.type = IIO_TEMP,
101*064a7463SDavid Barksdale 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
102*064a7463SDavid Barksdale 			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
103*064a7463SDavid Barksdale 	}
104*064a7463SDavid Barksdale };
105*064a7463SDavid Barksdale 
106*064a7463SDavid Barksdale static const struct iio_info si7020_info = {
107*064a7463SDavid Barksdale 	.read_raw = si7020_read_raw,
108*064a7463SDavid Barksdale 	.driver_module = THIS_MODULE,
109*064a7463SDavid Barksdale };
110*064a7463SDavid Barksdale 
111*064a7463SDavid Barksdale static int si7020_probe(struct i2c_client *client,
112*064a7463SDavid Barksdale 			const struct i2c_device_id *id)
113*064a7463SDavid Barksdale {
114*064a7463SDavid Barksdale 	struct iio_dev *indio_dev;
115*064a7463SDavid Barksdale 	struct i2c_client **data;
116*064a7463SDavid Barksdale 	int ret;
117*064a7463SDavid Barksdale 
118*064a7463SDavid Barksdale 	if (!i2c_check_functionality(client->adapter,
119*064a7463SDavid Barksdale 				     I2C_FUNC_SMBUS_WRITE_BYTE |
120*064a7463SDavid Barksdale 				     I2C_FUNC_SMBUS_READ_WORD_DATA))
121*064a7463SDavid Barksdale 		return -ENODEV;
122*064a7463SDavid Barksdale 
123*064a7463SDavid Barksdale 	/* Reset device, loads default settings. */
124*064a7463SDavid Barksdale 	ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
125*064a7463SDavid Barksdale 	if (ret < 0)
126*064a7463SDavid Barksdale 		return ret;
127*064a7463SDavid Barksdale 	/* Wait the maximum power-up time after software reset. */
128*064a7463SDavid Barksdale 	msleep(15);
129*064a7463SDavid Barksdale 
130*064a7463SDavid Barksdale 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client));
131*064a7463SDavid Barksdale 	if (!indio_dev)
132*064a7463SDavid Barksdale 		return -ENOMEM;
133*064a7463SDavid Barksdale 
134*064a7463SDavid Barksdale 	data = iio_priv(indio_dev);
135*064a7463SDavid Barksdale 	*data = client;
136*064a7463SDavid Barksdale 	i2c_set_clientdata(client, indio_dev);
137*064a7463SDavid Barksdale 
138*064a7463SDavid Barksdale 	indio_dev->dev.parent = &client->dev;
139*064a7463SDavid Barksdale 	indio_dev->name = dev_name(&client->dev);
140*064a7463SDavid Barksdale 	indio_dev->modes = INDIO_DIRECT_MODE;
141*064a7463SDavid Barksdale 	indio_dev->info = &si7020_info;
142*064a7463SDavid Barksdale 	indio_dev->channels = si7020_channels;
143*064a7463SDavid Barksdale 	indio_dev->num_channels = ARRAY_SIZE(si7020_channels);
144*064a7463SDavid Barksdale 
145*064a7463SDavid Barksdale 	return devm_iio_device_register(&client->dev, indio_dev);
146*064a7463SDavid Barksdale }
147*064a7463SDavid Barksdale 
148*064a7463SDavid Barksdale static const struct i2c_device_id si7020_id[] = {
149*064a7463SDavid Barksdale 	{ "si7020", 0 },
150*064a7463SDavid Barksdale 	{ }
151*064a7463SDavid Barksdale };
152*064a7463SDavid Barksdale MODULE_DEVICE_TABLE(i2c, si7020_id);
153*064a7463SDavid Barksdale 
154*064a7463SDavid Barksdale static struct i2c_driver si7020_driver = {
155*064a7463SDavid Barksdale 	.driver.name	= "si7020",
156*064a7463SDavid Barksdale 	.probe		= si7020_probe,
157*064a7463SDavid Barksdale 	.id_table	= si7020_id,
158*064a7463SDavid Barksdale };
159*064a7463SDavid Barksdale 
160*064a7463SDavid Barksdale module_i2c_driver(si7020_driver);
161*064a7463SDavid Barksdale MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors");
162*064a7463SDavid Barksdale MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>");
163*064a7463SDavid Barksdale MODULE_LICENSE("GPL");
164