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
6161 iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
6262 sensors
6363
64+config 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+
6475 config VZ89X
6576 tristate "SGX Sensortech MiCS VZ89X VOC sensor"
6677 depends on I2C
drivers/iio/chemical/Makefile+1−0
@@ -9,4 +9,5 @@ obj-$(CONFIG_BME680_I2C) += bme680_i2c.o · 8 unmodified lines
99 obj-$(CONFIG_BME680_SPI) += bme680_spi.o
1010 obj-$(CONFIG_CCS811) += ccs811.o
1111 obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
12+obj-$(CONFIG_SPS30) += sps30.o
1213 obj-$(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+
41+enum {
42+ PM1,
43+ PM2P5,
44+ PM4,
45+ PM10,
46+};
47+
48+struct 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+
57+DECLARE_CRC8_TABLE(sps30_crc8_table);
58+
59+static 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+
82+static 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+
138+static 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+
167+static 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+
197+static 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));
213+err:
214+ iio_trigger_notify_done(indio_dev->trig);
215+
216+ return IRQ_HANDLED;
217+}
218+
219+static 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+
278+static 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+
298+static 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+
306+static 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+
313+static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 };
314+
315+static 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+
383+static const struct i2c_device_id sps30_id[] = {
384+ { "sps30" },
385+ { }
386+};
387+MODULE_DEVICE_TABLE(i2c, sps30_id);
388+
389+static const struct of_device_id sps30_of_match[] = {
390+ { .compatible = "sensirion,sps30" },
391+ { }
392+};
393+MODULE_DEVICE_TABLE(of, sps30_of_match);
394+
395+static 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+};
403+module_i2c_driver(sps30_driver);
404+
405+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
406+MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver");
407+MODULE_LICENSE("GPL v2");