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