1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * hdc2010.c - Support for the TI HDC2010 and HDC2080
4 * temperature + relative humidity sensors
5 *
6 * Copyright (C) 2020 Norphonic AS
7 * Author: Eugene Zaikonnikov <ez@norphonic.com>
8 *
9 * Datasheet: https://www.ti.com/product/HDC2010/datasheet
10 * Datasheet: https://www.ti.com/product/HDC2080/datasheet
11 */
12
13 #include <linux/module.h>
14 #include <linux/init.h>
15 #include <linux/i2c.h>
16 #include <linux/bitops.h>
17
18 #include <linux/iio/iio.h>
19 #include <linux/iio/sysfs.h>
20
21 #define HDC2010_REG_TEMP_LOW 0x00
22 #define HDC2010_REG_TEMP_HIGH 0x01
23 #define HDC2010_REG_HUMIDITY_LOW 0x02
24 #define HDC2010_REG_HUMIDITY_HIGH 0x03
25 #define HDC2010_REG_INTERRUPT_DRDY 0x04
26 #define HDC2010_REG_TEMP_MAX 0x05
27 #define HDC2010_REG_HUMIDITY_MAX 0x06
28 #define HDC2010_REG_INTERRUPT_EN 0x07
29 #define HDC2010_REG_TEMP_OFFSET_ADJ 0x08
30 #define HDC2010_REG_HUMIDITY_OFFSET_ADJ 0x09
31 #define HDC2010_REG_TEMP_THR_L 0x0a
32 #define HDC2010_REG_TEMP_THR_H 0x0b
33 #define HDC2010_REG_RH_THR_L 0x0c
34 #define HDC2010_REG_RH_THR_H 0x0d
35 #define HDC2010_REG_RESET_DRDY_INT_CONF 0x0e
36 #define HDC2010_REG_MEASUREMENT_CONF 0x0f
37
38 #define HDC2010_MEAS_CONF GENMASK(2, 1)
39 #define HDC2010_MEAS_TRIG BIT(0)
40 #define HDC2010_HEATER_EN BIT(3)
41 #define HDC2010_AMM GENMASK(6, 4)
42
43 struct hdc2010_data {
44 struct i2c_client *client;
45 struct mutex lock;
46 u8 measurement_config;
47 u8 drdy_config;
48 };
49
50 enum hdc2010_addr_groups {
51 HDC2010_GROUP_TEMP = 0,
52 HDC2010_GROUP_HUMIDITY,
53 };
54
55 struct hdc2010_reg_record {
56 unsigned long primary;
57 unsigned long peak;
58 };
59
60 static const struct hdc2010_reg_record hdc2010_reg_translation[] = {
61 [HDC2010_GROUP_TEMP] = {
62 .primary = HDC2010_REG_TEMP_LOW,
63 .peak = HDC2010_REG_TEMP_MAX,
64 },
65 [HDC2010_GROUP_HUMIDITY] = {
66 .primary = HDC2010_REG_HUMIDITY_LOW,
67 .peak = HDC2010_REG_HUMIDITY_MAX,
68 },
69 };
70
71 static IIO_CONST_ATTR(out_current_heater_raw_available, "0 1");
72
73 static struct attribute *hdc2010_attributes[] = {
74 &iio_const_attr_out_current_heater_raw_available.dev_attr.attr,
75 NULL
76 };
77
78 static const struct attribute_group hdc2010_attribute_group = {
79 .attrs = hdc2010_attributes,
80 };
81
82 static const struct iio_chan_spec hdc2010_channels[] = {
83 {
84 .type = IIO_TEMP,
85 .address = HDC2010_GROUP_TEMP,
86 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
87 BIT(IIO_CHAN_INFO_PEAK) |
88 BIT(IIO_CHAN_INFO_OFFSET) |
89 BIT(IIO_CHAN_INFO_SCALE),
90 },
91 {
92 .type = IIO_HUMIDITYRELATIVE,
93 .address = HDC2010_GROUP_HUMIDITY,
94 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
95 BIT(IIO_CHAN_INFO_PEAK) |
96 BIT(IIO_CHAN_INFO_SCALE),
97 },
98 {
99 .type = IIO_CURRENT,
100 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
101 .extend_name = "heater",
102 .output = 1,
103 },
104 };
105
hdc2010_update_drdy_config(struct hdc2010_data * data,char mask,char val)106 static int hdc2010_update_drdy_config(struct hdc2010_data *data,
107 char mask, char val)
108 {
109 u8 tmp = (~mask & data->drdy_config) | val;
110 int ret;
111
112 ret = i2c_smbus_write_byte_data(data->client,
113 HDC2010_REG_RESET_DRDY_INT_CONF, tmp);
114 if (ret)
115 return ret;
116
117 data->drdy_config = tmp;
118
119 return 0;
120 }
121
hdc2010_get_prim_measurement_word(struct hdc2010_data * data,struct iio_chan_spec const * chan)122 static int hdc2010_get_prim_measurement_word(struct hdc2010_data *data,
123 struct iio_chan_spec const *chan)
124 {
125 struct i2c_client *client = data->client;
126 s32 ret;
127
128 ret = i2c_smbus_read_word_data(client,
129 hdc2010_reg_translation[chan->address].primary);
130
131 if (ret < 0)
132 dev_err(&client->dev, "Could not read sensor measurement word\n");
133
134 return ret;
135 }
136
hdc2010_get_peak_measurement_byte(struct hdc2010_data * data,struct iio_chan_spec const * chan)137 static int hdc2010_get_peak_measurement_byte(struct hdc2010_data *data,
138 struct iio_chan_spec const *chan)
139 {
140 struct i2c_client *client = data->client;
141 s32 ret;
142
143 ret = i2c_smbus_read_byte_data(client,
144 hdc2010_reg_translation[chan->address].peak);
145
146 if (ret < 0)
147 dev_err(&client->dev, "Could not read sensor measurement byte\n");
148
149 return ret;
150 }
151
hdc2010_get_heater_status(struct hdc2010_data * data)152 static int hdc2010_get_heater_status(struct hdc2010_data *data)
153 {
154 return !!(data->drdy_config & HDC2010_HEATER_EN);
155 }
156
hdc2010_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)157 static int hdc2010_read_raw(struct iio_dev *indio_dev,
158 struct iio_chan_spec const *chan, int *val,
159 int *val2, long mask)
160 {
161 struct hdc2010_data *data = iio_priv(indio_dev);
162
163 switch (mask) {
164 case IIO_CHAN_INFO_RAW: {
165 int ret;
166
167 if (chan->type == IIO_CURRENT) {
168 *val = hdc2010_get_heater_status(data);
169 return IIO_VAL_INT;
170 }
171 if (!iio_device_claim_direct(indio_dev))
172 return -EBUSY;
173 mutex_lock(&data->lock);
174 ret = hdc2010_get_prim_measurement_word(data, chan);
175 mutex_unlock(&data->lock);
176 iio_device_release_direct(indio_dev);
177 if (ret < 0)
178 return ret;
179 *val = ret;
180 return IIO_VAL_INT;
181 }
182 case IIO_CHAN_INFO_PEAK: {
183 int ret;
184
185 if (!iio_device_claim_direct(indio_dev))
186 return -EBUSY;
187 mutex_lock(&data->lock);
188 ret = hdc2010_get_peak_measurement_byte(data, chan);
189 mutex_unlock(&data->lock);
190 iio_device_release_direct(indio_dev);
191 if (ret < 0)
192 return ret;
193 /* Scaling up the value so we can use same offset as RAW */
194 *val = ret * 256;
195 return IIO_VAL_INT;
196 }
197 case IIO_CHAN_INFO_SCALE:
198 *val2 = 65536;
199 if (chan->type == IIO_TEMP)
200 *val = 165000;
201 else
202 *val = 100000;
203 return IIO_VAL_FRACTIONAL;
204 case IIO_CHAN_INFO_OFFSET:
205 *val = -15887;
206 *val2 = 515151;
207 return IIO_VAL_INT_PLUS_MICRO;
208 default:
209 return -EINVAL;
210 }
211 }
212
hdc2010_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)213 static int hdc2010_write_raw(struct iio_dev *indio_dev,
214 struct iio_chan_spec const *chan,
215 int val, int val2, long mask)
216 {
217 struct hdc2010_data *data = iio_priv(indio_dev);
218 int new, ret;
219
220 switch (mask) {
221 case IIO_CHAN_INFO_RAW:
222 if (chan->type != IIO_CURRENT || val2 != 0)
223 return -EINVAL;
224
225 switch (val) {
226 case 1:
227 new = HDC2010_HEATER_EN;
228 break;
229 case 0:
230 new = 0;
231 break;
232 default:
233 return -EINVAL;
234 }
235
236 mutex_lock(&data->lock);
237 ret = hdc2010_update_drdy_config(data, HDC2010_HEATER_EN, new);
238 mutex_unlock(&data->lock);
239 return ret;
240 default:
241 return -EINVAL;
242 }
243 }
244
245 static const struct iio_info hdc2010_info = {
246 .read_raw = hdc2010_read_raw,
247 .write_raw = hdc2010_write_raw,
248 .attrs = &hdc2010_attribute_group,
249 };
250
hdc2010_probe(struct i2c_client * client)251 static int hdc2010_probe(struct i2c_client *client)
252 {
253 struct iio_dev *indio_dev;
254 struct hdc2010_data *data;
255 u8 tmp;
256 int ret;
257
258 if (!i2c_check_functionality(client->adapter,
259 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
260 return -EOPNOTSUPP;
261
262 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
263 if (!indio_dev)
264 return -ENOMEM;
265
266 data = iio_priv(indio_dev);
267 i2c_set_clientdata(client, indio_dev);
268 data->client = client;
269 mutex_init(&data->lock);
270
271 /*
272 * As DEVICE ID register does not differentiate between
273 * HDC2010 and HDC2080, we have the name hardcoded
274 */
275 indio_dev->name = "hdc2010";
276 indio_dev->modes = INDIO_DIRECT_MODE;
277 indio_dev->info = &hdc2010_info;
278
279 indio_dev->channels = hdc2010_channels;
280 indio_dev->num_channels = ARRAY_SIZE(hdc2010_channels);
281
282 /* Enable Automatic Measurement Mode at 5Hz */
283 ret = hdc2010_update_drdy_config(data, HDC2010_AMM, HDC2010_AMM);
284 if (ret)
285 return ret;
286
287 /*
288 * We enable both temp and humidity measurement.
289 * However the measurement won't start even in AMM until triggered.
290 */
291 tmp = (data->measurement_config & ~HDC2010_MEAS_CONF) |
292 HDC2010_MEAS_TRIG;
293
294 ret = i2c_smbus_write_byte_data(client, HDC2010_REG_MEASUREMENT_CONF, tmp);
295 if (ret) {
296 dev_warn(&client->dev, "Unable to set up measurement\n");
297 if (hdc2010_update_drdy_config(data, HDC2010_AMM, 0))
298 dev_warn(&client->dev, "Unable to restore default AMM\n");
299 return ret;
300 }
301
302 data->measurement_config = tmp;
303
304 return iio_device_register(indio_dev);
305 }
306
hdc2010_remove(struct i2c_client * client)307 static void hdc2010_remove(struct i2c_client *client)
308 {
309 struct iio_dev *indio_dev = i2c_get_clientdata(client);
310 struct hdc2010_data *data = iio_priv(indio_dev);
311
312 iio_device_unregister(indio_dev);
313
314 /* Disable Automatic Measurement Mode */
315 if (hdc2010_update_drdy_config(data, HDC2010_AMM, 0))
316 dev_warn(&client->dev, "Unable to restore default AMM\n");
317 }
318
319 static const struct i2c_device_id hdc2010_id[] = {
320 { "hdc2010" },
321 { "hdc2080" },
322 { }
323 };
324 MODULE_DEVICE_TABLE(i2c, hdc2010_id);
325
326 static const struct of_device_id hdc2010_dt_ids[] = {
327 { .compatible = "ti,hdc2010" },
328 { .compatible = "ti,hdc2080" },
329 { }
330 };
331 MODULE_DEVICE_TABLE(of, hdc2010_dt_ids);
332
333 static struct i2c_driver hdc2010_driver = {
334 .driver = {
335 .name = "hdc2010",
336 .of_match_table = hdc2010_dt_ids,
337 },
338 .probe = hdc2010_probe,
339 .remove = hdc2010_remove,
340 .id_table = hdc2010_id,
341 };
342 module_i2c_driver(hdc2010_driver);
343
344 MODULE_AUTHOR("Eugene Zaikonnikov <ez@norphonic.com>");
345 MODULE_DESCRIPTION("TI HDC2010 humidity and temperature sensor driver");
346 MODULE_LICENSE("GPL");
347