0001
0002
0003
0004
0005
0006
0007 #include <linux/crc16.h>
0008 #include <linux/device.h>
0009 #include <linux/errno.h>
0010 #include <linux/iio/iio.h>
0011 #include <linux/jiffies.h>
0012 #include <linux/mod_devicetable.h>
0013 #include <linux/module.h>
0014 #include <linux/property.h>
0015 #include <linux/serdev.h>
0016 #include <linux/string.h>
0017 #include <linux/types.h>
0018 #include <asm/unaligned.h>
0019
0020 #include "scd30.h"
0021
0022 #define SCD30_SERDEV_ADDR 0x61
0023 #define SCD30_SERDEV_WRITE 0x06
0024 #define SCD30_SERDEV_READ 0x03
0025 #define SCD30_SERDEV_MAX_BUF_SIZE 17
0026 #define SCD30_SERDEV_RX_HEADER_SIZE 3
0027 #define SCD30_SERDEV_CRC_SIZE 2
0028 #define SCD30_SERDEV_TIMEOUT msecs_to_jiffies(200)
0029
0030 struct scd30_serdev_priv {
0031 struct completion meas_ready;
0032 char *buf;
0033 int num_expected;
0034 int num;
0035 };
0036
0037 static u16 scd30_serdev_cmd_lookup_tbl[] = {
0038 [CMD_START_MEAS] = 0x0036,
0039 [CMD_STOP_MEAS] = 0x0037,
0040 [CMD_MEAS_INTERVAL] = 0x0025,
0041 [CMD_MEAS_READY] = 0x0027,
0042 [CMD_READ_MEAS] = 0x0028,
0043 [CMD_ASC] = 0x003a,
0044 [CMD_FRC] = 0x0039,
0045 [CMD_TEMP_OFFSET] = 0x003b,
0046 [CMD_FW_VERSION] = 0x0020,
0047 [CMD_RESET] = 0x0034,
0048 };
0049
0050 static u16 scd30_serdev_calc_crc(const char *buf, int size)
0051 {
0052 return crc16(0xffff, buf, size);
0053 }
0054
0055 static int scd30_serdev_xfer(struct scd30_state *state, char *txbuf, int txsize,
0056 char *rxbuf, int rxsize)
0057 {
0058 struct serdev_device *serdev = to_serdev_device(state->dev);
0059 struct scd30_serdev_priv *priv = state->priv;
0060 int ret;
0061
0062 priv->buf = rxbuf;
0063 priv->num_expected = rxsize;
0064 priv->num = 0;
0065
0066 ret = serdev_device_write(serdev, txbuf, txsize, SCD30_SERDEV_TIMEOUT);
0067 if (ret < 0)
0068 return ret;
0069 if (ret != txsize)
0070 return -EIO;
0071
0072 ret = wait_for_completion_interruptible_timeout(&priv->meas_ready, SCD30_SERDEV_TIMEOUT);
0073 if (ret < 0)
0074 return ret;
0075 if (!ret)
0076 return -ETIMEDOUT;
0077
0078 return 0;
0079 }
0080
0081 static int scd30_serdev_command(struct scd30_state *state, enum scd30_cmd cmd, u16 arg,
0082 void *response, int size)
0083 {
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108 char txbuf[SCD30_SERDEV_MAX_BUF_SIZE] = { SCD30_SERDEV_ADDR },
0109 rxbuf[SCD30_SERDEV_MAX_BUF_SIZE];
0110 int ret, rxsize, txsize = 2;
0111 char *rsp = response;
0112 u16 crc;
0113
0114 put_unaligned_be16(scd30_serdev_cmd_lookup_tbl[cmd], txbuf + txsize);
0115 txsize += 2;
0116
0117 if (rsp) {
0118 txbuf[1] = SCD30_SERDEV_READ;
0119 if (cmd == CMD_READ_MEAS)
0120
0121 put_unaligned_be16(size / 2, txbuf + txsize);
0122 else
0123 put_unaligned_be16(0x0001, txbuf + txsize);
0124 txsize += 2;
0125 crc = scd30_serdev_calc_crc(txbuf, txsize);
0126 put_unaligned_le16(crc, txbuf + txsize);
0127 txsize += 2;
0128 rxsize = SCD30_SERDEV_RX_HEADER_SIZE + size + SCD30_SERDEV_CRC_SIZE;
0129 } else {
0130 if ((cmd == CMD_STOP_MEAS) || (cmd == CMD_RESET))
0131 arg = 0x0001;
0132
0133 txbuf[1] = SCD30_SERDEV_WRITE;
0134 put_unaligned_be16(arg, txbuf + txsize);
0135 txsize += 2;
0136 crc = scd30_serdev_calc_crc(txbuf, txsize);
0137 put_unaligned_le16(crc, txbuf + txsize);
0138 txsize += 2;
0139 rxsize = txsize;
0140 }
0141
0142 ret = scd30_serdev_xfer(state, txbuf, txsize, rxbuf, rxsize);
0143 if (ret)
0144 return ret;
0145
0146 switch (txbuf[1]) {
0147 case SCD30_SERDEV_WRITE:
0148 if (memcmp(txbuf, rxbuf, txsize)) {
0149 dev_err(state->dev, "wrong message received\n");
0150 return -EIO;
0151 }
0152 break;
0153 case SCD30_SERDEV_READ:
0154 if (rxbuf[2] != (rxsize - SCD30_SERDEV_RX_HEADER_SIZE - SCD30_SERDEV_CRC_SIZE)) {
0155 dev_err(state->dev, "received data size does not match header\n");
0156 return -EIO;
0157 }
0158
0159 rxsize -= SCD30_SERDEV_CRC_SIZE;
0160 crc = get_unaligned_le16(rxbuf + rxsize);
0161 if (crc != scd30_serdev_calc_crc(rxbuf, rxsize)) {
0162 dev_err(state->dev, "data integrity check failed\n");
0163 return -EIO;
0164 }
0165
0166 rxsize -= SCD30_SERDEV_RX_HEADER_SIZE;
0167 memcpy(rsp, rxbuf + SCD30_SERDEV_RX_HEADER_SIZE, rxsize);
0168 break;
0169 default:
0170 dev_err(state->dev, "received unknown op code\n");
0171 return -EIO;
0172 }
0173
0174 return 0;
0175 }
0176
0177 static int scd30_serdev_receive_buf(struct serdev_device *serdev,
0178 const unsigned char *buf, size_t size)
0179 {
0180 struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev);
0181 struct scd30_serdev_priv *priv;
0182 struct scd30_state *state;
0183 int num;
0184
0185 if (!indio_dev)
0186 return 0;
0187
0188 state = iio_priv(indio_dev);
0189 priv = state->priv;
0190
0191
0192 if (!priv->buf)
0193 return 0;
0194
0195 if (priv->num + size >= priv->num_expected)
0196 num = priv->num_expected - priv->num;
0197 else
0198 num = size;
0199
0200 memcpy(priv->buf + priv->num, buf, num);
0201 priv->num += num;
0202
0203 if (priv->num == priv->num_expected) {
0204 priv->buf = NULL;
0205 complete(&priv->meas_ready);
0206 }
0207
0208 return num;
0209 }
0210
0211 static const struct serdev_device_ops scd30_serdev_ops = {
0212 .receive_buf = scd30_serdev_receive_buf,
0213 .write_wakeup = serdev_device_write_wakeup,
0214 };
0215
0216 static int scd30_serdev_probe(struct serdev_device *serdev)
0217 {
0218 struct device *dev = &serdev->dev;
0219 struct scd30_serdev_priv *priv;
0220 int irq, ret;
0221
0222 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0223 if (!priv)
0224 return -ENOMEM;
0225
0226 init_completion(&priv->meas_ready);
0227 serdev_device_set_client_ops(serdev, &scd30_serdev_ops);
0228
0229 ret = devm_serdev_device_open(dev, serdev);
0230 if (ret)
0231 return ret;
0232
0233 serdev_device_set_baudrate(serdev, 19200);
0234 serdev_device_set_flow_control(serdev, false);
0235
0236 ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
0237 if (ret)
0238 return ret;
0239
0240 irq = fwnode_irq_get(dev_fwnode(dev), 0);
0241
0242 return scd30_probe(dev, irq, KBUILD_MODNAME, priv, scd30_serdev_command);
0243 }
0244
0245 static const struct of_device_id scd30_serdev_of_match[] = {
0246 { .compatible = "sensirion,scd30" },
0247 { }
0248 };
0249 MODULE_DEVICE_TABLE(of, scd30_serdev_of_match);
0250
0251 static struct serdev_device_driver scd30_serdev_driver = {
0252 .driver = {
0253 .name = KBUILD_MODNAME,
0254 .of_match_table = scd30_serdev_of_match,
0255 .pm = pm_sleep_ptr(&scd30_pm_ops),
0256 },
0257 .probe = scd30_serdev_probe,
0258 };
0259 module_serdev_device_driver(scd30_serdev_driver);
0260
0261 MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
0262 MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver");
0263 MODULE_LICENSE("GPL v2");
0264 MODULE_IMPORT_NS(IIO_SCD30);