1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ccs811.c - Support for AMS CCS811 VOC Sensor |
4 | * |
5 | * Copyright (C) 2017 Narcisa Vasile <narcisaanamaria12@gmail.com> |
6 | * |
7 | * Datasheet: ams.com/content/download/951091/2269479/CCS811_DS000459_3-00.pdf |
8 | * |
9 | * IIO driver for AMS CCS811 (I2C address 0x5A/0x5B set by ADDR Low/High) |
10 | * |
11 | * TODO: |
12 | * 1. Make the drive mode selectable form userspace |
13 | * 2. Add support for interrupts |
14 | * 3. Adjust time to wait for data to be ready based on selected operation mode |
15 | * 4. Read error register and put the information in logs |
16 | */ |
17 | |
18 | #include <linux/delay.h> |
19 | #include <linux/gpio/consumer.h> |
20 | #include <linux/i2c.h> |
21 | #include <linux/iio/iio.h> |
22 | #include <linux/iio/buffer.h> |
23 | #include <linux/iio/trigger.h> |
24 | #include <linux/iio/triggered_buffer.h> |
25 | #include <linux/iio/trigger_consumer.h> |
26 | #include <linux/module.h> |
27 | |
28 | #define CCS811_STATUS 0x00 |
29 | #define CCS811_MEAS_MODE 0x01 |
30 | #define CCS811_ALG_RESULT_DATA 0x02 |
31 | #define CCS811_RAW_DATA 0x03 |
32 | #define CCS811_HW_ID 0x20 |
33 | #define CCS811_HW_ID_VALUE 0x81 |
34 | #define CCS811_HW_VERSION 0x21 |
35 | #define CCS811_HW_VERSION_VALUE 0x10 |
36 | #define CCS811_HW_VERSION_MASK 0xF0 |
37 | #define CCS811_ERR 0xE0 |
38 | /* Used to transition from boot to application mode */ |
39 | #define CCS811_APP_START 0xF4 |
40 | #define CCS811_SW_RESET 0xFF |
41 | |
42 | /* Status register flags */ |
43 | #define CCS811_STATUS_ERROR BIT(0) |
44 | #define CCS811_STATUS_DATA_READY BIT(3) |
45 | #define CCS811_STATUS_APP_VALID_MASK BIT(4) |
46 | #define CCS811_STATUS_APP_VALID_LOADED BIT(4) |
47 | /* |
48 | * Value of FW_MODE bit of STATUS register describes the sensor's state: |
49 | * 0: Firmware is in boot mode, this allows new firmware to be loaded |
50 | * 1: Firmware is in application mode. CCS811 is ready to take ADC measurements |
51 | */ |
52 | #define CCS811_STATUS_FW_MODE_MASK BIT(7) |
53 | #define CCS811_STATUS_FW_MODE_APPLICATION BIT(7) |
54 | |
55 | /* Measurement modes */ |
56 | #define CCS811_MODE_IDLE 0x00 |
57 | #define CCS811_MODE_IAQ_1SEC 0x10 |
58 | #define CCS811_MODE_IAQ_10SEC 0x20 |
59 | #define CCS811_MODE_IAQ_60SEC 0x30 |
60 | #define CCS811_MODE_RAW_DATA 0x40 |
61 | |
62 | #define CCS811_MEAS_MODE_INTERRUPT BIT(3) |
63 | |
64 | #define CCS811_VOLTAGE_MASK 0x3FF |
65 | |
66 | struct ccs811_reading { |
67 | __be16 co2; |
68 | __be16 voc; |
69 | u8 status; |
70 | u8 error; |
71 | __be16 raw_data; |
72 | } __attribute__((__packed__)); |
73 | |
74 | struct ccs811_data { |
75 | struct i2c_client *client; |
76 | struct mutex lock; /* Protect readings */ |
77 | struct ccs811_reading buffer; |
78 | struct iio_trigger *drdy_trig; |
79 | struct gpio_desc *wakeup_gpio; |
80 | bool drdy_trig_on; |
81 | /* Ensures correct alignment of timestamp if present */ |
82 | struct { |
83 | s16 channels[2]; |
84 | s64 ts __aligned(8); |
85 | } scan; |
86 | }; |
87 | |
88 | static const struct iio_chan_spec ccs811_channels[] = { |
89 | { |
90 | .type = IIO_CURRENT, |
91 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
92 | BIT(IIO_CHAN_INFO_SCALE), |
93 | .scan_index = -1, |
94 | }, { |
95 | .type = IIO_VOLTAGE, |
96 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
97 | BIT(IIO_CHAN_INFO_SCALE), |
98 | .scan_index = -1, |
99 | }, { |
100 | .type = IIO_CONCENTRATION, |
101 | .channel2 = IIO_MOD_CO2, |
102 | .modified = 1, |
103 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
104 | BIT(IIO_CHAN_INFO_SCALE), |
105 | .scan_index = 0, |
106 | .scan_type = { |
107 | .sign = 'u', |
108 | .realbits = 16, |
109 | .storagebits = 16, |
110 | .endianness = IIO_BE, |
111 | }, |
112 | }, { |
113 | .type = IIO_CONCENTRATION, |
114 | .channel2 = IIO_MOD_VOC, |
115 | .modified = 1, |
116 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
117 | BIT(IIO_CHAN_INFO_SCALE), |
118 | .scan_index = 1, |
119 | .scan_type = { |
120 | .sign = 'u', |
121 | .realbits = 16, |
122 | .storagebits = 16, |
123 | .endianness = IIO_BE, |
124 | }, |
125 | }, |
126 | IIO_CHAN_SOFT_TIMESTAMP(2), |
127 | }; |
128 | |
129 | /* |
130 | * The CCS811 powers-up in boot mode. A setup write to CCS811_APP_START will |
131 | * transition the sensor to application mode. |
132 | */ |
133 | static int ccs811_start_sensor_application(struct i2c_client *client) |
134 | { |
135 | int ret; |
136 | |
137 | ret = i2c_smbus_read_byte_data(client, CCS811_STATUS); |
138 | if (ret < 0) |
139 | return ret; |
140 | |
141 | if ((ret & CCS811_STATUS_FW_MODE_APPLICATION)) |
142 | return 0; |
143 | |
144 | if ((ret & CCS811_STATUS_APP_VALID_MASK) != |
145 | CCS811_STATUS_APP_VALID_LOADED) |
146 | return -EIO; |
147 | |
148 | ret = i2c_smbus_write_byte(client, CCS811_APP_START); |
149 | if (ret < 0) |
150 | return ret; |
151 | |
152 | ret = i2c_smbus_read_byte_data(client, CCS811_STATUS); |
153 | if (ret < 0) |
154 | return ret; |
155 | |
156 | if ((ret & CCS811_STATUS_FW_MODE_MASK) != |
157 | CCS811_STATUS_FW_MODE_APPLICATION) { |
158 | dev_err(&client->dev, "Application failed to start. Sensor is still in boot mode.\n" ); |
159 | return -EIO; |
160 | } |
161 | |
162 | return 0; |
163 | } |
164 | |
165 | static int ccs811_setup(struct i2c_client *client) |
166 | { |
167 | int ret; |
168 | |
169 | ret = ccs811_start_sensor_application(client); |
170 | if (ret < 0) |
171 | return ret; |
172 | |
173 | return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, |
174 | CCS811_MODE_IAQ_1SEC); |
175 | } |
176 | |
177 | static void ccs811_set_wakeup(struct ccs811_data *data, bool enable) |
178 | { |
179 | if (!data->wakeup_gpio) |
180 | return; |
181 | |
182 | gpiod_set_value(desc: data->wakeup_gpio, value: enable); |
183 | |
184 | if (enable) |
185 | usleep_range(min: 50, max: 60); |
186 | else |
187 | usleep_range(min: 20, max: 30); |
188 | } |
189 | |
190 | static int ccs811_get_measurement(struct ccs811_data *data) |
191 | { |
192 | int ret, tries = 11; |
193 | |
194 | ccs811_set_wakeup(data, enable: true); |
195 | |
196 | /* Maximum waiting time: 1s, as measurements are made every second */ |
197 | while (tries-- > 0) { |
198 | ret = i2c_smbus_read_byte_data(client: data->client, CCS811_STATUS); |
199 | if (ret < 0) |
200 | return ret; |
201 | |
202 | if ((ret & CCS811_STATUS_DATA_READY) || tries == 0) |
203 | break; |
204 | msleep(msecs: 100); |
205 | } |
206 | if (!(ret & CCS811_STATUS_DATA_READY)) |
207 | return -EIO; |
208 | |
209 | ret = i2c_smbus_read_i2c_block_data(client: data->client, |
210 | CCS811_ALG_RESULT_DATA, length: 8, |
211 | values: (char *)&data->buffer); |
212 | ccs811_set_wakeup(data, enable: false); |
213 | |
214 | return ret; |
215 | } |
216 | |
217 | static int ccs811_read_raw(struct iio_dev *indio_dev, |
218 | struct iio_chan_spec const *chan, |
219 | int *val, int *val2, long mask) |
220 | { |
221 | struct ccs811_data *data = iio_priv(indio_dev); |
222 | int ret; |
223 | |
224 | switch (mask) { |
225 | case IIO_CHAN_INFO_RAW: |
226 | ret = iio_device_claim_direct_mode(indio_dev); |
227 | if (ret) |
228 | return ret; |
229 | mutex_lock(&data->lock); |
230 | ret = ccs811_get_measurement(data); |
231 | if (ret < 0) { |
232 | mutex_unlock(lock: &data->lock); |
233 | iio_device_release_direct_mode(indio_dev); |
234 | return ret; |
235 | } |
236 | |
237 | switch (chan->type) { |
238 | case IIO_VOLTAGE: |
239 | *val = be16_to_cpu(data->buffer.raw_data) & |
240 | CCS811_VOLTAGE_MASK; |
241 | ret = IIO_VAL_INT; |
242 | break; |
243 | case IIO_CURRENT: |
244 | *val = be16_to_cpu(data->buffer.raw_data) >> 10; |
245 | ret = IIO_VAL_INT; |
246 | break; |
247 | case IIO_CONCENTRATION: |
248 | switch (chan->channel2) { |
249 | case IIO_MOD_CO2: |
250 | *val = be16_to_cpu(data->buffer.co2); |
251 | ret = IIO_VAL_INT; |
252 | break; |
253 | case IIO_MOD_VOC: |
254 | *val = be16_to_cpu(data->buffer.voc); |
255 | ret = IIO_VAL_INT; |
256 | break; |
257 | default: |
258 | ret = -EINVAL; |
259 | } |
260 | break; |
261 | default: |
262 | ret = -EINVAL; |
263 | } |
264 | mutex_unlock(lock: &data->lock); |
265 | iio_device_release_direct_mode(indio_dev); |
266 | |
267 | return ret; |
268 | |
269 | case IIO_CHAN_INFO_SCALE: |
270 | switch (chan->type) { |
271 | case IIO_VOLTAGE: |
272 | *val = 1; |
273 | *val2 = 612903; |
274 | return IIO_VAL_INT_PLUS_MICRO; |
275 | case IIO_CURRENT: |
276 | *val = 0; |
277 | *val2 = 1000; |
278 | return IIO_VAL_INT_PLUS_MICRO; |
279 | case IIO_CONCENTRATION: |
280 | switch (chan->channel2) { |
281 | case IIO_MOD_CO2: |
282 | *val = 0; |
283 | *val2 = 100; |
284 | return IIO_VAL_INT_PLUS_MICRO; |
285 | case IIO_MOD_VOC: |
286 | *val = 0; |
287 | *val2 = 100; |
288 | return IIO_VAL_INT_PLUS_NANO; |
289 | default: |
290 | return -EINVAL; |
291 | } |
292 | default: |
293 | return -EINVAL; |
294 | } |
295 | default: |
296 | return -EINVAL; |
297 | } |
298 | } |
299 | |
300 | static const struct iio_info ccs811_info = { |
301 | .read_raw = ccs811_read_raw, |
302 | }; |
303 | |
304 | static int ccs811_set_trigger_state(struct iio_trigger *trig, |
305 | bool state) |
306 | { |
307 | struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); |
308 | struct ccs811_data *data = iio_priv(indio_dev); |
309 | int ret; |
310 | |
311 | ret = i2c_smbus_read_byte_data(client: data->client, CCS811_MEAS_MODE); |
312 | if (ret < 0) |
313 | return ret; |
314 | |
315 | if (state) |
316 | ret |= CCS811_MEAS_MODE_INTERRUPT; |
317 | else |
318 | ret &= ~CCS811_MEAS_MODE_INTERRUPT; |
319 | |
320 | data->drdy_trig_on = state; |
321 | |
322 | return i2c_smbus_write_byte_data(client: data->client, CCS811_MEAS_MODE, value: ret); |
323 | } |
324 | |
325 | static const struct iio_trigger_ops ccs811_trigger_ops = { |
326 | .set_trigger_state = ccs811_set_trigger_state, |
327 | }; |
328 | |
329 | static irqreturn_t ccs811_trigger_handler(int irq, void *p) |
330 | { |
331 | struct iio_poll_func *pf = p; |
332 | struct iio_dev *indio_dev = pf->indio_dev; |
333 | struct ccs811_data *data = iio_priv(indio_dev); |
334 | struct i2c_client *client = data->client; |
335 | int ret; |
336 | |
337 | ret = i2c_smbus_read_i2c_block_data(client, CCS811_ALG_RESULT_DATA, |
338 | length: sizeof(data->scan.channels), |
339 | values: (u8 *)data->scan.channels); |
340 | if (ret != 4) { |
341 | dev_err(&client->dev, "cannot read sensor data\n" ); |
342 | goto err; |
343 | } |
344 | |
345 | iio_push_to_buffers_with_timestamp(indio_dev, data: &data->scan, |
346 | timestamp: iio_get_time_ns(indio_dev)); |
347 | |
348 | err: |
349 | iio_trigger_notify_done(trig: indio_dev->trig); |
350 | |
351 | return IRQ_HANDLED; |
352 | } |
353 | |
354 | static irqreturn_t ccs811_data_rdy_trigger_poll(int irq, void *private) |
355 | { |
356 | struct iio_dev *indio_dev = private; |
357 | struct ccs811_data *data = iio_priv(indio_dev); |
358 | |
359 | if (data->drdy_trig_on) |
360 | iio_trigger_poll(trig: data->drdy_trig); |
361 | |
362 | return IRQ_HANDLED; |
363 | } |
364 | |
365 | static int ccs811_reset(struct i2c_client *client) |
366 | { |
367 | struct gpio_desc *reset_gpio; |
368 | int ret; |
369 | |
370 | reset_gpio = devm_gpiod_get_optional(dev: &client->dev, con_id: "reset" , |
371 | flags: GPIOD_OUT_LOW); |
372 | if (IS_ERR(ptr: reset_gpio)) |
373 | return PTR_ERR(ptr: reset_gpio); |
374 | |
375 | /* Try to reset using nRESET pin if available else do SW reset */ |
376 | if (reset_gpio) { |
377 | gpiod_set_value(desc: reset_gpio, value: 1); |
378 | usleep_range(min: 20, max: 30); |
379 | gpiod_set_value(desc: reset_gpio, value: 0); |
380 | } else { |
381 | /* |
382 | * As per the datasheet, this sequence of values needs to be |
383 | * written to the SW_RESET register for triggering the soft |
384 | * reset in the device and placing it in boot mode. |
385 | */ |
386 | static const u8 reset_seq[] = { |
387 | 0x11, 0xE5, 0x72, 0x8A, |
388 | }; |
389 | |
390 | ret = i2c_smbus_write_i2c_block_data(client, CCS811_SW_RESET, |
391 | length: sizeof(reset_seq), values: reset_seq); |
392 | if (ret < 0) { |
393 | dev_err(&client->dev, "Failed to reset sensor\n" ); |
394 | return ret; |
395 | } |
396 | } |
397 | |
398 | /* tSTART delay required after reset */ |
399 | usleep_range(min: 1000, max: 2000); |
400 | |
401 | return 0; |
402 | } |
403 | |
404 | static int ccs811_probe(struct i2c_client *client) |
405 | { |
406 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
407 | struct iio_dev *indio_dev; |
408 | struct ccs811_data *data; |
409 | int ret; |
410 | |
411 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE |
412 | | I2C_FUNC_SMBUS_BYTE_DATA |
413 | | I2C_FUNC_SMBUS_READ_I2C_BLOCK)) |
414 | return -EOPNOTSUPP; |
415 | |
416 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
417 | if (!indio_dev) |
418 | return -ENOMEM; |
419 | |
420 | data = iio_priv(indio_dev); |
421 | i2c_set_clientdata(client, data: indio_dev); |
422 | data->client = client; |
423 | |
424 | data->wakeup_gpio = devm_gpiod_get_optional(dev: &client->dev, con_id: "wakeup" , |
425 | flags: GPIOD_OUT_HIGH); |
426 | if (IS_ERR(ptr: data->wakeup_gpio)) |
427 | return PTR_ERR(ptr: data->wakeup_gpio); |
428 | |
429 | ccs811_set_wakeup(data, enable: true); |
430 | |
431 | ret = ccs811_reset(client); |
432 | if (ret) { |
433 | ccs811_set_wakeup(data, enable: false); |
434 | return ret; |
435 | } |
436 | |
437 | /* Check hardware id (should be 0x81 for this family of devices) */ |
438 | ret = i2c_smbus_read_byte_data(client, CCS811_HW_ID); |
439 | if (ret < 0) { |
440 | ccs811_set_wakeup(data, enable: false); |
441 | return ret; |
442 | } |
443 | |
444 | if (ret != CCS811_HW_ID_VALUE) { |
445 | dev_err(&client->dev, "hardware id doesn't match CCS81x\n" ); |
446 | ccs811_set_wakeup(data, enable: false); |
447 | return -ENODEV; |
448 | } |
449 | |
450 | ret = i2c_smbus_read_byte_data(client, CCS811_HW_VERSION); |
451 | if (ret < 0) { |
452 | ccs811_set_wakeup(data, enable: false); |
453 | return ret; |
454 | } |
455 | |
456 | if ((ret & CCS811_HW_VERSION_MASK) != CCS811_HW_VERSION_VALUE) { |
457 | dev_err(&client->dev, "no CCS811 sensor\n" ); |
458 | ccs811_set_wakeup(data, enable: false); |
459 | return -ENODEV; |
460 | } |
461 | |
462 | ret = ccs811_setup(client); |
463 | if (ret < 0) { |
464 | ccs811_set_wakeup(data, enable: false); |
465 | return ret; |
466 | } |
467 | |
468 | ccs811_set_wakeup(data, enable: false); |
469 | |
470 | mutex_init(&data->lock); |
471 | |
472 | indio_dev->name = id->name; |
473 | indio_dev->info = &ccs811_info; |
474 | indio_dev->modes = INDIO_DIRECT_MODE; |
475 | |
476 | indio_dev->channels = ccs811_channels; |
477 | indio_dev->num_channels = ARRAY_SIZE(ccs811_channels); |
478 | |
479 | if (client->irq > 0) { |
480 | ret = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, |
481 | handler: ccs811_data_rdy_trigger_poll, |
482 | NULL, |
483 | IRQF_TRIGGER_FALLING | |
484 | IRQF_ONESHOT, |
485 | devname: "ccs811_irq" , dev_id: indio_dev); |
486 | if (ret) { |
487 | dev_err(&client->dev, "irq request error %d\n" , -ret); |
488 | goto err_poweroff; |
489 | } |
490 | |
491 | data->drdy_trig = devm_iio_trigger_alloc(&client->dev, |
492 | "%s-dev%d" , |
493 | indio_dev->name, |
494 | iio_device_id(indio_dev)); |
495 | if (!data->drdy_trig) { |
496 | ret = -ENOMEM; |
497 | goto err_poweroff; |
498 | } |
499 | |
500 | data->drdy_trig->ops = &ccs811_trigger_ops; |
501 | iio_trigger_set_drvdata(trig: data->drdy_trig, data: indio_dev); |
502 | ret = iio_trigger_register(trig_info: data->drdy_trig); |
503 | if (ret) |
504 | goto err_poweroff; |
505 | |
506 | indio_dev->trig = iio_trigger_get(trig: data->drdy_trig); |
507 | } |
508 | |
509 | ret = iio_triggered_buffer_setup(indio_dev, NULL, |
510 | ccs811_trigger_handler, NULL); |
511 | |
512 | if (ret < 0) { |
513 | dev_err(&client->dev, "triggered buffer setup failed\n" ); |
514 | goto err_trigger_unregister; |
515 | } |
516 | |
517 | ret = iio_device_register(indio_dev); |
518 | if (ret < 0) { |
519 | dev_err(&client->dev, "unable to register iio device\n" ); |
520 | goto err_buffer_cleanup; |
521 | } |
522 | return 0; |
523 | |
524 | err_buffer_cleanup: |
525 | iio_triggered_buffer_cleanup(indio_dev); |
526 | err_trigger_unregister: |
527 | if (data->drdy_trig) |
528 | iio_trigger_unregister(trig_info: data->drdy_trig); |
529 | err_poweroff: |
530 | i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, CCS811_MODE_IDLE); |
531 | |
532 | return ret; |
533 | } |
534 | |
535 | static void ccs811_remove(struct i2c_client *client) |
536 | { |
537 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
538 | struct ccs811_data *data = iio_priv(indio_dev); |
539 | int ret; |
540 | |
541 | iio_device_unregister(indio_dev); |
542 | iio_triggered_buffer_cleanup(indio_dev); |
543 | if (data->drdy_trig) |
544 | iio_trigger_unregister(trig_info: data->drdy_trig); |
545 | |
546 | ret = i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, |
547 | CCS811_MODE_IDLE); |
548 | if (ret) |
549 | dev_warn(&client->dev, "Failed to power down device (%pe)\n" , |
550 | ERR_PTR(ret)); |
551 | } |
552 | |
553 | static const struct i2c_device_id ccs811_id[] = { |
554 | {"ccs811" , 0}, |
555 | { } |
556 | }; |
557 | MODULE_DEVICE_TABLE(i2c, ccs811_id); |
558 | |
559 | static const struct of_device_id ccs811_dt_ids[] = { |
560 | { .compatible = "ams,ccs811" }, |
561 | { } |
562 | }; |
563 | MODULE_DEVICE_TABLE(of, ccs811_dt_ids); |
564 | |
565 | static struct i2c_driver ccs811_driver = { |
566 | .driver = { |
567 | .name = "ccs811" , |
568 | .of_match_table = ccs811_dt_ids, |
569 | }, |
570 | .probe = ccs811_probe, |
571 | .remove = ccs811_remove, |
572 | .id_table = ccs811_id, |
573 | }; |
574 | module_i2c_driver(ccs811_driver); |
575 | |
576 | MODULE_AUTHOR("Narcisa Vasile <narcisaanamaria12@gmail.com>" ); |
577 | MODULE_DESCRIPTION("CCS811 volatile organic compounds sensor" ); |
578 | MODULE_LICENSE("GPL v2" ); |
579 | |