1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Sensirion SCD4X carbon dioxide sensor i2c driver |
4 | * |
5 | * Copyright (C) 2021 Protonic Holland |
6 | * Author: Roan van Dijk <roan@protonic.nl> |
7 | * |
8 | * I2C slave address: 0x62 |
9 | * |
10 | * Datasheets: |
11 | * https://www.sensirion.com/file/datasheet_scd4x |
12 | */ |
13 | |
14 | #include <asm/unaligned.h> |
15 | #include <linux/crc8.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/device.h> |
18 | #include <linux/i2c.h> |
19 | #include <linux/iio/buffer.h> |
20 | #include <linux/iio/iio.h> |
21 | #include <linux/iio/sysfs.h> |
22 | #include <linux/iio/trigger.h> |
23 | #include <linux/iio/trigger_consumer.h> |
24 | #include <linux/iio/triggered_buffer.h> |
25 | #include <linux/iio/types.h> |
26 | #include <linux/kernel.h> |
27 | #include <linux/mutex.h> |
28 | #include <linux/string.h> |
29 | #include <linux/sysfs.h> |
30 | #include <linux/types.h> |
31 | |
32 | #define SCD4X_CRC8_POLYNOMIAL 0x31 |
33 | #define SCD4X_TIMEOUT_ERR 1000 |
34 | #define SCD4X_READ_BUF_SIZE 9 |
35 | #define SCD4X_COMMAND_BUF_SIZE 2 |
36 | #define SCD4X_WRITE_BUF_SIZE 5 |
37 | #define SCD4X_FRC_MIN_PPM 0 |
38 | #define SCD4X_FRC_MAX_PPM 2000 |
39 | #define SCD4X_PRESSURE_COMP_MIN_MBAR 700 |
40 | #define SCD4X_PRESSURE_COMP_MAX_MBAR 1200 |
41 | #define SCD4X_READY_MASK 0x01 |
42 | |
43 | /*Commands SCD4X*/ |
44 | enum scd4x_cmd { |
45 | CMD_START_MEAS = 0x21b1, |
46 | CMD_READ_MEAS = 0xec05, |
47 | CMD_STOP_MEAS = 0x3f86, |
48 | CMD_SET_TEMP_OFFSET = 0x241d, |
49 | CMD_GET_TEMP_OFFSET = 0x2318, |
50 | CMD_SET_AMB_PRESSURE = 0xe000, |
51 | CMD_GET_AMB_PRESSURE = 0xe000, |
52 | CMD_FRC = 0x362f, |
53 | CMD_SET_ASC = 0x2416, |
54 | CMD_GET_ASC = 0x2313, |
55 | CMD_GET_DATA_READY = 0xe4b8, |
56 | }; |
57 | |
58 | enum scd4x_channel_idx { |
59 | SCD4X_CO2, |
60 | SCD4X_TEMP, |
61 | SCD4X_HR, |
62 | }; |
63 | |
64 | struct scd4x_state { |
65 | struct i2c_client *client; |
66 | /* maintain access to device, to prevent concurrent reads/writes */ |
67 | struct mutex lock; |
68 | struct regulator *vdd; |
69 | }; |
70 | |
71 | DECLARE_CRC8_TABLE(scd4x_crc8_table); |
72 | |
73 | static int scd4x_i2c_xfer(struct scd4x_state *state, char *txbuf, int txsize, |
74 | char *rxbuf, int rxsize) |
75 | { |
76 | struct i2c_client *client = state->client; |
77 | int ret; |
78 | |
79 | ret = i2c_master_send(client, buf: txbuf, count: txsize); |
80 | |
81 | if (ret < 0) |
82 | return ret; |
83 | if (ret != txsize) |
84 | return -EIO; |
85 | |
86 | if (rxsize == 0) |
87 | return 0; |
88 | |
89 | ret = i2c_master_recv(client, buf: rxbuf, count: rxsize); |
90 | if (ret < 0) |
91 | return ret; |
92 | if (ret != rxsize) |
93 | return -EIO; |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | static int scd4x_send_command(struct scd4x_state *state, enum scd4x_cmd cmd) |
99 | { |
100 | char buf[SCD4X_COMMAND_BUF_SIZE]; |
101 | int ret; |
102 | |
103 | /* |
104 | * Measurement needs to be stopped before sending commands. |
105 | * Except stop and start command. |
106 | */ |
107 | if ((cmd != CMD_STOP_MEAS) && (cmd != CMD_START_MEAS)) { |
108 | |
109 | ret = scd4x_send_command(state, cmd: CMD_STOP_MEAS); |
110 | if (ret) |
111 | return ret; |
112 | |
113 | /* execution time for stopping measurement */ |
114 | msleep_interruptible(msecs: 500); |
115 | } |
116 | |
117 | put_unaligned_be16(val: cmd, p: buf); |
118 | ret = scd4x_i2c_xfer(state, txbuf: buf, txsize: 2, rxbuf: buf, rxsize: 0); |
119 | if (ret) |
120 | return ret; |
121 | |
122 | if ((cmd != CMD_STOP_MEAS) && (cmd != CMD_START_MEAS)) { |
123 | ret = scd4x_send_command(state, cmd: CMD_START_MEAS); |
124 | if (ret) |
125 | return ret; |
126 | } |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd, |
132 | void *response, int response_sz) |
133 | { |
134 | struct i2c_client *client = state->client; |
135 | char buf[SCD4X_READ_BUF_SIZE]; |
136 | char *rsp = response; |
137 | int i, ret; |
138 | char crc; |
139 | |
140 | /* |
141 | * Measurement needs to be stopped before sending commands. |
142 | * Except for reading measurement and data ready command. |
143 | */ |
144 | if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) && |
145 | (cmd != CMD_GET_AMB_PRESSURE)) { |
146 | ret = scd4x_send_command(state, cmd: CMD_STOP_MEAS); |
147 | if (ret) |
148 | return ret; |
149 | |
150 | /* execution time for stopping measurement */ |
151 | msleep_interruptible(msecs: 500); |
152 | } |
153 | |
154 | /* CRC byte for every 2 bytes of data */ |
155 | response_sz += response_sz / 2; |
156 | |
157 | put_unaligned_be16(val: cmd, p: buf); |
158 | ret = scd4x_i2c_xfer(state, txbuf: buf, txsize: 2, rxbuf: buf, rxsize: response_sz); |
159 | if (ret) |
160 | return ret; |
161 | |
162 | for (i = 0; i < response_sz; i += 3) { |
163 | crc = crc8(table: scd4x_crc8_table, pdata: buf + i, nbytes: 2, CRC8_INIT_VALUE); |
164 | if (crc != buf[i + 2]) { |
165 | dev_err(&client->dev, "CRC error\n" ); |
166 | return -EIO; |
167 | } |
168 | |
169 | *rsp++ = buf[i]; |
170 | *rsp++ = buf[i + 1]; |
171 | } |
172 | |
173 | /* start measurement */ |
174 | if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) && |
175 | (cmd != CMD_GET_AMB_PRESSURE)) { |
176 | ret = scd4x_send_command(state, cmd: CMD_START_MEAS); |
177 | if (ret) |
178 | return ret; |
179 | } |
180 | |
181 | return 0; |
182 | } |
183 | |
184 | static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t arg) |
185 | { |
186 | char buf[SCD4X_WRITE_BUF_SIZE]; |
187 | int ret; |
188 | char crc; |
189 | |
190 | put_unaligned_be16(val: cmd, p: buf); |
191 | put_unaligned_be16(val: arg, p: buf + 2); |
192 | |
193 | crc = crc8(table: scd4x_crc8_table, pdata: buf + 2, nbytes: 2, CRC8_INIT_VALUE); |
194 | buf[4] = crc; |
195 | |
196 | /* measurement needs to be stopped before sending commands */ |
197 | if (cmd != CMD_SET_AMB_PRESSURE) { |
198 | ret = scd4x_send_command(state, cmd: CMD_STOP_MEAS); |
199 | if (ret) |
200 | return ret; |
201 | } |
202 | |
203 | /* execution time */ |
204 | msleep_interruptible(msecs: 500); |
205 | |
206 | ret = scd4x_i2c_xfer(state, txbuf: buf, SCD4X_WRITE_BUF_SIZE, rxbuf: buf, rxsize: 0); |
207 | if (ret) |
208 | return ret; |
209 | |
210 | /* start measurement, except for forced calibration command */ |
211 | if ((cmd != CMD_FRC) && (cmd != CMD_SET_AMB_PRESSURE)) { |
212 | ret = scd4x_send_command(state, cmd: CMD_START_MEAS); |
213 | if (ret) |
214 | return ret; |
215 | } |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | static int scd4x_write_and_fetch(struct scd4x_state *state, enum scd4x_cmd cmd, |
221 | uint16_t arg, void *response, int response_sz) |
222 | { |
223 | struct i2c_client *client = state->client; |
224 | char buf[SCD4X_READ_BUF_SIZE]; |
225 | char *rsp = response; |
226 | int i, ret; |
227 | char crc; |
228 | |
229 | ret = scd4x_write(state, cmd: CMD_FRC, arg); |
230 | if (ret) |
231 | goto err; |
232 | |
233 | /* execution time */ |
234 | msleep_interruptible(msecs: 400); |
235 | |
236 | /* CRC byte for every 2 bytes of data */ |
237 | response_sz += response_sz / 2; |
238 | |
239 | ret = i2c_master_recv(client, buf, count: response_sz); |
240 | if (ret < 0) |
241 | goto err; |
242 | if (ret != response_sz) { |
243 | ret = -EIO; |
244 | goto err; |
245 | } |
246 | |
247 | for (i = 0; i < response_sz; i += 3) { |
248 | crc = crc8(table: scd4x_crc8_table, pdata: buf + i, nbytes: 2, CRC8_INIT_VALUE); |
249 | if (crc != buf[i + 2]) { |
250 | dev_err(&client->dev, "CRC error\n" ); |
251 | ret = -EIO; |
252 | goto err; |
253 | } |
254 | |
255 | *rsp++ = buf[i]; |
256 | *rsp++ = buf[i + 1]; |
257 | } |
258 | |
259 | return scd4x_send_command(state, cmd: CMD_START_MEAS); |
260 | |
261 | err: |
262 | /* |
263 | * on error try to start the measurement, |
264 | * puts sensor back into continuous measurement |
265 | */ |
266 | scd4x_send_command(state, cmd: CMD_START_MEAS); |
267 | |
268 | return ret; |
269 | } |
270 | |
271 | static int scd4x_read_meas(struct scd4x_state *state, uint16_t *meas) |
272 | { |
273 | int i, ret; |
274 | __be16 buf[3]; |
275 | |
276 | ret = scd4x_read(state, cmd: CMD_READ_MEAS, response: buf, response_sz: sizeof(buf)); |
277 | if (ret) |
278 | return ret; |
279 | |
280 | for (i = 0; i < ARRAY_SIZE(buf); i++) |
281 | meas[i] = be16_to_cpu(buf[i]); |
282 | |
283 | return 0; |
284 | } |
285 | |
286 | static int scd4x_wait_meas_poll(struct scd4x_state *state) |
287 | { |
288 | struct i2c_client *client = state->client; |
289 | int tries = 6; |
290 | int ret; |
291 | |
292 | do { |
293 | __be16 bval; |
294 | uint16_t val; |
295 | |
296 | ret = scd4x_read(state, cmd: CMD_GET_DATA_READY, response: &bval, response_sz: sizeof(bval)); |
297 | if (ret) |
298 | return -EIO; |
299 | val = be16_to_cpu(bval); |
300 | |
301 | /* new measurement available */ |
302 | if (val & 0x7FF) |
303 | return 0; |
304 | |
305 | msleep_interruptible(msecs: 1000); |
306 | } while (--tries); |
307 | |
308 | /* try to start sensor on timeout */ |
309 | ret = scd4x_send_command(state, cmd: CMD_START_MEAS); |
310 | if (ret) |
311 | dev_err(&client->dev, "failed to start measurement: %d\n" , ret); |
312 | |
313 | return -ETIMEDOUT; |
314 | } |
315 | |
316 | static int scd4x_read_poll(struct scd4x_state *state, uint16_t *buf) |
317 | { |
318 | int ret; |
319 | |
320 | ret = scd4x_wait_meas_poll(state); |
321 | if (ret) |
322 | return ret; |
323 | |
324 | return scd4x_read_meas(state, meas: buf); |
325 | } |
326 | |
327 | static int scd4x_read_channel(struct scd4x_state *state, int chan) |
328 | { |
329 | int ret; |
330 | uint16_t buf[3]; |
331 | |
332 | ret = scd4x_read_poll(state, buf); |
333 | if (ret) |
334 | return ret; |
335 | |
336 | return buf[chan]; |
337 | } |
338 | |
339 | static int scd4x_read_raw(struct iio_dev *indio_dev, |
340 | struct iio_chan_spec const *chan, int *val, |
341 | int *val2, long mask) |
342 | { |
343 | struct scd4x_state *state = iio_priv(indio_dev); |
344 | int ret; |
345 | __be16 tmp; |
346 | |
347 | switch (mask) { |
348 | case IIO_CHAN_INFO_RAW: |
349 | if (chan->output) { |
350 | mutex_lock(&state->lock); |
351 | ret = scd4x_read(state, cmd: CMD_GET_AMB_PRESSURE, response: &tmp, response_sz: sizeof(tmp)); |
352 | mutex_unlock(lock: &state->lock); |
353 | |
354 | if (ret) |
355 | return ret; |
356 | |
357 | *val = be16_to_cpu(tmp); |
358 | return IIO_VAL_INT; |
359 | } |
360 | |
361 | ret = iio_device_claim_direct_mode(indio_dev); |
362 | if (ret) |
363 | return ret; |
364 | |
365 | mutex_lock(&state->lock); |
366 | ret = scd4x_read_channel(state, chan: chan->address); |
367 | mutex_unlock(lock: &state->lock); |
368 | |
369 | iio_device_release_direct_mode(indio_dev); |
370 | if (ret < 0) |
371 | return ret; |
372 | |
373 | *val = ret; |
374 | return IIO_VAL_INT; |
375 | case IIO_CHAN_INFO_SCALE: |
376 | if (chan->type == IIO_CONCENTRATION) { |
377 | *val = 0; |
378 | *val2 = 100; |
379 | return IIO_VAL_INT_PLUS_MICRO; |
380 | } else if (chan->type == IIO_TEMP) { |
381 | *val = 175000; |
382 | *val2 = 65536; |
383 | return IIO_VAL_FRACTIONAL; |
384 | } else if (chan->type == IIO_HUMIDITYRELATIVE) { |
385 | *val = 100000; |
386 | *val2 = 65536; |
387 | return IIO_VAL_FRACTIONAL; |
388 | } |
389 | return -EINVAL; |
390 | case IIO_CHAN_INFO_OFFSET: |
391 | *val = -16852; |
392 | *val2 = 114286; |
393 | return IIO_VAL_INT_PLUS_MICRO; |
394 | case IIO_CHAN_INFO_CALIBBIAS: |
395 | mutex_lock(&state->lock); |
396 | ret = scd4x_read(state, cmd: CMD_GET_TEMP_OFFSET, response: &tmp, response_sz: sizeof(tmp)); |
397 | mutex_unlock(lock: &state->lock); |
398 | if (ret) |
399 | return ret; |
400 | |
401 | *val = be16_to_cpu(tmp); |
402 | |
403 | return IIO_VAL_INT; |
404 | default: |
405 | return -EINVAL; |
406 | } |
407 | } |
408 | |
409 | static const int scd4x_pressure_calibbias_available[] = { |
410 | SCD4X_PRESSURE_COMP_MIN_MBAR, 1, SCD4X_PRESSURE_COMP_MAX_MBAR, |
411 | }; |
412 | |
413 | static int scd4x_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, |
414 | const int **vals, int *type, int *length, long mask) |
415 | { |
416 | switch (mask) { |
417 | case IIO_CHAN_INFO_RAW: |
418 | *vals = scd4x_pressure_calibbias_available; |
419 | *type = IIO_VAL_INT; |
420 | |
421 | return IIO_AVAIL_RANGE; |
422 | } |
423 | |
424 | return -EINVAL; |
425 | } |
426 | |
427 | |
428 | static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, |
429 | int val, int val2, long mask) |
430 | { |
431 | struct scd4x_state *state = iio_priv(indio_dev); |
432 | int ret = 0; |
433 | |
434 | switch (mask) { |
435 | case IIO_CHAN_INFO_CALIBBIAS: |
436 | mutex_lock(&state->lock); |
437 | ret = scd4x_write(state, cmd: CMD_SET_TEMP_OFFSET, arg: val); |
438 | mutex_unlock(lock: &state->lock); |
439 | |
440 | return ret; |
441 | case IIO_CHAN_INFO_RAW: |
442 | switch (chan->type) { |
443 | case IIO_PRESSURE: |
444 | if (val < SCD4X_PRESSURE_COMP_MIN_MBAR || |
445 | val > SCD4X_PRESSURE_COMP_MAX_MBAR) |
446 | return -EINVAL; |
447 | |
448 | mutex_lock(&state->lock); |
449 | ret = scd4x_write(state, cmd: CMD_SET_AMB_PRESSURE, arg: val); |
450 | mutex_unlock(lock: &state->lock); |
451 | |
452 | return ret; |
453 | default: |
454 | return -EINVAL; |
455 | } |
456 | default: |
457 | return -EINVAL; |
458 | } |
459 | } |
460 | |
461 | static ssize_t calibration_auto_enable_show(struct device *dev, |
462 | struct device_attribute *attr, char *buf) |
463 | { |
464 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
465 | struct scd4x_state *state = iio_priv(indio_dev); |
466 | int ret; |
467 | __be16 bval; |
468 | u16 val; |
469 | |
470 | mutex_lock(&state->lock); |
471 | ret = scd4x_read(state, cmd: CMD_GET_ASC, response: &bval, response_sz: sizeof(bval)); |
472 | mutex_unlock(lock: &state->lock); |
473 | if (ret) { |
474 | dev_err(dev, "failed to read automatic calibration" ); |
475 | return ret; |
476 | } |
477 | |
478 | val = (be16_to_cpu(bval) & SCD4X_READY_MASK) ? 1 : 0; |
479 | |
480 | return sysfs_emit(buf, fmt: "%d\n" , val); |
481 | } |
482 | |
483 | static ssize_t calibration_auto_enable_store(struct device *dev, |
484 | struct device_attribute *attr, |
485 | const char *buf, size_t len) |
486 | { |
487 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
488 | struct scd4x_state *state = iio_priv(indio_dev); |
489 | bool val; |
490 | int ret; |
491 | uint16_t value; |
492 | |
493 | ret = kstrtobool(s: buf, res: &val); |
494 | if (ret) |
495 | return ret; |
496 | |
497 | value = val; |
498 | |
499 | mutex_lock(&state->lock); |
500 | ret = scd4x_write(state, cmd: CMD_SET_ASC, arg: value); |
501 | mutex_unlock(lock: &state->lock); |
502 | if (ret) |
503 | dev_err(dev, "failed to set automatic calibration" ); |
504 | |
505 | return ret ?: len; |
506 | } |
507 | |
508 | static ssize_t calibration_forced_value_store(struct device *dev, |
509 | struct device_attribute *attr, |
510 | const char *buf, size_t len) |
511 | { |
512 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
513 | struct scd4x_state *state = iio_priv(indio_dev); |
514 | uint16_t val, arg; |
515 | int ret; |
516 | |
517 | ret = kstrtou16(s: buf, base: 0, res: &arg); |
518 | if (ret) |
519 | return ret; |
520 | |
521 | if (arg < SCD4X_FRC_MIN_PPM || arg > SCD4X_FRC_MAX_PPM) |
522 | return -EINVAL; |
523 | |
524 | mutex_lock(&state->lock); |
525 | ret = scd4x_write_and_fetch(state, cmd: CMD_FRC, arg, response: &val, response_sz: sizeof(val)); |
526 | mutex_unlock(lock: &state->lock); |
527 | |
528 | if (ret) |
529 | return ret; |
530 | |
531 | if (val == 0xff) { |
532 | dev_err(dev, "forced calibration has failed" ); |
533 | return -EINVAL; |
534 | } |
535 | |
536 | return len; |
537 | } |
538 | |
539 | static IIO_DEVICE_ATTR_RW(calibration_auto_enable, 0); |
540 | static IIO_DEVICE_ATTR_WO(calibration_forced_value, 0); |
541 | |
542 | static IIO_CONST_ATTR(calibration_forced_value_available, |
543 | __stringify([SCD4X_FRC_MIN_PPM 1 SCD4X_FRC_MAX_PPM])); |
544 | |
545 | static struct attribute *scd4x_attrs[] = { |
546 | &iio_dev_attr_calibration_auto_enable.dev_attr.attr, |
547 | &iio_dev_attr_calibration_forced_value.dev_attr.attr, |
548 | &iio_const_attr_calibration_forced_value_available.dev_attr.attr, |
549 | NULL |
550 | }; |
551 | |
552 | static const struct attribute_group scd4x_attr_group = { |
553 | .attrs = scd4x_attrs, |
554 | }; |
555 | |
556 | static const struct iio_info scd4x_info = { |
557 | .attrs = &scd4x_attr_group, |
558 | .read_raw = scd4x_read_raw, |
559 | .write_raw = scd4x_write_raw, |
560 | .read_avail = scd4x_read_avail, |
561 | }; |
562 | |
563 | static const struct iio_chan_spec scd4x_channels[] = { |
564 | { |
565 | /* |
566 | * this channel is special in a sense we are pretending that |
567 | * sensor is able to change measurement chamber pressure but in |
568 | * fact we're just setting pressure compensation value |
569 | */ |
570 | .type = IIO_PRESSURE, |
571 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
572 | .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), |
573 | .output = 1, |
574 | .scan_index = -1, |
575 | }, |
576 | { |
577 | .type = IIO_CONCENTRATION, |
578 | .channel2 = IIO_MOD_CO2, |
579 | .modified = 1, |
580 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
581 | BIT(IIO_CHAN_INFO_SCALE), |
582 | .address = SCD4X_CO2, |
583 | .scan_index = SCD4X_CO2, |
584 | .scan_type = { |
585 | .sign = 'u', |
586 | .realbits = 16, |
587 | .storagebits = 16, |
588 | .endianness = IIO_BE, |
589 | }, |
590 | }, |
591 | { |
592 | .type = IIO_TEMP, |
593 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
594 | BIT(IIO_CHAN_INFO_SCALE) | |
595 | BIT(IIO_CHAN_INFO_OFFSET) | |
596 | BIT(IIO_CHAN_INFO_CALIBBIAS), |
597 | .address = SCD4X_TEMP, |
598 | .scan_index = SCD4X_TEMP, |
599 | .scan_type = { |
600 | .sign = 'u', |
601 | .realbits = 16, |
602 | .storagebits = 16, |
603 | .endianness = IIO_BE, |
604 | }, |
605 | }, |
606 | { |
607 | .type = IIO_HUMIDITYRELATIVE, |
608 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
609 | BIT(IIO_CHAN_INFO_SCALE), |
610 | .address = SCD4X_HR, |
611 | .scan_index = SCD4X_HR, |
612 | .scan_type = { |
613 | .sign = 'u', |
614 | .realbits = 16, |
615 | .storagebits = 16, |
616 | .endianness = IIO_BE, |
617 | }, |
618 | }, |
619 | }; |
620 | |
621 | static int scd4x_suspend(struct device *dev) |
622 | { |
623 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
624 | struct scd4x_state *state = iio_priv(indio_dev); |
625 | int ret; |
626 | |
627 | ret = scd4x_send_command(state, cmd: CMD_STOP_MEAS); |
628 | if (ret) |
629 | return ret; |
630 | |
631 | return regulator_disable(regulator: state->vdd); |
632 | } |
633 | |
634 | static int scd4x_resume(struct device *dev) |
635 | { |
636 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
637 | struct scd4x_state *state = iio_priv(indio_dev); |
638 | int ret; |
639 | |
640 | ret = regulator_enable(regulator: state->vdd); |
641 | if (ret) |
642 | return ret; |
643 | |
644 | return scd4x_send_command(state, cmd: CMD_START_MEAS); |
645 | } |
646 | |
647 | static DEFINE_SIMPLE_DEV_PM_OPS(scd4x_pm_ops, scd4x_suspend, scd4x_resume); |
648 | |
649 | static void scd4x_stop_meas(void *state) |
650 | { |
651 | scd4x_send_command(state, cmd: CMD_STOP_MEAS); |
652 | } |
653 | |
654 | static void scd4x_disable_regulator(void *data) |
655 | { |
656 | struct scd4x_state *state = data; |
657 | |
658 | regulator_disable(regulator: state->vdd); |
659 | } |
660 | |
661 | static irqreturn_t scd4x_trigger_handler(int irq, void *p) |
662 | { |
663 | struct iio_poll_func *pf = p; |
664 | struct iio_dev *indio_dev = pf->indio_dev; |
665 | struct scd4x_state *state = iio_priv(indio_dev); |
666 | struct { |
667 | uint16_t data[3]; |
668 | int64_t ts __aligned(8); |
669 | } scan; |
670 | int ret; |
671 | |
672 | memset(&scan, 0, sizeof(scan)); |
673 | mutex_lock(&state->lock); |
674 | ret = scd4x_read_poll(state, buf: scan.data); |
675 | mutex_unlock(lock: &state->lock); |
676 | if (ret) |
677 | goto out; |
678 | |
679 | iio_push_to_buffers_with_timestamp(indio_dev, data: &scan, timestamp: iio_get_time_ns(indio_dev)); |
680 | out: |
681 | iio_trigger_notify_done(trig: indio_dev->trig); |
682 | return IRQ_HANDLED; |
683 | } |
684 | |
685 | static int scd4x_probe(struct i2c_client *client) |
686 | { |
687 | static const unsigned long scd4x_scan_masks[] = { 0x07, 0x00 }; |
688 | struct device *dev = &client->dev; |
689 | struct iio_dev *indio_dev; |
690 | struct scd4x_state *state; |
691 | int ret; |
692 | |
693 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*state)); |
694 | if (!indio_dev) |
695 | return -ENOMEM; |
696 | |
697 | state = iio_priv(indio_dev); |
698 | mutex_init(&state->lock); |
699 | state->client = client; |
700 | crc8_populate_msb(table: scd4x_crc8_table, SCD4X_CRC8_POLYNOMIAL); |
701 | |
702 | indio_dev->info = &scd4x_info; |
703 | indio_dev->name = client->name; |
704 | indio_dev->channels = scd4x_channels; |
705 | indio_dev->num_channels = ARRAY_SIZE(scd4x_channels); |
706 | indio_dev->modes = INDIO_DIRECT_MODE; |
707 | indio_dev->available_scan_masks = scd4x_scan_masks; |
708 | |
709 | state->vdd = devm_regulator_get(dev, id: "vdd" ); |
710 | if (IS_ERR(ptr: state->vdd)) |
711 | return dev_err_probe(dev, err: PTR_ERR(ptr: state->vdd), fmt: "failed to get regulator\n" ); |
712 | |
713 | ret = regulator_enable(regulator: state->vdd); |
714 | if (ret) |
715 | return ret; |
716 | |
717 | ret = devm_add_action_or_reset(dev, scd4x_disable_regulator, state); |
718 | if (ret) |
719 | return ret; |
720 | |
721 | ret = scd4x_send_command(state, cmd: CMD_STOP_MEAS); |
722 | if (ret) { |
723 | dev_err(dev, "failed to stop measurement: %d\n" , ret); |
724 | return ret; |
725 | } |
726 | |
727 | /* execution time */ |
728 | msleep_interruptible(msecs: 500); |
729 | |
730 | ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, scd4x_trigger_handler, NULL); |
731 | if (ret) |
732 | return ret; |
733 | |
734 | ret = scd4x_send_command(state, cmd: CMD_START_MEAS); |
735 | if (ret) { |
736 | dev_err(dev, "failed to start measurement: %d\n" , ret); |
737 | return ret; |
738 | } |
739 | |
740 | ret = devm_add_action_or_reset(dev, scd4x_stop_meas, state); |
741 | if (ret) |
742 | return ret; |
743 | |
744 | return devm_iio_device_register(dev, indio_dev); |
745 | } |
746 | |
747 | static const struct of_device_id scd4x_dt_ids[] = { |
748 | { .compatible = "sensirion,scd40" }, |
749 | { .compatible = "sensirion,scd41" }, |
750 | { } |
751 | }; |
752 | MODULE_DEVICE_TABLE(of, scd4x_dt_ids); |
753 | |
754 | static struct i2c_driver scd4x_i2c_driver = { |
755 | .driver = { |
756 | .name = KBUILD_MODNAME, |
757 | .of_match_table = scd4x_dt_ids, |
758 | .pm = pm_sleep_ptr(&scd4x_pm_ops), |
759 | }, |
760 | .probe = scd4x_probe, |
761 | }; |
762 | module_i2c_driver(scd4x_i2c_driver); |
763 | |
764 | MODULE_AUTHOR("Roan van Dijk <roan@protonic.nl>" ); |
765 | MODULE_DESCRIPTION("Sensirion SCD4X carbon dioxide sensor core driver" ); |
766 | MODULE_LICENSE("GPL v2" ); |
767 | |