root/drivers/iio/chemical/pms7003.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pms7003_do_cmd
  2. pms7003_get_pm
  3. pms7003_trigger_handler
  4. pms7003_read_raw
  5. pms7003_calc_checksum
  6. pms7003_frame_is_okay
  7. pms7003_receive_buf
  8. pms7003_stop
  9. pms7003_probe

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Plantower PMS7003 particulate matter sensor driver
   4  *
   5  * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
   6  */
   7 
   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>
  22 
  23 #define PMS7003_DRIVER_NAME "pms7003"
  24 
  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
  32 
  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
  37 
  38 enum {
  39         PM1,
  40         PM2P5,
  41         PM10,
  42 };
  43 
  44 enum 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  */
  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 },
  63 };
  64 
  65 struct pms7003_frame {
  66         u8 data[PMS7003_MAX_DATA_LENGTH];
  67         u16 expected_length;
  68         u16 length;
  69 };
  70 
  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 */
  77         struct {
  78                 u16 data[3]; /* PM1, PM2P5, PM10 */
  79                 s64 ts;
  80         } scan;
  81 };
  82 
  83 static 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 
 100 static 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 
 106 static 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));
 131 err:
 132         iio_trigger_notify_done(indio_dev->trig);
 133 
 134         return IRQ_HANDLED;
 135 }
 136 
 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)
 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 
 168 static 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 
 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),
 192 };
 193 
 194 static 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 
 206 static 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 
 214 static int pms7003_receive_buf(struct serdev_device *serdev,
 215                                const unsigned char *buf, 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         int 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 
 256 static const struct serdev_device_ops pms7003_serdev_ops = {
 257         .receive_buf = pms7003_receive_buf,
 258         .write_wakeup = serdev_device_write_wakeup,
 259 };
 260 
 261 static void pms7003_stop(void *data)
 262 {
 263         struct pms7003_state *state = data;
 264 
 265         pms7003_do_cmd(state, CMD_SLEEP);
 266 }
 267 
 268 static const unsigned long pms7003_scan_masks[] = { 0x07, 0x00 };
 269 
 270 static 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->dev.parent = &serdev->dev;
 284         indio_dev->info = &pms7003_info;
 285         indio_dev->name = PMS7003_DRIVER_NAME;
 286         indio_dev->channels = pms7003_channels,
 287         indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
 288         indio_dev->modes = INDIO_DIRECT_MODE;
 289         indio_dev->available_scan_masks = pms7003_scan_masks;
 290 
 291         mutex_init(&state->lock);
 292         init_completion(&state->frame_ready);
 293 
 294         serdev_device_set_client_ops(serdev, &pms7003_serdev_ops);
 295         ret = devm_serdev_device_open(&serdev->dev, serdev);
 296         if (ret)
 297                 return ret;
 298 
 299         serdev_device_set_baudrate(serdev, 9600);
 300         serdev_device_set_flow_control(serdev, false);
 301 
 302         ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
 303         if (ret)
 304                 return ret;
 305 
 306         ret = pms7003_do_cmd(state, CMD_WAKEUP);
 307         if (ret) {
 308                 dev_err(&serdev->dev, "failed to wakeup sensor\n");
 309                 return ret;
 310         }
 311 
 312         ret = pms7003_do_cmd(state, CMD_ENTER_PASSIVE_MODE);
 313         if (ret) {
 314                 dev_err(&serdev->dev, "failed to enter passive mode\n");
 315                 return ret;
 316         }
 317 
 318         ret = devm_add_action_or_reset(&serdev->dev, pms7003_stop, state);
 319         if (ret)
 320                 return ret;
 321 
 322         ret = devm_iio_triggered_buffer_setup(&serdev->dev, indio_dev, NULL,
 323                                               pms7003_trigger_handler, NULL);
 324         if (ret)
 325                 return ret;
 326 
 327         return devm_iio_device_register(&serdev->dev, indio_dev);
 328 }
 329 
 330 static const struct of_device_id pms7003_of_match[] = {
 331         { .compatible = "plantower,pms1003" },
 332         { .compatible = "plantower,pms3003" },
 333         { .compatible = "plantower,pms5003" },
 334         { .compatible = "plantower,pms6003" },
 335         { .compatible = "plantower,pms7003" },
 336         { .compatible = "plantower,pmsa003" },
 337         { }
 338 };
 339 MODULE_DEVICE_TABLE(of, pms7003_of_match);
 340 
 341 static struct serdev_device_driver pms7003_driver = {
 342         .driver = {
 343                 .name = PMS7003_DRIVER_NAME,
 344                 .of_match_table = pms7003_of_match,
 345         },
 346         .probe = pms7003_probe,
 347 };
 348 module_serdev_device_driver(pms7003_driver);
 349 
 350 MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
 351 MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver");
 352 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */