1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Plantower PMS7003 particulate matter sensor driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <asm/unaligned.h>
9*4882a593Smuzhiyun #include <linux/completion.h>
10*4882a593Smuzhiyun #include <linux/device.h>
11*4882a593Smuzhiyun #include <linux/errno.h>
12*4882a593Smuzhiyun #include <linux/iio/buffer.h>
13*4882a593Smuzhiyun #include <linux/iio/iio.h>
14*4882a593Smuzhiyun #include <linux/iio/trigger_consumer.h>
15*4882a593Smuzhiyun #include <linux/iio/triggered_buffer.h>
16*4882a593Smuzhiyun #include <linux/jiffies.h>
17*4882a593Smuzhiyun #include <linux/kernel.h>
18*4882a593Smuzhiyun #include <linux/mod_devicetable.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/mutex.h>
21*4882a593Smuzhiyun #include <linux/serdev.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define PMS7003_DRIVER_NAME "pms7003"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define PMS7003_MAGIC 0x424d
26*4882a593Smuzhiyun /* last 2 data bytes hold frame checksum */
27*4882a593Smuzhiyun #define PMS7003_MAX_DATA_LENGTH 28
28*4882a593Smuzhiyun #define PMS7003_CHECKSUM_LENGTH 2
29*4882a593Smuzhiyun #define PMS7003_PM10_OFFSET 10
30*4882a593Smuzhiyun #define PMS7003_PM2P5_OFFSET 8
31*4882a593Smuzhiyun #define PMS7003_PM1_OFFSET 6
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #define PMS7003_TIMEOUT msecs_to_jiffies(6000)
34*4882a593Smuzhiyun #define PMS7003_CMD_LENGTH 7
35*4882a593Smuzhiyun #define PMS7003_PM_MAX 1000
36*4882a593Smuzhiyun #define PMS7003_PM_MIN 0
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun enum {
39*4882a593Smuzhiyun PM1,
40*4882a593Smuzhiyun PM2P5,
41*4882a593Smuzhiyun PM10,
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun enum pms7003_cmd {
45*4882a593Smuzhiyun CMD_WAKEUP,
46*4882a593Smuzhiyun CMD_ENTER_PASSIVE_MODE,
47*4882a593Smuzhiyun CMD_READ_PASSIVE,
48*4882a593Smuzhiyun CMD_SLEEP,
49*4882a593Smuzhiyun };
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /*
52*4882a593Smuzhiyun * commands have following format:
53*4882a593Smuzhiyun *
54*4882a593Smuzhiyun * +------+------+-----+------+-----+-----------+-----------+
55*4882a593Smuzhiyun * | 0x42 | 0x4d | cmd | 0x00 | arg | cksum msb | cksum lsb |
56*4882a593Smuzhiyun * +------+------+-----+------+-----+-----------+-----------+
57*4882a593Smuzhiyun */
58*4882a593Smuzhiyun static const u8 pms7003_cmd_tbl[][PMS7003_CMD_LENGTH] = {
59*4882a593Smuzhiyun [CMD_WAKEUP] = { 0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74 },
60*4882a593Smuzhiyun [CMD_ENTER_PASSIVE_MODE] = { 0x42, 0x4d, 0xe1, 0x00, 0x00, 0x01, 0x70 },
61*4882a593Smuzhiyun [CMD_READ_PASSIVE] = { 0x42, 0x4d, 0xe2, 0x00, 0x00, 0x01, 0x71 },
62*4882a593Smuzhiyun [CMD_SLEEP] = { 0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73 },
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun struct pms7003_frame {
66*4882a593Smuzhiyun u8 data[PMS7003_MAX_DATA_LENGTH];
67*4882a593Smuzhiyun u16 expected_length;
68*4882a593Smuzhiyun u16 length;
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun struct pms7003_state {
72*4882a593Smuzhiyun struct serdev_device *serdev;
73*4882a593Smuzhiyun struct pms7003_frame frame;
74*4882a593Smuzhiyun struct completion frame_ready;
75*4882a593Smuzhiyun struct mutex lock; /* must be held whenever state gets touched */
76*4882a593Smuzhiyun /* Used to construct scan to push to the IIO buffer */
77*4882a593Smuzhiyun struct {
78*4882a593Smuzhiyun u16 data[3]; /* PM1, PM2P5, PM10 */
79*4882a593Smuzhiyun s64 ts;
80*4882a593Smuzhiyun } scan;
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun
pms7003_do_cmd(struct pms7003_state * state,enum pms7003_cmd cmd)83*4882a593Smuzhiyun static int pms7003_do_cmd(struct pms7003_state *state, enum pms7003_cmd cmd)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun int ret;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun ret = serdev_device_write(state->serdev, pms7003_cmd_tbl[cmd],
88*4882a593Smuzhiyun PMS7003_CMD_LENGTH, PMS7003_TIMEOUT);
89*4882a593Smuzhiyun if (ret < PMS7003_CMD_LENGTH)
90*4882a593Smuzhiyun return ret < 0 ? ret : -EIO;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun ret = wait_for_completion_interruptible_timeout(&state->frame_ready,
93*4882a593Smuzhiyun PMS7003_TIMEOUT);
94*4882a593Smuzhiyun if (!ret)
95*4882a593Smuzhiyun ret = -ETIMEDOUT;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun return ret < 0 ? ret : 0;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
pms7003_get_pm(const u8 * data)100*4882a593Smuzhiyun static u16 pms7003_get_pm(const u8 *data)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun return clamp_val(get_unaligned_be16(data),
103*4882a593Smuzhiyun PMS7003_PM_MIN, PMS7003_PM_MAX);
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
pms7003_trigger_handler(int irq,void * p)106*4882a593Smuzhiyun static irqreturn_t pms7003_trigger_handler(int irq, void *p)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun struct iio_poll_func *pf = p;
109*4882a593Smuzhiyun struct iio_dev *indio_dev = pf->indio_dev;
110*4882a593Smuzhiyun struct pms7003_state *state = iio_priv(indio_dev);
111*4882a593Smuzhiyun struct pms7003_frame *frame = &state->frame;
112*4882a593Smuzhiyun int ret;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun mutex_lock(&state->lock);
115*4882a593Smuzhiyun ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);
116*4882a593Smuzhiyun if (ret) {
117*4882a593Smuzhiyun mutex_unlock(&state->lock);
118*4882a593Smuzhiyun goto err;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun state->scan.data[PM1] =
122*4882a593Smuzhiyun pms7003_get_pm(frame->data + PMS7003_PM1_OFFSET);
123*4882a593Smuzhiyun state->scan.data[PM2P5] =
124*4882a593Smuzhiyun pms7003_get_pm(frame->data + PMS7003_PM2P5_OFFSET);
125*4882a593Smuzhiyun state->scan.data[PM10] =
126*4882a593Smuzhiyun pms7003_get_pm(frame->data + PMS7003_PM10_OFFSET);
127*4882a593Smuzhiyun mutex_unlock(&state->lock);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun iio_push_to_buffers_with_timestamp(indio_dev, &state->scan,
130*4882a593Smuzhiyun iio_get_time_ns(indio_dev));
131*4882a593Smuzhiyun err:
132*4882a593Smuzhiyun iio_trigger_notify_done(indio_dev->trig);
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun return IRQ_HANDLED;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
pms7003_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)137*4882a593Smuzhiyun static int pms7003_read_raw(struct iio_dev *indio_dev,
138*4882a593Smuzhiyun struct iio_chan_spec const *chan,
139*4882a593Smuzhiyun int *val, int *val2, long mask)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun struct pms7003_state *state = iio_priv(indio_dev);
142*4882a593Smuzhiyun struct pms7003_frame *frame = &state->frame;
143*4882a593Smuzhiyun int ret;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun switch (mask) {
146*4882a593Smuzhiyun case IIO_CHAN_INFO_PROCESSED:
147*4882a593Smuzhiyun switch (chan->type) {
148*4882a593Smuzhiyun case IIO_MASSCONCENTRATION:
149*4882a593Smuzhiyun mutex_lock(&state->lock);
150*4882a593Smuzhiyun ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);
151*4882a593Smuzhiyun if (ret) {
152*4882a593Smuzhiyun mutex_unlock(&state->lock);
153*4882a593Smuzhiyun return ret;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun *val = pms7003_get_pm(frame->data + chan->address);
157*4882a593Smuzhiyun mutex_unlock(&state->lock);
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun return IIO_VAL_INT;
160*4882a593Smuzhiyun default:
161*4882a593Smuzhiyun return -EINVAL;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun return -EINVAL;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun static const struct iio_info pms7003_info = {
169*4882a593Smuzhiyun .read_raw = pms7003_read_raw,
170*4882a593Smuzhiyun };
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun #define PMS7003_CHAN(_index, _mod, _addr) { \
173*4882a593Smuzhiyun .type = IIO_MASSCONCENTRATION, \
174*4882a593Smuzhiyun .modified = 1, \
175*4882a593Smuzhiyun .channel2 = IIO_MOD_ ## _mod, \
176*4882a593Smuzhiyun .address = _addr, \
177*4882a593Smuzhiyun .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
178*4882a593Smuzhiyun .scan_index = _index, \
179*4882a593Smuzhiyun .scan_type = { \
180*4882a593Smuzhiyun .sign = 'u', \
181*4882a593Smuzhiyun .realbits = 10, \
182*4882a593Smuzhiyun .storagebits = 16, \
183*4882a593Smuzhiyun .endianness = IIO_CPU, \
184*4882a593Smuzhiyun }, \
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun static const struct iio_chan_spec pms7003_channels[] = {
188*4882a593Smuzhiyun PMS7003_CHAN(0, PM1, PMS7003_PM1_OFFSET),
189*4882a593Smuzhiyun PMS7003_CHAN(1, PM2P5, PMS7003_PM2P5_OFFSET),
190*4882a593Smuzhiyun PMS7003_CHAN(2, PM10, PMS7003_PM10_OFFSET),
191*4882a593Smuzhiyun IIO_CHAN_SOFT_TIMESTAMP(3),
192*4882a593Smuzhiyun };
193*4882a593Smuzhiyun
pms7003_calc_checksum(struct pms7003_frame * frame)194*4882a593Smuzhiyun static u16 pms7003_calc_checksum(struct pms7003_frame *frame)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun u16 checksum = (PMS7003_MAGIC >> 8) + (u8)(PMS7003_MAGIC & 0xff) +
197*4882a593Smuzhiyun (frame->length >> 8) + (u8)frame->length;
198*4882a593Smuzhiyun int i;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun for (i = 0; i < frame->length - PMS7003_CHECKSUM_LENGTH; i++)
201*4882a593Smuzhiyun checksum += frame->data[i];
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun return checksum;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
pms7003_frame_is_okay(struct pms7003_frame * frame)206*4882a593Smuzhiyun static bool pms7003_frame_is_okay(struct pms7003_frame *frame)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun int offset = frame->length - PMS7003_CHECKSUM_LENGTH;
209*4882a593Smuzhiyun u16 checksum = get_unaligned_be16(frame->data + offset);
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun return checksum == pms7003_calc_checksum(frame);
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun
pms7003_receive_buf(struct serdev_device * serdev,const unsigned char * buf,size_t size)214*4882a593Smuzhiyun static int pms7003_receive_buf(struct serdev_device *serdev,
215*4882a593Smuzhiyun const unsigned char *buf, size_t size)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev);
218*4882a593Smuzhiyun struct pms7003_state *state = iio_priv(indio_dev);
219*4882a593Smuzhiyun struct pms7003_frame *frame = &state->frame;
220*4882a593Smuzhiyun int num;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun if (!frame->expected_length) {
223*4882a593Smuzhiyun u16 magic;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* wait for SOF and data length */
226*4882a593Smuzhiyun if (size < 4)
227*4882a593Smuzhiyun return 0;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun magic = get_unaligned_be16(buf);
230*4882a593Smuzhiyun if (magic != PMS7003_MAGIC)
231*4882a593Smuzhiyun return 2;
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun num = get_unaligned_be16(buf + 2);
234*4882a593Smuzhiyun if (num <= PMS7003_MAX_DATA_LENGTH) {
235*4882a593Smuzhiyun frame->expected_length = num;
236*4882a593Smuzhiyun frame->length = 0;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun return 4;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun num = min(size, (size_t)(frame->expected_length - frame->length));
243*4882a593Smuzhiyun memcpy(frame->data + frame->length, buf, num);
244*4882a593Smuzhiyun frame->length += num;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun if (frame->length == frame->expected_length) {
247*4882a593Smuzhiyun if (pms7003_frame_is_okay(frame))
248*4882a593Smuzhiyun complete(&state->frame_ready);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun frame->expected_length = 0;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun return num;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun static const struct serdev_device_ops pms7003_serdev_ops = {
257*4882a593Smuzhiyun .receive_buf = pms7003_receive_buf,
258*4882a593Smuzhiyun .write_wakeup = serdev_device_write_wakeup,
259*4882a593Smuzhiyun };
260*4882a593Smuzhiyun
pms7003_stop(void * data)261*4882a593Smuzhiyun static void pms7003_stop(void *data)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun struct pms7003_state *state = data;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun pms7003_do_cmd(state, CMD_SLEEP);
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun static const unsigned long pms7003_scan_masks[] = { 0x07, 0x00 };
269*4882a593Smuzhiyun
pms7003_probe(struct serdev_device * serdev)270*4882a593Smuzhiyun static int pms7003_probe(struct serdev_device *serdev)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun struct pms7003_state *state;
273*4882a593Smuzhiyun struct iio_dev *indio_dev;
274*4882a593Smuzhiyun int ret;
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun indio_dev = devm_iio_device_alloc(&serdev->dev, sizeof(*state));
277*4882a593Smuzhiyun if (!indio_dev)
278*4882a593Smuzhiyun return -ENOMEM;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun state = iio_priv(indio_dev);
281*4882a593Smuzhiyun serdev_device_set_drvdata(serdev, indio_dev);
282*4882a593Smuzhiyun state->serdev = serdev;
283*4882a593Smuzhiyun indio_dev->info = &pms7003_info;
284*4882a593Smuzhiyun indio_dev->name = PMS7003_DRIVER_NAME;
285*4882a593Smuzhiyun indio_dev->channels = pms7003_channels,
286*4882a593Smuzhiyun indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
287*4882a593Smuzhiyun indio_dev->modes = INDIO_DIRECT_MODE;
288*4882a593Smuzhiyun indio_dev->available_scan_masks = pms7003_scan_masks;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun mutex_init(&state->lock);
291*4882a593Smuzhiyun init_completion(&state->frame_ready);
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun serdev_device_set_client_ops(serdev, &pms7003_serdev_ops);
294*4882a593Smuzhiyun ret = devm_serdev_device_open(&serdev->dev, serdev);
295*4882a593Smuzhiyun if (ret)
296*4882a593Smuzhiyun return ret;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun serdev_device_set_baudrate(serdev, 9600);
299*4882a593Smuzhiyun serdev_device_set_flow_control(serdev, false);
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
302*4882a593Smuzhiyun if (ret)
303*4882a593Smuzhiyun return ret;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun ret = pms7003_do_cmd(state, CMD_WAKEUP);
306*4882a593Smuzhiyun if (ret) {
307*4882a593Smuzhiyun dev_err(&serdev->dev, "failed to wakeup sensor\n");
308*4882a593Smuzhiyun return ret;
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun ret = pms7003_do_cmd(state, CMD_ENTER_PASSIVE_MODE);
312*4882a593Smuzhiyun if (ret) {
313*4882a593Smuzhiyun dev_err(&serdev->dev, "failed to enter passive mode\n");
314*4882a593Smuzhiyun return ret;
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun ret = devm_add_action_or_reset(&serdev->dev, pms7003_stop, state);
318*4882a593Smuzhiyun if (ret)
319*4882a593Smuzhiyun return ret;
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun ret = devm_iio_triggered_buffer_setup(&serdev->dev, indio_dev, NULL,
322*4882a593Smuzhiyun pms7003_trigger_handler, NULL);
323*4882a593Smuzhiyun if (ret)
324*4882a593Smuzhiyun return ret;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun return devm_iio_device_register(&serdev->dev, indio_dev);
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun static const struct of_device_id pms7003_of_match[] = {
330*4882a593Smuzhiyun { .compatible = "plantower,pms1003" },
331*4882a593Smuzhiyun { .compatible = "plantower,pms3003" },
332*4882a593Smuzhiyun { .compatible = "plantower,pms5003" },
333*4882a593Smuzhiyun { .compatible = "plantower,pms6003" },
334*4882a593Smuzhiyun { .compatible = "plantower,pms7003" },
335*4882a593Smuzhiyun { .compatible = "plantower,pmsa003" },
336*4882a593Smuzhiyun { }
337*4882a593Smuzhiyun };
338*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, pms7003_of_match);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun static struct serdev_device_driver pms7003_driver = {
341*4882a593Smuzhiyun .driver = {
342*4882a593Smuzhiyun .name = PMS7003_DRIVER_NAME,
343*4882a593Smuzhiyun .of_match_table = pms7003_of_match,
344*4882a593Smuzhiyun },
345*4882a593Smuzhiyun .probe = pms7003_probe,
346*4882a593Smuzhiyun };
347*4882a593Smuzhiyun module_serdev_device_driver(pms7003_driver);
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
350*4882a593Smuzhiyun MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver");
351*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
352