352 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Plantower PMS7003 particulate matter sensor driver4 *5 * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>6 */7 8#include <linux/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>22 23#define PMS7003_DRIVER_NAME "pms7003"24 25#define PMS7003_MAGIC 0x424d26/* last 2 data bytes hold frame checksum */27#define PMS7003_MAX_DATA_LENGTH 2828#define PMS7003_CHECKSUM_LENGTH 229#define PMS7003_PM10_OFFSET 1030#define PMS7003_PM2P5_OFFSET 831#define PMS7003_PM1_OFFSET 632 33#define PMS7003_TIMEOUT msecs_to_jiffies(6000)34#define PMS7003_CMD_LENGTH 735#define PMS7003_PM_MAX 100036#define PMS7003_PM_MIN 037 38enum {39 PM1,40 PM2P5,41 PM10,42};43 44enum pms7003_cmd {45 CMD_WAKEUP,46 CMD_ENTER_PASSIVE_MODE,47 CMD_READ_PASSIVE,48 CMD_SLEEP,49};50 51/*52 * commands have following format:53 *54 * +------+------+-----+------+-----+-----------+-----------+55 * | 0x42 | 0x4d | cmd | 0x00 | arg | cksum msb | cksum lsb |56 * +------+------+-----+------+-----+-----------+-----------+57 */58static 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 },63};64 65struct pms7003_frame {66 u8 data[PMS7003_MAX_DATA_LENGTH];67 u16 expected_length;68 u16 length;69};70 71struct 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 */77 struct {78 u16 data[3]; /* PM1, PM2P5, PM10 */79 s64 ts;80 } scan;81};82 83static int pms7003_do_cmd(struct pms7003_state *state, enum pms7003_cmd cmd)84{85 int ret;86 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;91 92 ret = wait_for_completion_interruptible_timeout(&state->frame_ready,93 PMS7003_TIMEOUT);94 if (!ret)95 ret = -ETIMEDOUT;96 97 return ret < 0 ? ret : 0;98}99 100static u16 pms7003_get_pm(const u8 *data)101{102 return clamp_val(get_unaligned_be16(data),103 PMS7003_PM_MIN, PMS7003_PM_MAX);104}105 106static irqreturn_t pms7003_trigger_handler(int irq, void *p)107{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;112 int ret;113 114 mutex_lock(&state->lock);115 ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);116 if (ret) {117 mutex_unlock(&state->lock);118 goto err;119 }120 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);128 129 iio_push_to_buffers_with_timestamp(indio_dev, &state->scan,130 iio_get_time_ns(indio_dev));131err:132 iio_trigger_notify_done(indio_dev->trig);133 134 return IRQ_HANDLED;135}136 137static int pms7003_read_raw(struct iio_dev *indio_dev,138 struct iio_chan_spec const *chan,139 int *val, int *val2, long mask)140{141 struct pms7003_state *state = iio_priv(indio_dev);142 struct pms7003_frame *frame = &state->frame;143 int ret;144 145 switch (mask) {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);151 if (ret) {152 mutex_unlock(&state->lock);153 return ret;154 }155 156 *val = pms7003_get_pm(frame->data + chan->address);157 mutex_unlock(&state->lock);158 159 return IIO_VAL_INT;160 default:161 return -EINVAL;162 }163 }164 165 return -EINVAL;166}167 168static const struct iio_info pms7003_info = {169 .read_raw = pms7003_read_raw,170};171 172#define PMS7003_CHAN(_index, _mod, _addr) { \173 .type = IIO_MASSCONCENTRATION, \174 .modified = 1, \175 .channel2 = IIO_MOD_ ## _mod, \176 .address = _addr, \177 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \178 .scan_index = _index, \179 .scan_type = { \180 .sign = 'u', \181 .realbits = 10, \182 .storagebits = 16, \183 .endianness = IIO_CPU, \184 }, \185}186 187static 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),192};193 194static u16 pms7003_calc_checksum(struct pms7003_frame *frame)195{196 u16 checksum = (PMS7003_MAGIC >> 8) + (u8)(PMS7003_MAGIC & 0xff) +197 (frame->length >> 8) + (u8)frame->length;198 int i;199 200 for (i = 0; i < frame->length - PMS7003_CHECKSUM_LENGTH; i++)201 checksum += frame->data[i];202 203 return checksum;204}205 206static bool pms7003_frame_is_okay(struct pms7003_frame *frame)207{208 int offset = frame->length - PMS7003_CHECKSUM_LENGTH;209 u16 checksum = get_unaligned_be16(frame->data + offset);210 211 return checksum == pms7003_calc_checksum(frame);212}213 214static size_t pms7003_receive_buf(struct serdev_device *serdev, const u8 *buf,215 size_t size)216{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;220 size_t num;221 222 if (!frame->expected_length) {223 u16 magic;224 225 /* wait for SOF and data length */226 if (size < 4)227 return 0;228 229 magic = get_unaligned_be16(buf);230 if (magic != PMS7003_MAGIC)231 return 2;232 233 num = get_unaligned_be16(buf + 2);234 if (num <= PMS7003_MAX_DATA_LENGTH) {235 frame->expected_length = num;236 frame->length = 0;237 }238 239 return 4;240 }241 242 num = min(size, (size_t)(frame->expected_length - frame->length));243 memcpy(frame->data + frame->length, buf, num);244 frame->length += num;245 246 if (frame->length == frame->expected_length) {247 if (pms7003_frame_is_okay(frame))248 complete(&state->frame_ready);249 250 frame->expected_length = 0;251 }252 253 return num;254}255 256static const struct serdev_device_ops pms7003_serdev_ops = {257 .receive_buf = pms7003_receive_buf,258 .write_wakeup = serdev_device_write_wakeup,259};260 261static void pms7003_stop(void *data)262{263 struct pms7003_state *state = data;264 265 pms7003_do_cmd(state, CMD_SLEEP);266}267 268static const unsigned long pms7003_scan_masks[] = { 0x07, 0x00 };269 270static int pms7003_probe(struct serdev_device *serdev)271{272 struct pms7003_state *state;273 struct iio_dev *indio_dev;274 int ret;275 276 indio_dev = devm_iio_device_alloc(&serdev->dev, sizeof(*state));277 if (!indio_dev)278 return -ENOMEM;279 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;289 290 mutex_init(&state->lock);291 init_completion(&state->frame_ready);292 293 serdev_device_set_client_ops(serdev, &pms7003_serdev_ops);294 ret = devm_serdev_device_open(&serdev->dev, serdev);295 if (ret)296 return ret;297 298 serdev_device_set_baudrate(serdev, 9600);299 serdev_device_set_flow_control(serdev, false);300 301 ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);302 if (ret)303 return ret;304 305 ret = pms7003_do_cmd(state, CMD_WAKEUP);306 if (ret) {307 dev_err(&serdev->dev, "failed to wakeup sensor\n");308 return ret;309 }310 311 ret = pms7003_do_cmd(state, CMD_ENTER_PASSIVE_MODE);312 if (ret) {313 dev_err(&serdev->dev, "failed to enter passive mode\n");314 return ret;315 }316 317 ret = devm_add_action_or_reset(&serdev->dev, pms7003_stop, state);318 if (ret)319 return ret;320 321 ret = devm_iio_triggered_buffer_setup(&serdev->dev, indio_dev, NULL,322 pms7003_trigger_handler, NULL);323 if (ret)324 return ret;325 326 return devm_iio_device_register(&serdev->dev, indio_dev);327}328 329static 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" },336 { }337};338MODULE_DEVICE_TABLE(of, pms7003_of_match);339 340static struct serdev_device_driver pms7003_driver = {341 .driver = {342 .name = PMS7003_DRIVER_NAME,343 .of_match_table = pms7003_of_match,344 },345 .probe = pms7003_probe,346};347module_serdev_device_driver(pms7003_driver);348 349MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");350MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver");351MODULE_LICENSE("GPL v2");352