sps30_i2c.c (6592B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Sensirion SPS30 particulate matter sensor i2c driver 4 * 5 * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> 6 * 7 * I2C slave address: 0x69 8 */ 9#include <asm/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 0x31 22/* max number of bytes needed to store PM measurements or serial string */ 23#define SPS30_I2C_MAX_BUF_SIZE 48 24 25DECLARE_CRC8_TABLE(sps30_i2c_crc8_table); 26 27#define SPS30_I2C_START_MEAS 0x0010 28#define SPS30_I2C_STOP_MEAS 0x0104 29#define SPS30_I2C_READ_MEAS 0x0300 30#define SPS30_I2C_MEAS_READY 0x0202 31#define SPS30_I2C_RESET 0xd304 32#define SPS30_I2C_CLEAN_FAN 0x5607 33#define SPS30_I2C_PERIOD 0x8004 34#define SPS30_I2C_READ_SERIAL 0xd033 35#define SPS30_I2C_READ_VERSION 0xd100 36 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 of 45 * 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, crc8 72 * PM2P5: upper two bytes, crc8, lower two bytes, crc8 73 * PM4: upper two bytes, crc8, lower two bytes, crc8 74 * PM10: upper two bytes, crc8, lower two bytes, crc8 75 * 76 * What follows next are number concentration measurements and 77 * 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 and 144 * some controllers end up in error state. Recover simply by placing 145 * some data on the bus, for example STOP_MEAS command, which 146 * 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_new = 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");