xref: /linux/drivers/iio/chemical/sps30_serial.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Sensirion SPS30 particulate matter sensor serial driver
4  *
5  * Copyright (c) 2021 Tomasz Duszynski <tomasz.duszynski@octakon.com>
6  */
7 #include <linux/completion.h>
8 #include <linux/device.h>
9 #include <linux/errno.h>
10 #include <linux/iio/iio.h>
11 #include <linux/minmax.h>
12 #include <linux/mod_devicetable.h>
13 #include <linux/module.h>
14 #include <linux/serdev.h>
15 #include <linux/types.h>
16 
17 #include "sps30.h"
18 
19 #define SPS30_SERIAL_DEV_NAME "sps30"
20 
21 #define SPS30_SERIAL_SOF_EOF 0x7e
22 #define SPS30_SERIAL_TIMEOUT msecs_to_jiffies(20)
23 #define SPS30_SERIAL_MAX_BUF_SIZE 263
24 #define SPS30_SERIAL_ESCAPE_CHAR 0x7d
25 
26 #define SPS30_SERIAL_FRAME_MIN_SIZE 7
27 #define SPS30_SERIAL_FRAME_ADR_OFFSET 1
28 #define SPS30_SERIAL_FRAME_CMD_OFFSET 2
29 #define SPS30_SERIAL_FRAME_MOSI_LEN_OFFSET 3
30 #define SPS30_SERIAL_FRAME_MISO_STATE_OFFSET 3
31 #define SPS30_SERIAL_FRAME_MISO_LEN_OFFSET 4
32 #define SPS30_SERIAL_FRAME_MISO_DATA_OFFSET 5
33 
34 #define SPS30_SERIAL_START_MEAS 0x00
35 #define SPS30_SERIAL_STOP_MEAS 0x01
36 #define SPS30_SERIAL_READ_MEAS 0x03
37 #define SPS30_SERIAL_RESET 0xd3
38 #define SPS30_SERIAL_CLEAN_FAN 0x56
39 #define SPS30_SERIAL_PERIOD 0x80
40 #define SPS30_SERIAL_DEV_INFO 0xd0
41 #define SPS30_SERIAL_READ_VERSION 0xd1
42 
43 struct sps30_serial_priv {
44 	struct completion new_frame;
45 	unsigned char buf[SPS30_SERIAL_MAX_BUF_SIZE];
46 	size_t num;
47 	bool escaped;
48 	bool done;
49 };
50 
51 static int sps30_serial_xfer(struct sps30_state *state, const unsigned char *buf, size_t size)
52 {
53 	struct serdev_device *serdev = to_serdev_device(state->dev);
54 	struct sps30_serial_priv *priv = state->priv;
55 	int ret;
56 
57 	priv->num = 0;
58 	priv->escaped = false;
59 	priv->done = false;
60 
61 	ret = serdev_device_write(serdev, buf, size, SPS30_SERIAL_TIMEOUT);
62 	if (ret < 0)
63 		return ret;
64 	if (ret != size)
65 		return -EIO;
66 
67 	ret = wait_for_completion_interruptible_timeout(&priv->new_frame, SPS30_SERIAL_TIMEOUT);
68 	if (ret < 0)
69 		return ret;
70 	if (!ret)
71 		return -ETIMEDOUT;
72 
73 	return 0;
74 }
75 
76 static const struct {
77 	unsigned char byte;
78 	unsigned char byte2;
79 } sps30_serial_bytes[] = {
80 	{ 0x11, 0x31 },
81 	{ 0x13, 0x33 },
82 	{ 0x7e, 0x5e },
83 	{ 0x7d, 0x5d },
84 };
85 
86 static int sps30_serial_put_byte(unsigned char *buf, unsigned char byte)
87 {
88 	int i;
89 
90 	for (i = 0; i < ARRAY_SIZE(sps30_serial_bytes); i++) {
91 		if (sps30_serial_bytes[i].byte != byte)
92 			continue;
93 
94 		buf[0] = SPS30_SERIAL_ESCAPE_CHAR;
95 		buf[1] = sps30_serial_bytes[i].byte2;
96 
97 		return 2;
98 	}
99 
100 	buf[0] = byte;
101 
102 	return 1;
103 }
104 
105 static char sps30_serial_get_byte(bool escaped, unsigned char byte2)
106 {
107 	int i;
108 
109 	if (!escaped)
110 		return byte2;
111 
112 	for (i = 0; i < ARRAY_SIZE(sps30_serial_bytes); i++) {
113 		if (sps30_serial_bytes[i].byte2 != byte2)
114 			continue;
115 
116 		return sps30_serial_bytes[i].byte;
117 	}
118 
119 	return 0;
120 }
121 
122 static unsigned char sps30_serial_calc_chksum(const unsigned char *buf, size_t num)
123 {
124 	unsigned int chksum = 0;
125 	size_t i;
126 
127 	for (i = 0; i < num; i++)
128 		chksum += buf[i];
129 
130 	return ~chksum;
131 }
132 
133 static int sps30_serial_prep_frame(unsigned char *buf, unsigned char cmd,
134 				   const unsigned char *arg, size_t arg_size)
135 {
136 	unsigned char chksum;
137 	int num = 0;
138 	size_t i;
139 
140 	buf[num++] = SPS30_SERIAL_SOF_EOF;
141 	buf[num++] = 0;
142 	num += sps30_serial_put_byte(buf + num, cmd);
143 	num += sps30_serial_put_byte(buf + num, arg_size);
144 
145 	for (i = 0; i < arg_size; i++)
146 		num += sps30_serial_put_byte(buf + num, arg[i]);
147 
148 	/* SOF isn't checksummed */
149 	chksum = sps30_serial_calc_chksum(buf + 1, num - 1);
150 	num += sps30_serial_put_byte(buf + num, chksum);
151 	buf[num++] = SPS30_SERIAL_SOF_EOF;
152 
153 	return num;
154 }
155 
156 static bool sps30_serial_frame_valid(struct sps30_state *state, const unsigned char *buf)
157 {
158 	struct sps30_serial_priv *priv = state->priv;
159 	unsigned char chksum;
160 
161 	if ((priv->num < SPS30_SERIAL_FRAME_MIN_SIZE) ||
162 	    (priv->num != SPS30_SERIAL_FRAME_MIN_SIZE +
163 	     priv->buf[SPS30_SERIAL_FRAME_MISO_LEN_OFFSET])) {
164 		dev_err(state->dev, "frame has invalid number of bytes\n");
165 		return false;
166 	}
167 
168 	if ((priv->buf[SPS30_SERIAL_FRAME_ADR_OFFSET] != buf[SPS30_SERIAL_FRAME_ADR_OFFSET]) ||
169 	    (priv->buf[SPS30_SERIAL_FRAME_CMD_OFFSET] != buf[SPS30_SERIAL_FRAME_CMD_OFFSET])) {
170 		dev_err(state->dev, "frame has wrong ADR and CMD bytes\n");
171 		return false;
172 	}
173 
174 	if (priv->buf[SPS30_SERIAL_FRAME_MISO_STATE_OFFSET]) {
175 		dev_err(state->dev, "frame with non-zero state received (0x%02x)\n",
176 			priv->buf[SPS30_SERIAL_FRAME_MISO_STATE_OFFSET]);
177 		return false;
178 	}
179 
180 	/* SOF, checksum and EOF are not checksummed */
181 	chksum = sps30_serial_calc_chksum(priv->buf + 1, priv->num - 3);
182 	if (priv->buf[priv->num - 2] != chksum) {
183 		dev_err(state->dev, "frame integrity check failed\n");
184 		return false;
185 	}
186 
187 	return true;
188 }
189 
190 static int sps30_serial_command(struct sps30_state *state, unsigned char cmd,
191 				const void *arg, size_t arg_size, void *rsp, size_t rsp_size)
192 {
193 	struct sps30_serial_priv *priv = state->priv;
194 	unsigned char buf[SPS30_SERIAL_MAX_BUF_SIZE];
195 	int ret, size;
196 
197 	size = sps30_serial_prep_frame(buf, cmd, arg, arg_size);
198 	ret = sps30_serial_xfer(state, buf, size);
199 	if (ret)
200 		return ret;
201 
202 	if (!sps30_serial_frame_valid(state, buf))
203 		return -EIO;
204 
205 	if (rsp) {
206 		rsp_size = min_t(size_t, priv->buf[SPS30_SERIAL_FRAME_MISO_LEN_OFFSET], rsp_size);
207 		memcpy(rsp, &priv->buf[SPS30_SERIAL_FRAME_MISO_DATA_OFFSET], rsp_size);
208 	}
209 
210 	return rsp_size;
211 }
212 
213 static int sps30_serial_receive_buf(struct serdev_device *serdev,
214 				    const unsigned char *buf, size_t size)
215 {
216 	struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev);
217 	struct sps30_serial_priv *priv;
218 	struct sps30_state *state;
219 	unsigned char byte;
220 	size_t i;
221 
222 	if (!indio_dev)
223 		return 0;
224 
225 	state = iio_priv(indio_dev);
226 	priv = state->priv;
227 
228 	/* just in case device put some unexpected data on the bus */
229 	if (priv->done)
230 		return size;
231 
232 	/* wait for the start of frame */
233 	if (!priv->num && size && buf[0] != SPS30_SERIAL_SOF_EOF)
234 		return 1;
235 
236 	if (priv->num + size >= ARRAY_SIZE(priv->buf))
237 		size = ARRAY_SIZE(priv->buf) - priv->num;
238 
239 	for (i = 0; i < size; i++) {
240 		byte = buf[i];
241 		/* remove stuffed bytes on-the-fly */
242 		if (byte == SPS30_SERIAL_ESCAPE_CHAR) {
243 			priv->escaped = true;
244 			continue;
245 		}
246 
247 		byte = sps30_serial_get_byte(priv->escaped, byte);
248 		if (priv->escaped && !byte)
249 			dev_warn(state->dev, "unrecognized escaped char (0x%02x)\n", byte);
250 
251 		priv->buf[priv->num++] = byte;
252 
253 		/* EOF received */
254 		if (!priv->escaped && byte == SPS30_SERIAL_SOF_EOF) {
255 			if (priv->num < SPS30_SERIAL_FRAME_MIN_SIZE)
256 				continue;
257 
258 			priv->done = true;
259 			complete(&priv->new_frame);
260 			i++;
261 			break;
262 		}
263 
264 		priv->escaped = false;
265 	}
266 
267 	return i;
268 }
269 
270 static const struct serdev_device_ops sps30_serial_device_ops = {
271 	.receive_buf = sps30_serial_receive_buf,
272 	.write_wakeup = serdev_device_write_wakeup,
273 };
274 
275 static int sps30_serial_start_meas(struct sps30_state *state)
276 {
277 	/* request BE IEEE754 formatted data */
278 	unsigned char buf[] = { 0x01, 0x03 };
279 
280 	return sps30_serial_command(state, SPS30_SERIAL_START_MEAS, buf, sizeof(buf), NULL, 0);
281 }
282 
283 static int sps30_serial_stop_meas(struct sps30_state *state)
284 {
285 	return sps30_serial_command(state, SPS30_SERIAL_STOP_MEAS, NULL, 0, NULL, 0);
286 }
287 
288 static int sps30_serial_reset(struct sps30_state *state)
289 {
290 	int ret;
291 
292 	ret = sps30_serial_command(state, SPS30_SERIAL_RESET, NULL, 0, NULL, 0);
293 	msleep(500);
294 
295 	return ret;
296 }
297 
298 static int sps30_serial_read_meas(struct sps30_state *state, __be32 *meas, size_t num)
299 {
300 	int ret;
301 
302 	/* measurements are ready within a second */
303 	if (msleep_interruptible(1000))
304 		return -EINTR;
305 
306 	ret = sps30_serial_command(state, SPS30_SERIAL_READ_MEAS, NULL, 0, meas, num * sizeof(num));
307 	if (ret < 0)
308 		return ret;
309 	/* if measurements aren't ready sensor returns empty frame */
310 	if (ret == SPS30_SERIAL_FRAME_MIN_SIZE)
311 		return -ETIMEDOUT;
312 	if (ret != num * sizeof(*meas))
313 		return -EIO;
314 
315 	return 0;
316 }
317 
318 static int sps30_serial_clean_fan(struct sps30_state *state)
319 {
320 	return sps30_serial_command(state, SPS30_SERIAL_CLEAN_FAN, NULL, 0, NULL, 0);
321 }
322 
323 static int sps30_serial_read_cleaning_period(struct sps30_state *state, __be32 *period)
324 {
325 	unsigned char buf[] = { 0x00 };
326 	int ret;
327 
328 	ret = sps30_serial_command(state, SPS30_SERIAL_PERIOD, buf, sizeof(buf),
329 				   period, sizeof(*period));
330 	if (ret < 0)
331 		return ret;
332 	if (ret != sizeof(*period))
333 		return -EIO;
334 
335 	return 0;
336 }
337 
338 static int sps30_serial_write_cleaning_period(struct sps30_state *state, __be32 period)
339 {
340 	unsigned char buf[5] = { 0x00 };
341 
342 	memcpy(buf + 1, &period, sizeof(period));
343 
344 	return sps30_serial_command(state, SPS30_SERIAL_PERIOD, buf, sizeof(buf), NULL, 0);
345 }
346 
347 static int sps30_serial_show_info(struct sps30_state *state)
348 {
349 	/*
350 	 * tell device do return serial number and add extra nul byte just in case
351 	 * serial number isn't a valid string
352 	 */
353 	unsigned char buf[32 + 1] = { 0x03 };
354 	struct device *dev = state->dev;
355 	int ret;
356 
357 	ret = sps30_serial_command(state, SPS30_SERIAL_DEV_INFO, buf, 1, buf, sizeof(buf) - 1);
358 	if (ret < 0)
359 		return ret;
360 	if (ret != sizeof(buf) - 1)
361 		return -EIO;
362 
363 	dev_info(dev, "serial number: %s\n", buf);
364 
365 	ret = sps30_serial_command(state, SPS30_SERIAL_READ_VERSION, NULL, 0, buf, sizeof(buf) - 1);
366 	if (ret < 0)
367 		return ret;
368 	if (ret < 2)
369 		return -EIO;
370 
371 	dev_info(dev, "fw version: %u.%u\n", buf[0], buf[1]);
372 
373 	return 0;
374 }
375 
376 static const struct sps30_ops sps30_serial_ops = {
377 	.start_meas = sps30_serial_start_meas,
378 	.stop_meas = sps30_serial_stop_meas,
379 	.read_meas = sps30_serial_read_meas,
380 	.reset = sps30_serial_reset,
381 	.clean_fan = sps30_serial_clean_fan,
382 	.read_cleaning_period = sps30_serial_read_cleaning_period,
383 	.write_cleaning_period = sps30_serial_write_cleaning_period,
384 	.show_info = sps30_serial_show_info,
385 };
386 
387 static int sps30_serial_probe(struct serdev_device *serdev)
388 {
389 	struct device *dev = &serdev->dev;
390 	struct sps30_serial_priv *priv;
391 	int ret;
392 
393 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
394 	if (!priv)
395 		return -ENOMEM;
396 
397 	init_completion(&priv->new_frame);
398 	serdev_device_set_client_ops(serdev, &sps30_serial_device_ops);
399 
400 	ret = devm_serdev_device_open(dev, serdev);
401 	if (ret)
402 		return ret;
403 
404 	serdev_device_set_baudrate(serdev, 115200);
405 	serdev_device_set_flow_control(serdev, false);
406 
407 	ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
408 	if (ret)
409 		return ret;
410 
411 	return sps30_probe(dev, SPS30_SERIAL_DEV_NAME, priv, &sps30_serial_ops);
412 }
413 
414 static const struct of_device_id sps30_serial_of_match[] = {
415 	{ .compatible = "sensirion,sps30" },
416 	{ }
417 };
418 MODULE_DEVICE_TABLE(of, sps30_serial_of_match);
419 
420 static struct serdev_device_driver sps30_serial_driver = {
421 	.driver = {
422 		.name = KBUILD_MODNAME,
423 		.of_match_table = sps30_serial_of_match,
424 	},
425 	.probe = sps30_serial_probe,
426 };
427 module_serdev_device_driver(sps30_serial_driver);
428 
429 MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
430 MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor serial driver");
431 MODULE_LICENSE("GPL v2");
432