1 // SPDX-License-Identifier: GPL-2.0
3 * Plantower PMS7003 particulate matter sensor driver
5 * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
8 #include <asm/unaligned.h>
9 #include <linux/completion.h>
10 #include <linux/device.h>
11 #include <linux/errno.h>
12 #include <linux/iio/buffer.h>
13 #include <linux/iio/iio.h>
14 #include <linux/iio/trigger_consumer.h>
15 #include <linux/iio/triggered_buffer.h>
16 #include <linux/jiffies.h>
17 #include <linux/kernel.h>
18 #include <linux/mod_devicetable.h>
19 #include <linux/module.h>
20 #include <linux/mutex.h>
21 #include <linux/serdev.h>
23 #define PMS7003_DRIVER_NAME "pms7003"
25 #define PMS7003_MAGIC 0x424d
26 /* last 2 data bytes hold frame checksum */
27 #define PMS7003_MAX_DATA_LENGTH 28
28 #define PMS7003_CHECKSUM_LENGTH 2
29 #define PMS7003_PM10_OFFSET 10
30 #define PMS7003_PM2P5_OFFSET 8
31 #define PMS7003_PM1_OFFSET 6
33 #define PMS7003_TIMEOUT msecs_to_jiffies(6000)
34 #define PMS7003_CMD_LENGTH 7
35 #define PMS7003_PM_MAX 1000
36 #define PMS7003_PM_MIN 0
46 CMD_ENTER_PASSIVE_MODE
,
52 * commands have following format:
54 * +------+------+-----+------+-----+-----------+-----------+
55 * | 0x42 | 0x4d | cmd | 0x00 | arg | cksum msb | cksum lsb |
56 * +------+------+-----+------+-----+-----------+-----------+
58 static const u8 pms7003_cmd_tbl
[][PMS7003_CMD_LENGTH
] = {
59 [CMD_WAKEUP
] = { 0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74 },
60 [CMD_ENTER_PASSIVE_MODE
] = { 0x42, 0x4d, 0xe1, 0x00, 0x00, 0x01, 0x70 },
61 [CMD_READ_PASSIVE
] = { 0x42, 0x4d, 0xe2, 0x00, 0x00, 0x01, 0x71 },
62 [CMD_SLEEP
] = { 0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73 },
65 struct pms7003_frame
{
66 u8 data
[PMS7003_MAX_DATA_LENGTH
];
71 struct pms7003_state
{
72 struct serdev_device
*serdev
;
73 struct pms7003_frame frame
;
74 struct completion frame_ready
;
75 struct mutex lock
; /* must be held whenever state gets touched */
76 /* Used to construct scan to push to the IIO buffer */
78 u16 data
[3]; /* PM1, PM2P5, PM10 */
83 static int pms7003_do_cmd(struct pms7003_state
*state
, enum pms7003_cmd cmd
)
87 ret
= serdev_device_write(state
->serdev
, pms7003_cmd_tbl
[cmd
],
88 PMS7003_CMD_LENGTH
, PMS7003_TIMEOUT
);
89 if (ret
< PMS7003_CMD_LENGTH
)
90 return ret
< 0 ? ret
: -EIO
;
92 ret
= wait_for_completion_interruptible_timeout(&state
->frame_ready
,
97 return ret
< 0 ? ret
: 0;
100 static u16
pms7003_get_pm(const u8
*data
)
102 return clamp_val(get_unaligned_be16(data
),
103 PMS7003_PM_MIN
, PMS7003_PM_MAX
);
106 static irqreturn_t
pms7003_trigger_handler(int irq
, void *p
)
108 struct iio_poll_func
*pf
= p
;
109 struct iio_dev
*indio_dev
= pf
->indio_dev
;
110 struct pms7003_state
*state
= iio_priv(indio_dev
);
111 struct pms7003_frame
*frame
= &state
->frame
;
114 mutex_lock(&state
->lock
);
115 ret
= pms7003_do_cmd(state
, CMD_READ_PASSIVE
);
117 mutex_unlock(&state
->lock
);
121 state
->scan
.data
[PM1
] =
122 pms7003_get_pm(frame
->data
+ PMS7003_PM1_OFFSET
);
123 state
->scan
.data
[PM2P5
] =
124 pms7003_get_pm(frame
->data
+ PMS7003_PM2P5_OFFSET
);
125 state
->scan
.data
[PM10
] =
126 pms7003_get_pm(frame
->data
+ PMS7003_PM10_OFFSET
);
127 mutex_unlock(&state
->lock
);
129 iio_push_to_buffers_with_timestamp(indio_dev
, &state
->scan
,
130 iio_get_time_ns(indio_dev
));
132 iio_trigger_notify_done(indio_dev
->trig
);
137 static int pms7003_read_raw(struct iio_dev
*indio_dev
,
138 struct iio_chan_spec
const *chan
,
139 int *val
, int *val2
, long mask
)
141 struct pms7003_state
*state
= iio_priv(indio_dev
);
142 struct pms7003_frame
*frame
= &state
->frame
;
146 case IIO_CHAN_INFO_PROCESSED
:
147 switch (chan
->type
) {
148 case IIO_MASSCONCENTRATION
:
149 mutex_lock(&state
->lock
);
150 ret
= pms7003_do_cmd(state
, CMD_READ_PASSIVE
);
152 mutex_unlock(&state
->lock
);
156 *val
= pms7003_get_pm(frame
->data
+ chan
->address
);
157 mutex_unlock(&state
->lock
);
168 static const struct iio_info pms7003_info
= {
169 .read_raw
= pms7003_read_raw
,
172 #define PMS7003_CHAN(_index, _mod, _addr) { \
173 .type = IIO_MASSCONCENTRATION, \
175 .channel2 = IIO_MOD_ ## _mod, \
177 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
178 .scan_index = _index, \
183 .endianness = IIO_CPU, \
187 static const struct iio_chan_spec pms7003_channels
[] = {
188 PMS7003_CHAN(0, PM1
, PMS7003_PM1_OFFSET
),
189 PMS7003_CHAN(1, PM2P5
, PMS7003_PM2P5_OFFSET
),
190 PMS7003_CHAN(2, PM10
, PMS7003_PM10_OFFSET
),
191 IIO_CHAN_SOFT_TIMESTAMP(3),
194 static u16
pms7003_calc_checksum(struct pms7003_frame
*frame
)
196 u16 checksum
= (PMS7003_MAGIC
>> 8) + (u8
)(PMS7003_MAGIC
& 0xff) +
197 (frame
->length
>> 8) + (u8
)frame
->length
;
200 for (i
= 0; i
< frame
->length
- PMS7003_CHECKSUM_LENGTH
; i
++)
201 checksum
+= frame
->data
[i
];
206 static bool pms7003_frame_is_okay(struct pms7003_frame
*frame
)
208 int offset
= frame
->length
- PMS7003_CHECKSUM_LENGTH
;
209 u16 checksum
= get_unaligned_be16(frame
->data
+ offset
);
211 return checksum
== pms7003_calc_checksum(frame
);
214 static int pms7003_receive_buf(struct serdev_device
*serdev
,
215 const unsigned char *buf
, size_t size
)
217 struct iio_dev
*indio_dev
= serdev_device_get_drvdata(serdev
);
218 struct pms7003_state
*state
= iio_priv(indio_dev
);
219 struct pms7003_frame
*frame
= &state
->frame
;
222 if (!frame
->expected_length
) {
225 /* wait for SOF and data length */
229 magic
= get_unaligned_be16(buf
);
230 if (magic
!= PMS7003_MAGIC
)
233 num
= get_unaligned_be16(buf
+ 2);
234 if (num
<= PMS7003_MAX_DATA_LENGTH
) {
235 frame
->expected_length
= num
;
242 num
= min(size
, (size_t)(frame
->expected_length
- frame
->length
));
243 memcpy(frame
->data
+ frame
->length
, buf
, num
);
244 frame
->length
+= num
;
246 if (frame
->length
== frame
->expected_length
) {
247 if (pms7003_frame_is_okay(frame
))
248 complete(&state
->frame_ready
);
250 frame
->expected_length
= 0;
256 static const struct serdev_device_ops pms7003_serdev_ops
= {
257 .receive_buf
= pms7003_receive_buf
,
258 .write_wakeup
= serdev_device_write_wakeup
,
261 static void pms7003_stop(void *data
)
263 struct pms7003_state
*state
= data
;
265 pms7003_do_cmd(state
, CMD_SLEEP
);
268 static const unsigned long pms7003_scan_masks
[] = { 0x07, 0x00 };
270 static int pms7003_probe(struct serdev_device
*serdev
)
272 struct pms7003_state
*state
;
273 struct iio_dev
*indio_dev
;
276 indio_dev
= devm_iio_device_alloc(&serdev
->dev
, sizeof(*state
));
280 state
= iio_priv(indio_dev
);
281 serdev_device_set_drvdata(serdev
, indio_dev
);
282 state
->serdev
= serdev
;
283 indio_dev
->info
= &pms7003_info
;
284 indio_dev
->name
= PMS7003_DRIVER_NAME
;
285 indio_dev
->channels
= pms7003_channels
,
286 indio_dev
->num_channels
= ARRAY_SIZE(pms7003_channels
);
287 indio_dev
->modes
= INDIO_DIRECT_MODE
;
288 indio_dev
->available_scan_masks
= pms7003_scan_masks
;
290 mutex_init(&state
->lock
);
291 init_completion(&state
->frame_ready
);
293 serdev_device_set_client_ops(serdev
, &pms7003_serdev_ops
);
294 ret
= devm_serdev_device_open(&serdev
->dev
, serdev
);
298 serdev_device_set_baudrate(serdev
, 9600);
299 serdev_device_set_flow_control(serdev
, false);
301 ret
= serdev_device_set_parity(serdev
, SERDEV_PARITY_NONE
);
305 ret
= pms7003_do_cmd(state
, CMD_WAKEUP
);
307 dev_err(&serdev
->dev
, "failed to wakeup sensor\n");
311 ret
= pms7003_do_cmd(state
, CMD_ENTER_PASSIVE_MODE
);
313 dev_err(&serdev
->dev
, "failed to enter passive mode\n");
317 ret
= devm_add_action_or_reset(&serdev
->dev
, pms7003_stop
, state
);
321 ret
= devm_iio_triggered_buffer_setup(&serdev
->dev
, indio_dev
, NULL
,
322 pms7003_trigger_handler
, NULL
);
326 return devm_iio_device_register(&serdev
->dev
, indio_dev
);
329 static const struct of_device_id pms7003_of_match
[] = {
330 { .compatible
= "plantower,pms1003" },
331 { .compatible
= "plantower,pms3003" },
332 { .compatible
= "plantower,pms5003" },
333 { .compatible
= "plantower,pms6003" },
334 { .compatible
= "plantower,pms7003" },
335 { .compatible
= "plantower,pmsa003" },
338 MODULE_DEVICE_TABLE(of
, pms7003_of_match
);
340 static struct serdev_device_driver pms7003_driver
= {
342 .name
= PMS7003_DRIVER_NAME
,
343 .of_match_table
= pms7003_of_match
,
345 .probe
= pms7003_probe
,
347 module_serdev_device_driver(pms7003_driver
);
349 MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
350 MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver");
351 MODULE_LICENSE("GPL v2");