1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * hdc3020.c - Support for the TI HDC3020,HDC3021 and HDC3022 |
4 | * temperature + relative humidity sensors |
5 | * |
6 | * Copyright (C) 2023 |
7 | * |
8 | * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH |
9 | * |
10 | * Datasheet: https://www.ti.com/lit/ds/symlink/hdc3020.pdf |
11 | */ |
12 | |
13 | #include <linux/bitfield.h> |
14 | #include <linux/bitops.h> |
15 | #include <linux/cleanup.h> |
16 | #include <linux/crc8.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/i2c.h> |
19 | #include <linux/init.h> |
20 | #include <linux/interrupt.h> |
21 | #include <linux/module.h> |
22 | #include <linux/mutex.h> |
23 | #include <linux/units.h> |
24 | |
25 | #include <asm/unaligned.h> |
26 | |
27 | #include <linux/iio/events.h> |
28 | #include <linux/iio/iio.h> |
29 | |
30 | #define HDC3020_S_AUTO_10HZ_MOD0 0x2737 |
31 | #define HDC3020_S_STATUS 0x3041 |
32 | #define HDC3020_HEATER_DISABLE 0x3066 |
33 | #define HDC3020_HEATER_ENABLE 0x306D |
34 | #define HDC3020_HEATER_CONFIG 0x306E |
35 | #define HDC3020_EXIT_AUTO 0x3093 |
36 | #define HDC3020_S_T_RH_THRESH_LOW 0x6100 |
37 | #define HDC3020_S_T_RH_THRESH_LOW_CLR 0x610B |
38 | #define HDC3020_S_T_RH_THRESH_HIGH_CLR 0x6116 |
39 | #define HDC3020_S_T_RH_THRESH_HIGH 0x611D |
40 | #define HDC3020_R_T_RH_AUTO 0xE000 |
41 | #define HDC3020_R_T_LOW_AUTO 0xE002 |
42 | #define HDC3020_R_T_HIGH_AUTO 0xE003 |
43 | #define HDC3020_R_RH_LOW_AUTO 0xE004 |
44 | #define HDC3020_R_RH_HIGH_AUTO 0xE005 |
45 | #define HDC3020_R_T_RH_THRESH_LOW 0xE102 |
46 | #define HDC3020_R_T_RH_THRESH_LOW_CLR 0xE109 |
47 | #define HDC3020_R_T_RH_THRESH_HIGH_CLR 0xE114 |
48 | #define HDC3020_R_T_RH_THRESH_HIGH 0xE11F |
49 | #define HDC3020_R_STATUS 0xF32D |
50 | |
51 | #define HDC3020_THRESH_TEMP_MASK GENMASK(8, 0) |
52 | #define HDC3020_THRESH_TEMP_TRUNC_SHIFT 7 |
53 | #define HDC3020_THRESH_HUM_MASK GENMASK(15, 9) |
54 | #define HDC3020_THRESH_HUM_TRUNC_SHIFT 9 |
55 | |
56 | #define HDC3020_STATUS_T_LOW_ALERT BIT(6) |
57 | #define HDC3020_STATUS_T_HIGH_ALERT BIT(7) |
58 | #define HDC3020_STATUS_RH_LOW_ALERT BIT(8) |
59 | #define HDC3020_STATUS_RH_HIGH_ALERT BIT(9) |
60 | |
61 | #define HDC3020_READ_RETRY_TIMES 10 |
62 | #define HDC3020_BUSY_DELAY_MS 10 |
63 | |
64 | #define HDC3020_CRC8_POLYNOMIAL 0x31 |
65 | |
66 | #define HDC3020_MIN_TEMP -40 |
67 | #define HDC3020_MAX_TEMP 125 |
68 | |
69 | struct hdc3020_data { |
70 | struct i2c_client *client; |
71 | /* |
72 | * Ensure that the sensor configuration (currently only heater is |
73 | * supported) will not be changed during the process of reading |
74 | * sensor data (this driver will try HDC3020_READ_RETRY_TIMES times |
75 | * if the device does not respond). |
76 | */ |
77 | struct mutex lock; |
78 | }; |
79 | |
80 | static const int hdc3020_heater_vals[] = {0, 1, 0x3FFF}; |
81 | |
82 | static const struct iio_event_spec hdc3020_t_rh_event[] = { |
83 | { |
84 | .type = IIO_EV_TYPE_THRESH, |
85 | .dir = IIO_EV_DIR_RISING, |
86 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | |
87 | BIT(IIO_EV_INFO_HYSTERESIS), |
88 | }, |
89 | { |
90 | .type = IIO_EV_TYPE_THRESH, |
91 | .dir = IIO_EV_DIR_FALLING, |
92 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | |
93 | BIT(IIO_EV_INFO_HYSTERESIS), |
94 | }, |
95 | }; |
96 | |
97 | static const struct iio_chan_spec hdc3020_channels[] = { |
98 | { |
99 | .type = IIO_TEMP, |
100 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
101 | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) | |
102 | BIT(IIO_CHAN_INFO_TROUGH) | BIT(IIO_CHAN_INFO_OFFSET), |
103 | .event_spec = hdc3020_t_rh_event, |
104 | .num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event), |
105 | }, |
106 | { |
107 | .type = IIO_HUMIDITYRELATIVE, |
108 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
109 | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) | |
110 | BIT(IIO_CHAN_INFO_TROUGH), |
111 | .event_spec = hdc3020_t_rh_event, |
112 | .num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event), |
113 | }, |
114 | { |
115 | /* |
116 | * For setting the internal heater, which can be switched on to |
117 | * prevent or remove any condensation that may develop when the |
118 | * ambient environment approaches its dew point temperature. |
119 | */ |
120 | .type = IIO_CURRENT, |
121 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
122 | .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), |
123 | .output = 1, |
124 | }, |
125 | }; |
126 | |
127 | DECLARE_CRC8_TABLE(hdc3020_crc8_table); |
128 | |
129 | static int hdc3020_write_bytes(struct hdc3020_data *data, u8 *buf, u8 len) |
130 | { |
131 | struct i2c_client *client = data->client; |
132 | struct i2c_msg msg; |
133 | int ret, cnt; |
134 | |
135 | msg.addr = client->addr; |
136 | msg.flags = 0; |
137 | msg.buf = buf; |
138 | msg.len = len; |
139 | |
140 | /* |
141 | * During the measurement process, HDC3020 will not return data. |
142 | * So wait for a while and try again |
143 | */ |
144 | for (cnt = 0; cnt < HDC3020_READ_RETRY_TIMES; cnt++) { |
145 | ret = i2c_transfer(adap: client->adapter, msgs: &msg, num: 1); |
146 | if (ret == 1) |
147 | return 0; |
148 | |
149 | mdelay(HDC3020_BUSY_DELAY_MS); |
150 | } |
151 | dev_err(&client->dev, "Could not write sensor command\n" ); |
152 | |
153 | return -ETIMEDOUT; |
154 | } |
155 | |
156 | static |
157 | int hdc3020_read_bytes(struct hdc3020_data *data, u16 reg, u8 *buf, int len) |
158 | { |
159 | u8 reg_buf[2]; |
160 | int ret, cnt; |
161 | struct i2c_client *client = data->client; |
162 | struct i2c_msg msg[2] = { |
163 | [0] = { |
164 | .addr = client->addr, |
165 | .flags = 0, |
166 | .buf = reg_buf, |
167 | .len = 2, |
168 | }, |
169 | [1] = { |
170 | .addr = client->addr, |
171 | .flags = I2C_M_RD, |
172 | .buf = buf, |
173 | .len = len, |
174 | }, |
175 | }; |
176 | |
177 | put_unaligned_be16(val: reg, p: reg_buf); |
178 | /* |
179 | * During the measurement process, HDC3020 will not return data. |
180 | * So wait for a while and try again |
181 | */ |
182 | for (cnt = 0; cnt < HDC3020_READ_RETRY_TIMES; cnt++) { |
183 | ret = i2c_transfer(adap: client->adapter, msgs: msg, num: 2); |
184 | if (ret == 2) |
185 | return 0; |
186 | |
187 | mdelay(HDC3020_BUSY_DELAY_MS); |
188 | } |
189 | dev_err(&client->dev, "Could not read sensor data\n" ); |
190 | |
191 | return -ETIMEDOUT; |
192 | } |
193 | |
194 | static int hdc3020_read_be16(struct hdc3020_data *data, u16 reg) |
195 | { |
196 | u8 crc, buf[3]; |
197 | int ret; |
198 | |
199 | ret = hdc3020_read_bytes(data, reg, buf, len: 3); |
200 | if (ret < 0) |
201 | return ret; |
202 | |
203 | crc = crc8(table: hdc3020_crc8_table, pdata: buf, nbytes: 2, CRC8_INIT_VALUE); |
204 | if (crc != buf[2]) |
205 | return -EINVAL; |
206 | |
207 | return get_unaligned_be16(p: buf); |
208 | } |
209 | |
210 | static int hdc3020_exec_cmd(struct hdc3020_data *data, u16 reg) |
211 | { |
212 | u8 reg_buf[2]; |
213 | |
214 | put_unaligned_be16(val: reg, p: reg_buf); |
215 | return hdc3020_write_bytes(data, buf: reg_buf, len: 2); |
216 | } |
217 | |
218 | static int hdc3020_read_measurement(struct hdc3020_data *data, |
219 | enum iio_chan_type type, int *val) |
220 | { |
221 | u8 crc, buf[6]; |
222 | int ret; |
223 | |
224 | ret = hdc3020_read_bytes(data, HDC3020_R_T_RH_AUTO, buf, len: 6); |
225 | if (ret < 0) |
226 | return ret; |
227 | |
228 | /* CRC check of the temperature measurement */ |
229 | crc = crc8(table: hdc3020_crc8_table, pdata: buf, nbytes: 2, CRC8_INIT_VALUE); |
230 | if (crc != buf[2]) |
231 | return -EINVAL; |
232 | |
233 | /* CRC check of the relative humidity measurement */ |
234 | crc = crc8(table: hdc3020_crc8_table, pdata: buf + 3, nbytes: 2, CRC8_INIT_VALUE); |
235 | if (crc != buf[5]) |
236 | return -EINVAL; |
237 | |
238 | if (type == IIO_TEMP) |
239 | *val = get_unaligned_be16(p: buf); |
240 | else if (type == IIO_HUMIDITYRELATIVE) |
241 | *val = get_unaligned_be16(p: &buf[3]); |
242 | else |
243 | return -EINVAL; |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | static int hdc3020_read_raw(struct iio_dev *indio_dev, |
249 | struct iio_chan_spec const *chan, int *val, |
250 | int *val2, long mask) |
251 | { |
252 | struct hdc3020_data *data = iio_priv(indio_dev); |
253 | int ret; |
254 | |
255 | if (chan->type != IIO_TEMP && chan->type != IIO_HUMIDITYRELATIVE) |
256 | return -EINVAL; |
257 | |
258 | switch (mask) { |
259 | case IIO_CHAN_INFO_RAW: { |
260 | guard(mutex)(T: &data->lock); |
261 | ret = hdc3020_read_measurement(data, type: chan->type, val); |
262 | if (ret < 0) |
263 | return ret; |
264 | |
265 | return IIO_VAL_INT; |
266 | } |
267 | case IIO_CHAN_INFO_PEAK: { |
268 | guard(mutex)(T: &data->lock); |
269 | if (chan->type == IIO_TEMP) |
270 | ret = hdc3020_read_be16(data, HDC3020_R_T_HIGH_AUTO); |
271 | else |
272 | ret = hdc3020_read_be16(data, HDC3020_R_RH_HIGH_AUTO); |
273 | |
274 | if (ret < 0) |
275 | return ret; |
276 | |
277 | *val = ret; |
278 | return IIO_VAL_INT; |
279 | } |
280 | case IIO_CHAN_INFO_TROUGH: { |
281 | guard(mutex)(T: &data->lock); |
282 | if (chan->type == IIO_TEMP) |
283 | ret = hdc3020_read_be16(data, HDC3020_R_T_LOW_AUTO); |
284 | else |
285 | ret = hdc3020_read_be16(data, HDC3020_R_RH_LOW_AUTO); |
286 | |
287 | if (ret < 0) |
288 | return ret; |
289 | |
290 | *val = ret; |
291 | return IIO_VAL_INT; |
292 | } |
293 | case IIO_CHAN_INFO_SCALE: |
294 | *val2 = 65536; |
295 | if (chan->type == IIO_TEMP) |
296 | *val = 175; |
297 | else |
298 | *val = 100; |
299 | return IIO_VAL_FRACTIONAL; |
300 | |
301 | case IIO_CHAN_INFO_OFFSET: |
302 | if (chan->type != IIO_TEMP) |
303 | return -EINVAL; |
304 | |
305 | *val = -16852; |
306 | return IIO_VAL_INT; |
307 | |
308 | default: |
309 | return -EINVAL; |
310 | } |
311 | } |
312 | |
313 | static int hdc3020_read_available(struct iio_dev *indio_dev, |
314 | struct iio_chan_spec const *chan, |
315 | const int **vals, |
316 | int *type, int *length, long mask) |
317 | { |
318 | if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_CURRENT) |
319 | return -EINVAL; |
320 | |
321 | *vals = hdc3020_heater_vals; |
322 | *type = IIO_VAL_INT; |
323 | |
324 | return IIO_AVAIL_RANGE; |
325 | } |
326 | |
327 | static int hdc3020_update_heater(struct hdc3020_data *data, int val) |
328 | { |
329 | u8 buf[5]; |
330 | int ret; |
331 | |
332 | if (val < hdc3020_heater_vals[0] || val > hdc3020_heater_vals[2]) |
333 | return -EINVAL; |
334 | |
335 | if (!val) |
336 | hdc3020_exec_cmd(data, HDC3020_HEATER_DISABLE); |
337 | |
338 | put_unaligned_be16(HDC3020_HEATER_CONFIG, p: buf); |
339 | put_unaligned_be16(val: val & GENMASK(13, 0), p: &buf[2]); |
340 | buf[4] = crc8(table: hdc3020_crc8_table, pdata: buf + 2, nbytes: 2, CRC8_INIT_VALUE); |
341 | ret = hdc3020_write_bytes(data, buf, len: 5); |
342 | if (ret < 0) |
343 | return ret; |
344 | |
345 | return hdc3020_exec_cmd(data, HDC3020_HEATER_ENABLE); |
346 | } |
347 | |
348 | static int hdc3020_write_raw(struct iio_dev *indio_dev, |
349 | struct iio_chan_spec const *chan, |
350 | int val, int val2, long mask) |
351 | { |
352 | struct hdc3020_data *data = iio_priv(indio_dev); |
353 | |
354 | switch (mask) { |
355 | case IIO_CHAN_INFO_RAW: |
356 | if (chan->type != IIO_CURRENT) |
357 | return -EINVAL; |
358 | |
359 | guard(mutex)(T: &data->lock); |
360 | return hdc3020_update_heater(data, val); |
361 | } |
362 | |
363 | return -EINVAL; |
364 | } |
365 | |
366 | static int hdc3020_write_thresh(struct iio_dev *indio_dev, |
367 | const struct iio_chan_spec *chan, |
368 | enum iio_event_type type, |
369 | enum iio_event_direction dir, |
370 | enum iio_event_info info, |
371 | int val, int val2) |
372 | { |
373 | struct hdc3020_data *data = iio_priv(indio_dev); |
374 | u8 buf[5]; |
375 | u64 tmp; |
376 | u16 reg; |
377 | int ret; |
378 | |
379 | /* Supported temperature range is from –40 to 125 degree celsius */ |
380 | if (val < HDC3020_MIN_TEMP || val > HDC3020_MAX_TEMP) |
381 | return -EINVAL; |
382 | |
383 | /* Select threshold register */ |
384 | if (info == IIO_EV_INFO_VALUE) { |
385 | if (dir == IIO_EV_DIR_RISING) |
386 | reg = HDC3020_S_T_RH_THRESH_HIGH; |
387 | else |
388 | reg = HDC3020_S_T_RH_THRESH_LOW; |
389 | } else { |
390 | if (dir == IIO_EV_DIR_RISING) |
391 | reg = HDC3020_S_T_RH_THRESH_HIGH_CLR; |
392 | else |
393 | reg = HDC3020_S_T_RH_THRESH_LOW_CLR; |
394 | } |
395 | |
396 | guard(mutex)(T: &data->lock); |
397 | ret = hdc3020_read_be16(data, reg); |
398 | if (ret < 0) |
399 | return ret; |
400 | |
401 | switch (chan->type) { |
402 | case IIO_TEMP: |
403 | /* |
404 | * Calculate temperature threshold, shift it down to get the |
405 | * truncated threshold representation in the 9LSBs while keeping |
406 | * the current humidity threshold in the 7 MSBs. |
407 | */ |
408 | tmp = ((u64)(((val + 45) * MICRO) + val2)) * 65535ULL; |
409 | tmp = div_u64(dividend: tmp, MICRO * 175); |
410 | val = tmp >> HDC3020_THRESH_TEMP_TRUNC_SHIFT; |
411 | val = FIELD_PREP(HDC3020_THRESH_TEMP_MASK, val); |
412 | val |= (FIELD_GET(HDC3020_THRESH_HUM_MASK, ret) << |
413 | HDC3020_THRESH_HUM_TRUNC_SHIFT); |
414 | break; |
415 | case IIO_HUMIDITYRELATIVE: |
416 | /* |
417 | * Calculate humidity threshold, shift it down and up to get the |
418 | * truncated threshold representation in the 7MSBs while keeping |
419 | * the current temperature threshold in the 9 LSBs. |
420 | */ |
421 | tmp = ((u64)((val * MICRO) + val2)) * 65535ULL; |
422 | tmp = div_u64(dividend: tmp, MICRO * 100); |
423 | val = tmp >> HDC3020_THRESH_HUM_TRUNC_SHIFT; |
424 | val = FIELD_PREP(HDC3020_THRESH_HUM_MASK, val); |
425 | val |= FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret); |
426 | break; |
427 | default: |
428 | return -EOPNOTSUPP; |
429 | } |
430 | |
431 | put_unaligned_be16(val: reg, p: buf); |
432 | put_unaligned_be16(val, p: buf + 2); |
433 | buf[4] = crc8(table: hdc3020_crc8_table, pdata: buf + 2, nbytes: 2, CRC8_INIT_VALUE); |
434 | return hdc3020_write_bytes(data, buf, len: 5); |
435 | } |
436 | |
437 | static int hdc3020_read_thresh(struct iio_dev *indio_dev, |
438 | const struct iio_chan_spec *chan, |
439 | enum iio_event_type type, |
440 | enum iio_event_direction dir, |
441 | enum iio_event_info info, |
442 | int *val, int *val2) |
443 | { |
444 | struct hdc3020_data *data = iio_priv(indio_dev); |
445 | u16 reg; |
446 | int ret; |
447 | |
448 | /* Select threshold register */ |
449 | if (info == IIO_EV_INFO_VALUE) { |
450 | if (dir == IIO_EV_DIR_RISING) |
451 | reg = HDC3020_R_T_RH_THRESH_HIGH; |
452 | else |
453 | reg = HDC3020_R_T_RH_THRESH_LOW; |
454 | } else { |
455 | if (dir == IIO_EV_DIR_RISING) |
456 | reg = HDC3020_R_T_RH_THRESH_HIGH_CLR; |
457 | else |
458 | reg = HDC3020_R_T_RH_THRESH_LOW_CLR; |
459 | } |
460 | |
461 | guard(mutex)(T: &data->lock); |
462 | ret = hdc3020_read_be16(data, reg); |
463 | if (ret < 0) |
464 | return ret; |
465 | |
466 | switch (chan->type) { |
467 | case IIO_TEMP: |
468 | /* |
469 | * Get the temperature threshold from 9 LSBs, shift them to get |
470 | * the truncated temperature threshold representation and |
471 | * calculate the threshold according to the formula in the |
472 | * datasheet. |
473 | */ |
474 | *val = FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret); |
475 | *val = *val << HDC3020_THRESH_TEMP_TRUNC_SHIFT; |
476 | *val = -2949075 + (175 * (*val)); |
477 | *val2 = 65535; |
478 | return IIO_VAL_FRACTIONAL; |
479 | case IIO_HUMIDITYRELATIVE: |
480 | /* |
481 | * Get the humidity threshold from 7 MSBs, shift them to get the |
482 | * truncated humidity threshold representation and calculate the |
483 | * threshold according to the formula in the datasheet. |
484 | */ |
485 | *val = FIELD_GET(HDC3020_THRESH_HUM_MASK, ret); |
486 | *val = (*val << HDC3020_THRESH_HUM_TRUNC_SHIFT) * 100; |
487 | *val2 = 65535; |
488 | return IIO_VAL_FRACTIONAL; |
489 | default: |
490 | return -EOPNOTSUPP; |
491 | } |
492 | } |
493 | |
494 | static irqreturn_t hdc3020_interrupt_handler(int irq, void *private) |
495 | { |
496 | struct iio_dev *indio_dev = private; |
497 | struct hdc3020_data *data; |
498 | s64 time; |
499 | int ret; |
500 | |
501 | data = iio_priv(indio_dev); |
502 | ret = hdc3020_read_be16(data, HDC3020_R_STATUS); |
503 | if (ret < 0) |
504 | return IRQ_HANDLED; |
505 | |
506 | if (!(ret & (HDC3020_STATUS_T_HIGH_ALERT | HDC3020_STATUS_T_LOW_ALERT | |
507 | HDC3020_STATUS_RH_HIGH_ALERT | HDC3020_STATUS_RH_LOW_ALERT))) |
508 | return IRQ_NONE; |
509 | |
510 | time = iio_get_time_ns(indio_dev); |
511 | if (ret & HDC3020_STATUS_T_HIGH_ALERT) |
512 | iio_push_event(indio_dev, |
513 | IIO_MOD_EVENT_CODE(IIO_TEMP, 0, |
514 | IIO_NO_MOD, |
515 | IIO_EV_TYPE_THRESH, |
516 | IIO_EV_DIR_RISING), |
517 | timestamp: time); |
518 | |
519 | if (ret & HDC3020_STATUS_T_LOW_ALERT) |
520 | iio_push_event(indio_dev, |
521 | IIO_MOD_EVENT_CODE(IIO_TEMP, 0, |
522 | IIO_NO_MOD, |
523 | IIO_EV_TYPE_THRESH, |
524 | IIO_EV_DIR_FALLING), |
525 | timestamp: time); |
526 | |
527 | if (ret & HDC3020_STATUS_RH_HIGH_ALERT) |
528 | iio_push_event(indio_dev, |
529 | IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0, |
530 | IIO_NO_MOD, |
531 | IIO_EV_TYPE_THRESH, |
532 | IIO_EV_DIR_RISING), |
533 | timestamp: time); |
534 | |
535 | if (ret & HDC3020_STATUS_RH_LOW_ALERT) |
536 | iio_push_event(indio_dev, |
537 | IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0, |
538 | IIO_NO_MOD, |
539 | IIO_EV_TYPE_THRESH, |
540 | IIO_EV_DIR_FALLING), |
541 | timestamp: time); |
542 | |
543 | return IRQ_HANDLED; |
544 | } |
545 | |
546 | static const struct iio_info hdc3020_info = { |
547 | .read_raw = hdc3020_read_raw, |
548 | .write_raw = hdc3020_write_raw, |
549 | .read_avail = hdc3020_read_available, |
550 | .read_event_value = hdc3020_read_thresh, |
551 | .write_event_value = hdc3020_write_thresh, |
552 | }; |
553 | |
554 | static void hdc3020_stop(void *data) |
555 | { |
556 | hdc3020_exec_cmd(data: (struct hdc3020_data *)data, HDC3020_EXIT_AUTO); |
557 | } |
558 | |
559 | static int hdc3020_probe(struct i2c_client *client) |
560 | { |
561 | struct iio_dev *indio_dev; |
562 | struct hdc3020_data *data; |
563 | int ret; |
564 | |
565 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) |
566 | return -EOPNOTSUPP; |
567 | |
568 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
569 | if (!indio_dev) |
570 | return -ENOMEM; |
571 | |
572 | data = iio_priv(indio_dev); |
573 | data->client = client; |
574 | mutex_init(&data->lock); |
575 | |
576 | crc8_populate_msb(table: hdc3020_crc8_table, HDC3020_CRC8_POLYNOMIAL); |
577 | |
578 | indio_dev->name = "hdc3020" ; |
579 | indio_dev->modes = INDIO_DIRECT_MODE; |
580 | indio_dev->info = &hdc3020_info; |
581 | indio_dev->channels = hdc3020_channels; |
582 | indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels); |
583 | if (client->irq) { |
584 | ret = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, |
585 | NULL, thread_fn: hdc3020_interrupt_handler, |
586 | IRQF_ONESHOT, devname: "hdc3020" , |
587 | dev_id: indio_dev); |
588 | if (ret) |
589 | return dev_err_probe(dev: &client->dev, err: ret, |
590 | fmt: "Failed to request IRQ\n" ); |
591 | |
592 | /* |
593 | * The alert output is activated by default upon power up, |
594 | * hardware reset, and soft reset. Clear the status register. |
595 | */ |
596 | ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS); |
597 | if (ret) |
598 | return ret; |
599 | } |
600 | |
601 | ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0); |
602 | if (ret) |
603 | return dev_err_probe(dev: &client->dev, err: ret, |
604 | fmt: "Unable to set up measurement\n" ); |
605 | |
606 | ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data); |
607 | if (ret) |
608 | return ret; |
609 | |
610 | ret = devm_iio_device_register(&data->client->dev, indio_dev); |
611 | if (ret) |
612 | return dev_err_probe(dev: &client->dev, err: ret, fmt: "Failed to add device" ); |
613 | |
614 | return 0; |
615 | } |
616 | |
617 | static const struct i2c_device_id hdc3020_id[] = { |
618 | { "hdc3020" }, |
619 | { "hdc3021" }, |
620 | { "hdc3022" }, |
621 | { } |
622 | }; |
623 | MODULE_DEVICE_TABLE(i2c, hdc3020_id); |
624 | |
625 | static const struct of_device_id hdc3020_dt_ids[] = { |
626 | { .compatible = "ti,hdc3020" }, |
627 | { .compatible = "ti,hdc3021" }, |
628 | { .compatible = "ti,hdc3022" }, |
629 | { } |
630 | }; |
631 | MODULE_DEVICE_TABLE(of, hdc3020_dt_ids); |
632 | |
633 | static struct i2c_driver hdc3020_driver = { |
634 | .driver = { |
635 | .name = "hdc3020" , |
636 | .of_match_table = hdc3020_dt_ids, |
637 | }, |
638 | .probe = hdc3020_probe, |
639 | .id_table = hdc3020_id, |
640 | }; |
641 | module_i2c_driver(hdc3020_driver); |
642 | |
643 | MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>" ); |
644 | MODULE_AUTHOR("Li peiyu <579lpy@gmail.com>" ); |
645 | MODULE_DESCRIPTION("TI HDC3020 humidity and temperature sensor driver" ); |
646 | MODULE_LICENSE("GPL" ); |
647 | |