History

iio: chemical: add support for Sensirion SPS30 sensor

Add support for Sensirion SPS30 particulate matter sensor.

Tomasz Duszynski committed 7 years ago

Commit 232e0f61 parent 17abc9e3 files · +419 −0Browse files

Unified · Split

3 files
  1. drivers/iio/chemical/Kconfig +11 −0
  2. drivers/iio/chemical/Makefile +1 −0
  3. drivers/iio/chemical/sps30.c +407 −0
drivers/iio/chemical/Kconfig+11−0
@@ -61,6 +61,17 @@ config IAQCORE · 60 unmodified lines
61 iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)61 iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
62 sensors62 sensors
63 63
64config SPS30
65 tristate "SPS30 particulate matter sensor"
66 depends on I2C
67 select CRC8
68 help
69 Say Y here to build support for the Sensirion SPS30 particulate
70 matter sensor.
71
72 To compile this driver as a module, choose M here: the module will
73 be called sps30.
74
64config VZ89X75config VZ89X
65 tristate "SGX Sensortech MiCS VZ89X VOC sensor"76 tristate "SGX Sensortech MiCS VZ89X VOC sensor"
66 depends on I2C77 depends on I2C
drivers/iio/chemical/Makefile+1−0
@@ -9,4 +9,5 @@ obj-$(CONFIG_BME680_I2C) += bme680_i2c.o · 8 unmodified lines
9obj-$(CONFIG_BME680_SPI) += bme680_spi.o9obj-$(CONFIG_BME680_SPI) += bme680_spi.o
10obj-$(CONFIG_CCS811) += ccs811.o10obj-$(CONFIG_CCS811) += ccs811.o
11obj-$(CONFIG_IAQCORE) += ams-iaq-core.o11obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
12obj-$(CONFIG_SPS30) += sps30.o
12obj-$(CONFIG_VZ89X) += vz89x.o13obj-$(CONFIG_VZ89X) += vz89x.o
drivers/iio/chemical/sps30.c+407−0
@@ -0,0 +1,407 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Sensirion SPS30 particulate matter sensor driver
4 *
5 * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
6 *
7 * I2C slave address: 0x69
8 *
9 * TODO:
10 * - support for turning on fan cleaning
11 * - support for reading/setting auto cleaning interval
12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <asm/unaligned.h>
17#include <linux/crc8.h>
18#include <linux/delay.h>
19#include <linux/i2c.h>
20#include <linux/iio/buffer.h>
21#include <linux/iio/iio.h>
22#include <linux/iio/sysfs.h>
23#include <linux/iio/trigger_consumer.h>
24#include <linux/iio/triggered_buffer.h>
25#include <linux/module.h>
26
27#define SPS30_CRC8_POLYNOMIAL 0x31
28/* max number of bytes needed to store PM measurements or serial string */
29#define SPS30_MAX_READ_SIZE 48
30/* sensor measures reliably up to 3000 ug / m3 */
31#define SPS30_MAX_PM 3000
32
33/* SPS30 commands */
34#define SPS30_START_MEAS 0x0010
35#define SPS30_STOP_MEAS 0x0104
36#define SPS30_RESET 0xd304
37#define SPS30_READ_DATA_READY_FLAG 0x0202
38#define SPS30_READ_DATA 0x0300
39#define SPS30_READ_SERIAL 0xd033
40
41enum {
42 PM1,
43 PM2P5,
44 PM4,
45 PM10,
46};
47
48struct sps30_state {
49 struct i2c_client *client;
50 /*
51 * Guards against concurrent access to sensor registers.
52 * Must be held whenever sequence of commands is to be executed.
53 */
54 struct mutex lock;
55};
56
57DECLARE_CRC8_TABLE(sps30_crc8_table);
58
59static int sps30_write_then_read(struct sps30_state *state, u8 *txbuf,
60 int txsize, u8 *rxbuf, int rxsize)
61{
62 int ret;
63
64 /*
65 * Sensor does not support repeated start so instead of
66 * sending two i2c messages in a row we just send one by one.
67 */
68 ret = i2c_master_send(state->client, txbuf, txsize);
69 if (ret != txsize)
70 return ret < 0 ? ret : -EIO;
71
72 if (!rxbuf)
73 return 0;
74
75 ret = i2c_master_recv(state->client, rxbuf, rxsize);
76 if (ret != rxsize)
77 return ret < 0 ? ret : -EIO;
78
79 return 0;
80}
81
82static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size)
83{
84 /*
85 * Internally sensor stores measurements in a following manner:
86 *
87 * PM1: upper two bytes, crc8, lower two bytes, crc8
88 * PM2P5: upper two bytes, crc8, lower two bytes, crc8
89 * PM4: upper two bytes, crc8, lower two bytes, crc8
90 * PM10: upper two bytes, crc8, lower two bytes, crc8
91 *
92 * What follows next are number concentration measurements and
93 * typical particle size measurement which we omit.
94 */
95 u8 buf[SPS30_MAX_READ_SIZE] = { cmd >> 8, cmd };
96 int i, ret = 0;
97
98 switch (cmd) {
99 case SPS30_START_MEAS:
100 buf[2] = 0x03;
101 buf[3] = 0x00;
102 buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
103 ret = sps30_write_then_read(state, buf, 5, NULL, 0);
104 break;
105 case SPS30_STOP_MEAS:
106 case SPS30_RESET:
107 ret = sps30_write_then_read(state, buf, 2, NULL, 0);
108 break;
109 case SPS30_READ_DATA_READY_FLAG:
110 case SPS30_READ_DATA:
111 case SPS30_READ_SERIAL:
112 /* every two data bytes are checksummed */
113 size += size / 2;
114 ret = sps30_write_then_read(state, buf, 2, buf, size);
115 break;
116 }
117
118 if (ret)
119 return ret;
120
121 /* validate received data and strip off crc bytes */
122 for (i = 0; i < size; i += 3) {
123 u8 crc = crc8(sps30_crc8_table, &buf[i], 2, CRC8_INIT_VALUE);
124
125 if (crc != buf[i + 2]) {
126 dev_err(&state->client->dev,
127 "data integrity check failed\n");
128 return -EIO;
129 }
130
131 *data++ = buf[i];
132 *data++ = buf[i + 1];
133 }
134
135 return 0;
136}
137
138static s32 sps30_float_to_int_clamped(const u8 *fp)
139{
140 int val = get_unaligned_be32(fp);
141 int mantissa = val & GENMASK(22, 0);
142 /* this is fine since passed float is always non-negative */
143 int exp = val >> 23;
144 int fraction, shift;
145
146 /* special case 0 */
147 if (!exp && !mantissa)
148 return 0;
149
150 exp -= 127;
151 if (exp < 0) {
152 /* return values ranging from 1 to 99 */
153 return ((((1 << 23) + mantissa) * 100) >> 23) >> (-exp);
154 }
155
156 /* return values ranging from 100 to 300000 */
157 shift = 23 - exp;
158 val = (1 << exp) + (mantissa >> shift);
159 if (val >= SPS30_MAX_PM)
160 return SPS30_MAX_PM * 100;
161
162 fraction = mantissa & GENMASK(shift - 1, 0);
163
164 return val * 100 + ((fraction * 100) >> shift);
165}
166
167static int sps30_do_meas(struct sps30_state *state, s32 *data, int size)
168{
169 int i, ret, tries = 5;
170 u8 tmp[16];
171
172 while (tries--) {
173 ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2);
174 if (ret)
175 return -EIO;
176
177 /* new measurements ready to be read */
178 if (tmp[1] == 1)
179 break;
180
181 msleep_interruptible(300);
182 }
183
184 if (!tries)
185 return -ETIMEDOUT;
186
187 ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size);
188 if (ret)
189 return ret;
190
191 for (i = 0; i < size; i++)
192 data[i] = sps30_float_to_int_clamped(&tmp[4 * i]);
193
194 return 0;
195}
196
197static irqreturn_t sps30_trigger_handler(int irq, void *p)
198{
199 struct iio_poll_func *pf = p;
200 struct iio_dev *indio_dev = pf->indio_dev;
201 struct sps30_state *state = iio_priv(indio_dev);
202 int ret;
203 s32 data[4 + 2]; /* PM1, PM2P5, PM4, PM10, timestamp */
204
205 mutex_lock(&state->lock);
206 ret = sps30_do_meas(state, data, 4);
207 mutex_unlock(&state->lock);
208 if (ret)
209 goto err;
210
211 iio_push_to_buffers_with_timestamp(indio_dev, data,
212 iio_get_time_ns(indio_dev));
213err:
214 iio_trigger_notify_done(indio_dev->trig);
215
216 return IRQ_HANDLED;
217}
218
219static int sps30_read_raw(struct iio_dev *indio_dev,
220 struct iio_chan_spec const *chan,
221 int *val, int *val2, long mask)
222{
223 struct sps30_state *state = iio_priv(indio_dev);
224 int data[4], ret = -EINVAL;
225
226 switch (mask) {
227 case IIO_CHAN_INFO_PROCESSED:
228 switch (chan->type) {
229 case IIO_MASSCONCENTRATION:
230 mutex_lock(&state->lock);
231 /* read up to the number of bytes actually needed */
232 switch (chan->channel2) {
233 case IIO_MOD_PM1:
234 ret = sps30_do_meas(state, data, 1);
235 break;
236 case IIO_MOD_PM2P5:
237 ret = sps30_do_meas(state, data, 2);
238 break;
239 case IIO_MOD_PM4:
240 ret = sps30_do_meas(state, data, 3);
241 break;
242 case IIO_MOD_PM10:
243 ret = sps30_do_meas(state, data, 4);
244 break;
245 }
246 mutex_unlock(&state->lock);
247 if (ret)
248 return ret;
249
250 *val = data[chan->address] / 100;
251 *val2 = (data[chan->address] % 100) * 10000;
252
253 return IIO_VAL_INT_PLUS_MICRO;
254 default:
255 return -EINVAL;
256 }
257 case IIO_CHAN_INFO_SCALE:
258 switch (chan->type) {
259 case IIO_MASSCONCENTRATION:
260 switch (chan->channel2) {
261 case IIO_MOD_PM1:
262 case IIO_MOD_PM2P5:
263 case IIO_MOD_PM4:
264 case IIO_MOD_PM10:
265 *val = 0;
266 *val2 = 10000;
267
268 return IIO_VAL_INT_PLUS_MICRO;
269 }
270 default:
271 return -EINVAL;
272 }
273 }
274
275 return -EINVAL;
276}
277
278static const struct iio_info sps30_info = {
279 .read_raw = sps30_read_raw,
280};
281
282#define SPS30_CHAN(_index, _mod) { \
283 .type = IIO_MASSCONCENTRATION, \
284 .modified = 1, \
285 .channel2 = IIO_MOD_ ## _mod, \
286 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
287 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
288 .address = _mod, \
289 .scan_index = _index, \
290 .scan_type = { \
291 .sign = 'u', \
292 .realbits = 19, \
293 .storagebits = 32, \
294 .endianness = IIO_CPU, \
295 }, \
296}
297
298static const struct iio_chan_spec sps30_channels[] = {
299 SPS30_CHAN(0, PM1),
300 SPS30_CHAN(1, PM2P5),
301 SPS30_CHAN(2, PM4),
302 SPS30_CHAN(3, PM10),
303 IIO_CHAN_SOFT_TIMESTAMP(4),
304};
305
306static void sps30_stop_meas(void *data)
307{
308 struct sps30_state *state = data;
309
310 sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
311}
312
313static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 };
314
315static int sps30_probe(struct i2c_client *client)
316{
317 struct iio_dev *indio_dev;
318 struct sps30_state *state;
319 u8 buf[32];
320 int ret;
321
322 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
323 return -EOPNOTSUPP;
324
325 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state));
326 if (!indio_dev)
327 return -ENOMEM;
328
329 state = iio_priv(indio_dev);
330 i2c_set_clientdata(client, indio_dev);
331 state->client = client;
332 indio_dev->dev.parent = &client->dev;
333 indio_dev->info = &sps30_info;
334 indio_dev->name = client->name;
335 indio_dev->channels = sps30_channels;
336 indio_dev->num_channels = ARRAY_SIZE(sps30_channels);
337 indio_dev->modes = INDIO_DIRECT_MODE;
338 indio_dev->available_scan_masks = sps30_scan_masks;
339
340 mutex_init(&state->lock);
341 crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL);
342
343 ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0);
344 if (ret) {
345 dev_err(&client->dev, "failed to reset device\n");
346 return ret;
347 }
348 msleep(300);
349 /*
350 * Power-on-reset causes sensor to produce some glitch on i2c bus and
351 * some controllers end up in error state. Recover simply by placing
352 * some data on the bus, for example STOP_MEAS command, which
353 * is NOP in this case.
354 */
355 sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
356
357 ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf));
358 if (ret) {
359 dev_err(&client->dev, "failed to read serial number\n");
360 return ret;
361 }
362 /* returned serial number is already NUL terminated */
363 dev_info(&client->dev, "serial number: %s\n", buf);
364
365 ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0);
366 if (ret) {
367 dev_err(&client->dev, "failed to start measurement\n");
368 return ret;
369 }
370
371 ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state);
372 if (ret)
373 return ret;
374
375 ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
376 sps30_trigger_handler, NULL);
377 if (ret)
378 return ret;
379
380 return devm_iio_device_register(&client->dev, indio_dev);
381}
382
383static const struct i2c_device_id sps30_id[] = {
384 { "sps30" },
385 { }
386};
387MODULE_DEVICE_TABLE(i2c, sps30_id);
388
389static const struct of_device_id sps30_of_match[] = {
390 { .compatible = "sensirion,sps30" },
391 { }
392};
393MODULE_DEVICE_TABLE(of, sps30_of_match);
394
395static struct i2c_driver sps30_driver = {
396 .driver = {
397 .name = "sps30",
398 .of_match_table = sps30_of_match,
399 },
400 .id_table = sps30_id,
401 .probe_new = sps30_probe,
402};
403module_i2c_driver(sps30_driver);
404
405MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
406MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver");
407MODULE_LICENSE("GPL v2");