767 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Sensirion SCD4X carbon dioxide sensor i2c driver4 *5 * Copyright (C) 2021 Protonic Holland6 * Author: Roan van Dijk <roan@protonic.nl>7 *8 * I2C slave address: 0x629 *10 * Datasheets:11 * https://www.sensirion.com/file/datasheet_scd4x12 */13 14#include <linux/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 0x3133#define SCD4X_TIMEOUT_ERR 100034#define SCD4X_READ_BUF_SIZE 935#define SCD4X_COMMAND_BUF_SIZE 236#define SCD4X_WRITE_BUF_SIZE 537#define SCD4X_FRC_MIN_PPM 038#define SCD4X_FRC_MAX_PPM 200039#define SCD4X_PRESSURE_COMP_MIN_MBAR 70040#define SCD4X_PRESSURE_COMP_MAX_MBAR 120041#define SCD4X_READY_MASK 0x0142 43/*Commands SCD4X*/44enum 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 58enum scd4x_channel_idx {59 SCD4X_CO2,60 SCD4X_TEMP,61 SCD4X_HR,62};63 64struct 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 71DECLARE_CRC8_TABLE(scd4x_crc8_table);72 73static 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, txbuf, 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, rxbuf, rxsize);90 if (ret < 0)91 return ret;92 if (ret != rxsize)93 return -EIO;94 95 return 0;96}97 98static 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_STOP_MEAS);110 if (ret)111 return ret;112 113 /* execution time for stopping measurement */114 msleep_interruptible(500);115 }116 117 put_unaligned_be16(cmd, buf);118 ret = scd4x_i2c_xfer(state, buf, 2, buf, 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_START_MEAS);124 if (ret)125 return ret;126 }127 128 return 0;129}130 131static 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_STOP_MEAS);147 if (ret)148 return ret;149 150 /* execution time for stopping measurement */151 msleep_interruptible(500);152 }153 154 /* CRC byte for every 2 bytes of data */155 response_sz += response_sz / 2;156 157 put_unaligned_be16(cmd, buf);158 ret = scd4x_i2c_xfer(state, buf, 2, buf, response_sz);159 if (ret)160 return ret;161 162 for (i = 0; i < response_sz; i += 3) {163 crc = crc8(scd4x_crc8_table, buf + i, 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_START_MEAS);177 if (ret)178 return ret;179 }180 181 return 0;182}183 184static 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(cmd, buf);191 put_unaligned_be16(arg, buf + 2);192 193 crc = crc8(scd4x_crc8_table, buf + 2, 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_STOP_MEAS);199 if (ret)200 return ret;201 }202 203 /* execution time */204 msleep_interruptible(500);205 206 ret = scd4x_i2c_xfer(state, buf, SCD4X_WRITE_BUF_SIZE, buf, 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_START_MEAS);213 if (ret)214 return ret;215 }216 217 return 0;218}219 220static 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_FRC, arg);230 if (ret)231 goto err;232 233 /* execution time */234 msleep_interruptible(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, 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(scd4x_crc8_table, buf + i, 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_START_MEAS);260 261err:262 /*263 * on error try to start the measurement,264 * puts sensor back into continuous measurement265 */266 scd4x_send_command(state, CMD_START_MEAS);267 268 return ret;269}270 271static 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_READ_MEAS, buf, 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 286static 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_GET_DATA_READY, &bval, 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(1000);306 } while (--tries);307 308 /* try to start sensor on timeout */309 ret = scd4x_send_command(state, CMD_START_MEAS);310 if (ret)311 dev_err(&client->dev, "failed to start measurement: %d\n", ret);312 313 return -ETIMEDOUT;314}315 316static 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, buf);325}326 327static 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 339static 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_GET_AMB_PRESSURE, &tmp, sizeof(tmp));352 mutex_unlock(&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->address);367 mutex_unlock(&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_GET_TEMP_OFFSET, &tmp, sizeof(tmp));397 mutex_unlock(&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 409static const int scd4x_pressure_calibbias_available[] = {410 SCD4X_PRESSURE_COMP_MIN_MBAR, 1, SCD4X_PRESSURE_COMP_MAX_MBAR,411};412 413static 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 428static 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_SET_TEMP_OFFSET, val);438 mutex_unlock(&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_SET_AMB_PRESSURE, val);450 mutex_unlock(&state->lock);451 452 return ret;453 default:454 return -EINVAL;455 }456 default:457 return -EINVAL;458 }459}460 461static 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_GET_ASC, &bval, sizeof(bval));472 mutex_unlock(&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, "%d\n", val);481}482 483static 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(buf, &val);494 if (ret)495 return ret;496 497 value = val;498 499 mutex_lock(&state->lock);500 ret = scd4x_write(state, CMD_SET_ASC, value);501 mutex_unlock(&state->lock);502 if (ret)503 dev_err(dev, "failed to set automatic calibration");504 505 return ret ?: len;506}507 508static 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(buf, 0, &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_FRC, arg, &val, sizeof(val));526 mutex_unlock(&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 539static IIO_DEVICE_ATTR_RW(calibration_auto_enable, 0);540static IIO_DEVICE_ATTR_WO(calibration_forced_value, 0);541 542static IIO_CONST_ATTR(calibration_forced_value_available,543 __stringify([SCD4X_FRC_MIN_PPM 1 SCD4X_FRC_MAX_PPM]));544 545static 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 NULL550};551 552static const struct attribute_group scd4x_attr_group = {553 .attrs = scd4x_attrs,554};555 556static 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 563static const struct iio_chan_spec scd4x_channels[] = {564 {565 /*566 * this channel is special in a sense we are pretending that567 * sensor is able to change measurement chamber pressure but in568 * fact we're just setting pressure compensation value569 */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 621static 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_STOP_MEAS);628 if (ret)629 return ret;630 631 return regulator_disable(state->vdd);632}633 634static 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(state->vdd);641 if (ret)642 return ret;643 644 return scd4x_send_command(state, CMD_START_MEAS);645}646 647static DEFINE_SIMPLE_DEV_PM_OPS(scd4x_pm_ops, scd4x_suspend, scd4x_resume);648 649static void scd4x_stop_meas(void *state)650{651 scd4x_send_command(state, CMD_STOP_MEAS);652}653 654static void scd4x_disable_regulator(void *data)655{656 struct scd4x_state *state = data;657 658 regulator_disable(state->vdd);659}660 661static 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, scan.data);675 mutex_unlock(&state->lock);676 if (ret)677 goto out;678 679 iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev));680out:681 iio_trigger_notify_done(indio_dev->trig);682 return IRQ_HANDLED;683}684 685static 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(dev, 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(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, "vdd");710 if (IS_ERR(state->vdd))711 return dev_err_probe(dev, PTR_ERR(state->vdd), "failed to get regulator\n");712 713 ret = regulator_enable(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_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(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_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 747static const struct of_device_id scd4x_dt_ids[] = {748 { .compatible = "sensirion,scd40" },749 { .compatible = "sensirion,scd41" },750 { }751};752MODULE_DEVICE_TABLE(of, scd4x_dt_ids);753 754static 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};762module_i2c_driver(scd4x_i2c_driver);763 764MODULE_AUTHOR("Roan van Dijk <roan@protonic.nl>");765MODULE_DESCRIPTION("Sensirion SCD4X carbon dioxide sensor core driver");766MODULE_LICENSE("GPL v2");767