xref: /OK3568_Linux_fs/kernel/drivers/iio/humidity/si7020.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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