260 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Sensirion SPS30 particulate matter sensor i2c driver4 *5 * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>6 *7 * I2C slave address: 0x698 */9#include <linux/unaligned.h>10#include <linux/crc8.h>11#include <linux/delay.h>12#include <linux/device.h>13#include <linux/errno.h>14#include <linux/i2c.h>15#include <linux/mod_devicetable.h>16#include <linux/module.h>17#include <linux/types.h>18 19#include "sps30.h"20 21#define SPS30_I2C_CRC8_POLYNOMIAL 0x3122/* max number of bytes needed to store PM measurements or serial string */23#define SPS30_I2C_MAX_BUF_SIZE 4824 25DECLARE_CRC8_TABLE(sps30_i2c_crc8_table);26 27#define SPS30_I2C_START_MEAS 0x001028#define SPS30_I2C_STOP_MEAS 0x010429#define SPS30_I2C_READ_MEAS 0x030030#define SPS30_I2C_MEAS_READY 0x020231#define SPS30_I2C_RESET 0xd30432#define SPS30_I2C_CLEAN_FAN 0x560733#define SPS30_I2C_PERIOD 0x800434#define SPS30_I2C_READ_SERIAL 0xd03335#define SPS30_I2C_READ_VERSION 0xd10036 37static int sps30_i2c_xfer(struct sps30_state *state, unsigned char *txbuf, size_t txsize,38 unsigned char *rxbuf, size_t rxsize)39{40 struct i2c_client *client = to_i2c_client(state->dev);41 int ret;42 43 /*44 * Sensor does not support repeated start so instead of45 * sending two i2c messages in a row we just send one by one.46 */47 ret = i2c_master_send(client, txbuf, txsize);48 if (ret < 0)49 return ret;50 if (ret != txsize)51 return -EIO;52 53 if (!rxsize)54 return 0;55 56 ret = i2c_master_recv(client, rxbuf, rxsize);57 if (ret < 0)58 return ret;59 if (ret != rxsize)60 return -EIO;61 62 return 0;63}64 65static int sps30_i2c_command(struct sps30_state *state, u16 cmd, void *arg, size_t arg_size,66 void *rsp, size_t rsp_size)67{68 /*69 * Internally sensor stores measurements in a following manner:70 *71 * PM1: upper two bytes, crc8, lower two bytes, crc872 * PM2P5: upper two bytes, crc8, lower two bytes, crc873 * PM4: upper two bytes, crc8, lower two bytes, crc874 * PM10: upper two bytes, crc8, lower two bytes, crc875 *76 * What follows next are number concentration measurements and77 * typical particle size measurement which we omit.78 */79 unsigned char buf[SPS30_I2C_MAX_BUF_SIZE];80 unsigned char *tmp;81 unsigned char crc;82 size_t i;83 int ret;84 85 put_unaligned_be16(cmd, buf);86 i = 2;87 88 if (rsp) {89 /* each two bytes are followed by a crc8 */90 rsp_size += rsp_size / 2;91 } else {92 tmp = arg;93 94 while (arg_size) {95 buf[i] = *tmp++;96 buf[i + 1] = *tmp++;97 buf[i + 2] = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE);98 arg_size -= 2;99 i += 3;100 }101 }102 103 ret = sps30_i2c_xfer(state, buf, i, buf, rsp_size);104 if (ret)105 return ret;106 107 /* validate received data and strip off crc bytes */108 tmp = rsp;109 for (i = 0; i < rsp_size; i += 3) {110 crc = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE);111 if (crc != buf[i + 2]) {112 dev_err(state->dev, "data integrity check failed\n");113 return -EIO;114 }115 116 *tmp++ = buf[i];117 *tmp++ = buf[i + 1];118 }119 120 return 0;121}122 123static int sps30_i2c_start_meas(struct sps30_state *state)124{125 /* request BE IEEE754 formatted data */126 unsigned char buf[] = { 0x03, 0x00 };127 128 return sps30_i2c_command(state, SPS30_I2C_START_MEAS, buf, sizeof(buf), NULL, 0);129}130 131static int sps30_i2c_stop_meas(struct sps30_state *state)132{133 return sps30_i2c_command(state, SPS30_I2C_STOP_MEAS, NULL, 0, NULL, 0);134}135 136static int sps30_i2c_reset(struct sps30_state *state)137{138 int ret;139 140 ret = sps30_i2c_command(state, SPS30_I2C_RESET, NULL, 0, NULL, 0);141 msleep(500);142 /*143 * Power-on-reset causes sensor to produce some glitch on i2c bus and144 * some controllers end up in error state. Recover simply by placing145 * some data on the bus, for example STOP_MEAS command, which146 * is NOP in this case.147 */148 sps30_i2c_stop_meas(state);149 150 return ret;151}152 153static bool sps30_i2c_meas_ready(struct sps30_state *state)154{155 unsigned char buf[2];156 int ret;157 158 ret = sps30_i2c_command(state, SPS30_I2C_MEAS_READY, NULL, 0, buf, sizeof(buf));159 if (ret)160 return false;161 162 return buf[1];163}164 165static int sps30_i2c_read_meas(struct sps30_state *state, __be32 *meas, size_t num)166{167 /* measurements are ready within a second */168 if (msleep_interruptible(1000))169 return -EINTR;170 171 if (!sps30_i2c_meas_ready(state))172 return -ETIMEDOUT;173 174 return sps30_i2c_command(state, SPS30_I2C_READ_MEAS, NULL, 0, meas, sizeof(num) * num);175}176 177static int sps30_i2c_clean_fan(struct sps30_state *state)178{179 return sps30_i2c_command(state, SPS30_I2C_CLEAN_FAN, NULL, 0, NULL, 0);180}181 182static int sps30_i2c_read_cleaning_period(struct sps30_state *state, __be32 *period)183{184 return sps30_i2c_command(state, SPS30_I2C_PERIOD, NULL, 0, period, sizeof(*period));185}186 187static int sps30_i2c_write_cleaning_period(struct sps30_state *state, __be32 period)188{189 return sps30_i2c_command(state, SPS30_I2C_PERIOD, &period, sizeof(period), NULL, 0);190}191 192static int sps30_i2c_show_info(struct sps30_state *state)193{194 /* extra nul just in case */195 unsigned char buf[32 + 1] = { 0x00 };196 int ret;197 198 ret = sps30_i2c_command(state, SPS30_I2C_READ_SERIAL, NULL, 0, buf, sizeof(buf) - 1);199 if (ret)200 return ret;201 202 dev_info(state->dev, "serial number: %s\n", buf);203 204 ret = sps30_i2c_command(state, SPS30_I2C_READ_VERSION, NULL, 0, buf, 2);205 if (ret)206 return ret;207 208 dev_info(state->dev, "fw version: %u.%u\n", buf[0], buf[1]);209 210 return 0;211}212 213static const struct sps30_ops sps30_i2c_ops = {214 .start_meas = sps30_i2c_start_meas,215 .stop_meas = sps30_i2c_stop_meas,216 .read_meas = sps30_i2c_read_meas,217 .reset = sps30_i2c_reset,218 .clean_fan = sps30_i2c_clean_fan,219 .read_cleaning_period = sps30_i2c_read_cleaning_period,220 .write_cleaning_period = sps30_i2c_write_cleaning_period,221 .show_info = sps30_i2c_show_info,222};223 224static int sps30_i2c_probe(struct i2c_client *client)225{226 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))227 return -EOPNOTSUPP;228 229 crc8_populate_msb(sps30_i2c_crc8_table, SPS30_I2C_CRC8_POLYNOMIAL);230 231 return sps30_probe(&client->dev, client->name, NULL, &sps30_i2c_ops);232}233 234static const struct i2c_device_id sps30_i2c_id[] = {235 { "sps30" },236 { }237};238MODULE_DEVICE_TABLE(i2c, sps30_i2c_id);239 240static const struct of_device_id sps30_i2c_of_match[] = {241 { .compatible = "sensirion,sps30" },242 { }243};244MODULE_DEVICE_TABLE(of, sps30_i2c_of_match);245 246static struct i2c_driver sps30_i2c_driver = {247 .driver = {248 .name = KBUILD_MODNAME,249 .of_match_table = sps30_i2c_of_match,250 },251 .id_table = sps30_i2c_id,252 .probe = sps30_i2c_probe,253};254module_i2c_driver(sps30_i2c_driver);255 256MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");257MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver");258MODULE_LICENSE("GPL v2");259MODULE_IMPORT_NS(IIO_SPS30);260