Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Sensirion SPS30 particulate matter sensor i2c driver
0004  *
0005  * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
0006  *
0007  * I2C slave address: 0x69
0008  */
0009 #include <asm/unaligned.h>
0010 #include <linux/crc8.h>
0011 #include <linux/delay.h>
0012 #include <linux/device.h>
0013 #include <linux/errno.h>
0014 #include <linux/i2c.h>
0015 #include <linux/mod_devicetable.h>
0016 #include <linux/module.h>
0017 #include <linux/types.h>
0018 
0019 #include "sps30.h"
0020 
0021 #define SPS30_I2C_CRC8_POLYNOMIAL 0x31
0022 /* max number of bytes needed to store PM measurements or serial string */
0023 #define SPS30_I2C_MAX_BUF_SIZE 48
0024 
0025 DECLARE_CRC8_TABLE(sps30_i2c_crc8_table);
0026 
0027 #define SPS30_I2C_START_MEAS 0x0010
0028 #define SPS30_I2C_STOP_MEAS 0x0104
0029 #define SPS30_I2C_READ_MEAS 0x0300
0030 #define SPS30_I2C_MEAS_READY 0x0202
0031 #define SPS30_I2C_RESET 0xd304
0032 #define SPS30_I2C_CLEAN_FAN 0x5607
0033 #define SPS30_I2C_PERIOD 0x8004
0034 #define SPS30_I2C_READ_SERIAL 0xd033
0035 #define SPS30_I2C_READ_VERSION 0xd100
0036 
0037 static int sps30_i2c_xfer(struct sps30_state *state, unsigned char *txbuf, size_t txsize,
0038               unsigned char *rxbuf, size_t rxsize)
0039 {
0040     struct i2c_client *client = to_i2c_client(state->dev);
0041     int ret;
0042 
0043     /*
0044      * Sensor does not support repeated start so instead of
0045      * sending two i2c messages in a row we just send one by one.
0046      */
0047     ret = i2c_master_send(client, txbuf, txsize);
0048     if (ret < 0)
0049         return ret;
0050     if (ret != txsize)
0051         return -EIO;
0052 
0053     if (!rxsize)
0054         return 0;
0055 
0056     ret = i2c_master_recv(client, rxbuf, rxsize);
0057     if (ret < 0)
0058         return ret;
0059     if (ret != rxsize)
0060         return -EIO;
0061 
0062     return 0;
0063 }
0064 
0065 static int sps30_i2c_command(struct sps30_state *state, u16 cmd, void *arg, size_t arg_size,
0066                  void *rsp, size_t rsp_size)
0067 {
0068     /*
0069      * Internally sensor stores measurements in a following manner:
0070      *
0071      * PM1: upper two bytes, crc8, lower two bytes, crc8
0072      * PM2P5: upper two bytes, crc8, lower two bytes, crc8
0073      * PM4: upper two bytes, crc8, lower two bytes, crc8
0074      * PM10: upper two bytes, crc8, lower two bytes, crc8
0075      *
0076      * What follows next are number concentration measurements and
0077      * typical particle size measurement which we omit.
0078      */
0079     unsigned char buf[SPS30_I2C_MAX_BUF_SIZE];
0080     unsigned char *tmp;
0081     unsigned char crc;
0082     size_t i;
0083     int ret;
0084 
0085     put_unaligned_be16(cmd, buf);
0086     i = 2;
0087 
0088     if (rsp) {
0089         /* each two bytes are followed by a crc8 */
0090         rsp_size += rsp_size / 2;
0091     } else {
0092         tmp = arg;
0093 
0094         while (arg_size) {
0095             buf[i] = *tmp++;
0096             buf[i + 1] = *tmp++;
0097             buf[i + 2] = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE);
0098             arg_size -= 2;
0099             i += 3;
0100         }
0101     }
0102 
0103     ret = sps30_i2c_xfer(state, buf, i, buf, rsp_size);
0104     if (ret)
0105         return ret;
0106 
0107     /* validate received data and strip off crc bytes */
0108     tmp = rsp;
0109     for (i = 0; i < rsp_size; i += 3) {
0110         crc = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE);
0111         if (crc != buf[i + 2]) {
0112             dev_err(state->dev, "data integrity check failed\n");
0113             return -EIO;
0114         }
0115 
0116         *tmp++ = buf[i];
0117         *tmp++ = buf[i + 1];
0118     }
0119 
0120     return 0;
0121 }
0122 
0123 static int sps30_i2c_start_meas(struct sps30_state *state)
0124 {
0125     /* request BE IEEE754 formatted data */
0126     unsigned char buf[] = { 0x03, 0x00 };
0127 
0128     return sps30_i2c_command(state, SPS30_I2C_START_MEAS, buf, sizeof(buf), NULL, 0);
0129 }
0130 
0131 static int sps30_i2c_stop_meas(struct sps30_state *state)
0132 {
0133     return sps30_i2c_command(state, SPS30_I2C_STOP_MEAS, NULL, 0, NULL, 0);
0134 }
0135 
0136 static int sps30_i2c_reset(struct sps30_state *state)
0137 {
0138     int ret;
0139 
0140     ret = sps30_i2c_command(state, SPS30_I2C_RESET, NULL, 0, NULL, 0);
0141     msleep(500);
0142     /*
0143      * Power-on-reset causes sensor to produce some glitch on i2c bus and
0144      * some controllers end up in error state. Recover simply by placing
0145      * some data on the bus, for example STOP_MEAS command, which
0146      * is NOP in this case.
0147      */
0148     sps30_i2c_stop_meas(state);
0149 
0150     return ret;
0151 }
0152 
0153 static bool sps30_i2c_meas_ready(struct sps30_state *state)
0154 {
0155     unsigned char buf[2];
0156     int ret;
0157 
0158     ret = sps30_i2c_command(state, SPS30_I2C_MEAS_READY, NULL, 0, buf, sizeof(buf));
0159     if (ret)
0160         return false;
0161 
0162     return buf[1];
0163 }
0164 
0165 static int sps30_i2c_read_meas(struct sps30_state *state, __be32 *meas, size_t num)
0166 {
0167     /* measurements are ready within a second */
0168     if (msleep_interruptible(1000))
0169         return -EINTR;
0170 
0171     if (!sps30_i2c_meas_ready(state))
0172         return -ETIMEDOUT;
0173 
0174     return sps30_i2c_command(state, SPS30_I2C_READ_MEAS, NULL, 0, meas, sizeof(num) * num);
0175 }
0176 
0177 static int sps30_i2c_clean_fan(struct sps30_state *state)
0178 {
0179     return sps30_i2c_command(state, SPS30_I2C_CLEAN_FAN, NULL, 0, NULL, 0);
0180 }
0181 
0182 static int sps30_i2c_read_cleaning_period(struct sps30_state *state, __be32 *period)
0183 {
0184     return sps30_i2c_command(state, SPS30_I2C_PERIOD, NULL, 0, period, sizeof(*period));
0185 }
0186 
0187 static int sps30_i2c_write_cleaning_period(struct sps30_state *state, __be32 period)
0188 {
0189     return sps30_i2c_command(state, SPS30_I2C_PERIOD, &period, sizeof(period), NULL, 0);
0190 }
0191 
0192 static int sps30_i2c_show_info(struct sps30_state *state)
0193 {
0194     /* extra nul just in case */
0195     unsigned char buf[32 + 1] = { 0x00 };
0196     int ret;
0197 
0198     ret = sps30_i2c_command(state, SPS30_I2C_READ_SERIAL, NULL, 0, buf, sizeof(buf) - 1);
0199     if (ret)
0200         return ret;
0201 
0202     dev_info(state->dev, "serial number: %s\n", buf);
0203 
0204     ret = sps30_i2c_command(state, SPS30_I2C_READ_VERSION, NULL, 0, buf, 2);
0205     if (ret)
0206         return ret;
0207 
0208     dev_info(state->dev, "fw version: %u.%u\n", buf[0], buf[1]);
0209 
0210     return 0;
0211 }
0212 
0213 static const struct sps30_ops sps30_i2c_ops = {
0214     .start_meas = sps30_i2c_start_meas,
0215     .stop_meas = sps30_i2c_stop_meas,
0216     .read_meas = sps30_i2c_read_meas,
0217     .reset = sps30_i2c_reset,
0218     .clean_fan = sps30_i2c_clean_fan,
0219     .read_cleaning_period = sps30_i2c_read_cleaning_period,
0220     .write_cleaning_period = sps30_i2c_write_cleaning_period,
0221     .show_info = sps30_i2c_show_info,
0222 };
0223 
0224 static int sps30_i2c_probe(struct i2c_client *client)
0225 {
0226     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
0227         return -EOPNOTSUPP;
0228 
0229     crc8_populate_msb(sps30_i2c_crc8_table, SPS30_I2C_CRC8_POLYNOMIAL);
0230 
0231     return sps30_probe(&client->dev, client->name, NULL, &sps30_i2c_ops);
0232 }
0233 
0234 static const struct i2c_device_id sps30_i2c_id[] = {
0235     { "sps30" },
0236     { }
0237 };
0238 MODULE_DEVICE_TABLE(i2c, sps30_i2c_id);
0239 
0240 static const struct of_device_id sps30_i2c_of_match[] = {
0241     { .compatible = "sensirion,sps30" },
0242     { }
0243 };
0244 MODULE_DEVICE_TABLE(of, sps30_i2c_of_match);
0245 
0246 static struct i2c_driver sps30_i2c_driver = {
0247     .driver = {
0248         .name = KBUILD_MODNAME,
0249         .of_match_table = sps30_i2c_of_match,
0250     },
0251     .id_table = sps30_i2c_id,
0252     .probe_new = sps30_i2c_probe,
0253 };
0254 module_i2c_driver(sps30_i2c_driver);
0255 
0256 MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
0257 MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver");
0258 MODULE_LICENSE("GPL v2");
0259 MODULE_IMPORT_NS(IIO_SPS30);