1e510190eSTomasz Duszynski // SPDX-License-Identifier: GPL-2.0 2e510190eSTomasz Duszynski /* 3e510190eSTomasz Duszynski * Sensirion SCD30 carbon dioxide sensor i2c driver 4e510190eSTomasz Duszynski * 5e510190eSTomasz Duszynski * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> 6e510190eSTomasz Duszynski * 7e510190eSTomasz Duszynski * I2C slave address: 0x61 8e510190eSTomasz Duszynski */ 9e510190eSTomasz Duszynski #include <linux/crc8.h> 10e510190eSTomasz Duszynski #include <linux/device.h> 11e510190eSTomasz Duszynski #include <linux/errno.h> 12e510190eSTomasz Duszynski #include <linux/i2c.h> 13e510190eSTomasz Duszynski #include <linux/mod_devicetable.h> 14e510190eSTomasz Duszynski #include <linux/module.h> 15e510190eSTomasz Duszynski #include <linux/types.h> 16e510190eSTomasz Duszynski #include <asm/unaligned.h> 17e510190eSTomasz Duszynski 18e510190eSTomasz Duszynski #include "scd30.h" 19e510190eSTomasz Duszynski 20e510190eSTomasz Duszynski #define SCD30_I2C_MAX_BUF_SIZE 18 21e510190eSTomasz Duszynski #define SCD30_I2C_CRC8_POLYNOMIAL 0x31 22e510190eSTomasz Duszynski 23e510190eSTomasz Duszynski static u16 scd30_i2c_cmd_lookup_tbl[] = { 24e510190eSTomasz Duszynski [CMD_START_MEAS] = 0x0010, 25e510190eSTomasz Duszynski [CMD_STOP_MEAS] = 0x0104, 26e510190eSTomasz Duszynski [CMD_MEAS_INTERVAL] = 0x4600, 27e510190eSTomasz Duszynski [CMD_MEAS_READY] = 0x0202, 28e510190eSTomasz Duszynski [CMD_READ_MEAS] = 0x0300, 29e510190eSTomasz Duszynski [CMD_ASC] = 0x5306, 30e510190eSTomasz Duszynski [CMD_FRC] = 0x5204, 31e510190eSTomasz Duszynski [CMD_TEMP_OFFSET] = 0x5403, 32e510190eSTomasz Duszynski [CMD_FW_VERSION] = 0xd100, 33e510190eSTomasz Duszynski [CMD_RESET] = 0xd304, 34e510190eSTomasz Duszynski }; 35e510190eSTomasz Duszynski 36e510190eSTomasz Duszynski DECLARE_CRC8_TABLE(scd30_i2c_crc8_tbl); 37e510190eSTomasz Duszynski 38e510190eSTomasz Duszynski static int scd30_i2c_xfer(struct scd30_state *state, char *txbuf, int txsize, 39e510190eSTomasz Duszynski char *rxbuf, int rxsize) 40e510190eSTomasz Duszynski { 41e510190eSTomasz Duszynski struct i2c_client *client = to_i2c_client(state->dev); 42e510190eSTomasz Duszynski int ret; 43e510190eSTomasz Duszynski 44e510190eSTomasz Duszynski /* 45e510190eSTomasz Duszynski * repeated start is not supported hence instead of sending two i2c 46e510190eSTomasz Duszynski * messages in a row we send one by one 47e510190eSTomasz Duszynski */ 48e510190eSTomasz Duszynski ret = i2c_master_send(client, txbuf, txsize); 49e510190eSTomasz Duszynski if (ret < 0) 50e510190eSTomasz Duszynski return ret; 51e510190eSTomasz Duszynski if (ret != txsize) 52e510190eSTomasz Duszynski return -EIO; 53e510190eSTomasz Duszynski 54e510190eSTomasz Duszynski if (!rxbuf) 55e510190eSTomasz Duszynski return 0; 56e510190eSTomasz Duszynski 57e510190eSTomasz Duszynski ret = i2c_master_recv(client, rxbuf, rxsize); 58e510190eSTomasz Duszynski if (ret < 0) 59e510190eSTomasz Duszynski return ret; 60e510190eSTomasz Duszynski if (ret != rxsize) 61e510190eSTomasz Duszynski return -EIO; 62e510190eSTomasz Duszynski 63e510190eSTomasz Duszynski return 0; 64e510190eSTomasz Duszynski } 65e510190eSTomasz Duszynski 66e510190eSTomasz Duszynski static int scd30_i2c_command(struct scd30_state *state, enum scd30_cmd cmd, u16 arg, 67e510190eSTomasz Duszynski void *response, int size) 68e510190eSTomasz Duszynski { 69e510190eSTomasz Duszynski char buf[SCD30_I2C_MAX_BUF_SIZE]; 70e510190eSTomasz Duszynski char *rsp = response; 71e510190eSTomasz Duszynski int i, ret; 72e510190eSTomasz Duszynski char crc; 73e510190eSTomasz Duszynski 74e510190eSTomasz Duszynski put_unaligned_be16(scd30_i2c_cmd_lookup_tbl[cmd], buf); 75e510190eSTomasz Duszynski i = 2; 76e510190eSTomasz Duszynski 77e510190eSTomasz Duszynski if (rsp) { 78e510190eSTomasz Duszynski /* each two bytes are followed by a crc8 */ 79e510190eSTomasz Duszynski size += size / 2; 80e510190eSTomasz Duszynski } else { 81e510190eSTomasz Duszynski put_unaligned_be16(arg, buf + i); 82e510190eSTomasz Duszynski crc = crc8(scd30_i2c_crc8_tbl, buf + i, 2, CRC8_INIT_VALUE); 83e510190eSTomasz Duszynski i += 2; 84e510190eSTomasz Duszynski buf[i] = crc; 85e510190eSTomasz Duszynski i += 1; 86e510190eSTomasz Duszynski 87e510190eSTomasz Duszynski /* commands below don't take an argument */ 88e510190eSTomasz Duszynski if ((cmd == CMD_STOP_MEAS) || (cmd == CMD_RESET)) 89e510190eSTomasz Duszynski i -= 3; 90e510190eSTomasz Duszynski } 91e510190eSTomasz Duszynski 92e510190eSTomasz Duszynski ret = scd30_i2c_xfer(state, buf, i, buf, size); 93e510190eSTomasz Duszynski if (ret) 94e510190eSTomasz Duszynski return ret; 95e510190eSTomasz Duszynski 96e510190eSTomasz Duszynski /* validate received data and strip off crc bytes */ 97e510190eSTomasz Duszynski for (i = 0; i < size; i += 3) { 98e510190eSTomasz Duszynski crc = crc8(scd30_i2c_crc8_tbl, buf + i, 2, CRC8_INIT_VALUE); 99e510190eSTomasz Duszynski if (crc != buf[i + 2]) { 100e510190eSTomasz Duszynski dev_err(state->dev, "data integrity check failed\n"); 101e510190eSTomasz Duszynski return -EIO; 102e510190eSTomasz Duszynski } 103e510190eSTomasz Duszynski 104e510190eSTomasz Duszynski *rsp++ = buf[i]; 105e510190eSTomasz Duszynski *rsp++ = buf[i + 1]; 106e510190eSTomasz Duszynski } 107e510190eSTomasz Duszynski 108e510190eSTomasz Duszynski return 0; 109e510190eSTomasz Duszynski } 110e510190eSTomasz Duszynski 111e510190eSTomasz Duszynski static int scd30_i2c_probe(struct i2c_client *client) 112e510190eSTomasz Duszynski { 113e510190eSTomasz Duszynski if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 114e510190eSTomasz Duszynski return -EOPNOTSUPP; 115e510190eSTomasz Duszynski 116e510190eSTomasz Duszynski crc8_populate_msb(scd30_i2c_crc8_tbl, SCD30_I2C_CRC8_POLYNOMIAL); 117e510190eSTomasz Duszynski 118e510190eSTomasz Duszynski return scd30_probe(&client->dev, client->irq, client->name, NULL, scd30_i2c_command); 119e510190eSTomasz Duszynski } 120e510190eSTomasz Duszynski 121e510190eSTomasz Duszynski static const struct of_device_id scd30_i2c_of_match[] = { 122e510190eSTomasz Duszynski { .compatible = "sensirion,scd30" }, 123e510190eSTomasz Duszynski { } 124e510190eSTomasz Duszynski }; 125e510190eSTomasz Duszynski MODULE_DEVICE_TABLE(of, scd30_i2c_of_match); 126e510190eSTomasz Duszynski 127e510190eSTomasz Duszynski static struct i2c_driver scd30_i2c_driver = { 128e510190eSTomasz Duszynski .driver = { 129e510190eSTomasz Duszynski .name = KBUILD_MODNAME, 130e510190eSTomasz Duszynski .of_match_table = scd30_i2c_of_match, 13195d5a721SJonathan Cameron .pm = pm_sleep_ptr(&scd30_pm_ops), 132e510190eSTomasz Duszynski }, 133e510190eSTomasz Duszynski .probe_new = scd30_i2c_probe, 134e510190eSTomasz Duszynski }; 135e510190eSTomasz Duszynski module_i2c_driver(scd30_i2c_driver); 136e510190eSTomasz Duszynski 137e510190eSTomasz Duszynski MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); 138e510190eSTomasz Duszynski MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver"); 139e510190eSTomasz Duszynski MODULE_LICENSE("GPL v2"); 140*bd8284e9SJonathan Cameron MODULE_IMPORT_NS(IIO_SCD30); 141