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