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