1232e0f6dSTomasz Duszynski // SPDX-License-Identifier: GPL-2.0 2232e0f6dSTomasz Duszynski /* 3232e0f6dSTomasz Duszynski * Sensirion SPS30 particulate matter sensor driver 4232e0f6dSTomasz Duszynski * 5232e0f6dSTomasz Duszynski * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> 6232e0f6dSTomasz Duszynski */ 7232e0f6dSTomasz Duszynski 8232e0f6dSTomasz Duszynski #include <linux/crc8.h> 9232e0f6dSTomasz Duszynski #include <linux/delay.h> 10232e0f6dSTomasz Duszynski #include <linux/i2c.h> 11232e0f6dSTomasz Duszynski #include <linux/iio/buffer.h> 12232e0f6dSTomasz Duszynski #include <linux/iio/iio.h> 13232e0f6dSTomasz Duszynski #include <linux/iio/sysfs.h> 14232e0f6dSTomasz Duszynski #include <linux/iio/trigger_consumer.h> 15232e0f6dSTomasz Duszynski #include <linux/iio/triggered_buffer.h> 1662129a08STomasz Duszynski #include <linux/kernel.h> 17232e0f6dSTomasz Duszynski #include <linux/module.h> 18232e0f6dSTomasz Duszynski 19*8f3f1308STomasz Duszynski #include "sps30.h" 20*8f3f1308STomasz Duszynski 21232e0f6dSTomasz Duszynski /* sensor measures reliably up to 3000 ug / m3 */ 22232e0f6dSTomasz Duszynski #define SPS30_MAX_PM 3000 2362129a08STomasz Duszynski /* minimum and maximum self cleaning periods in seconds */ 2462129a08STomasz Duszynski #define SPS30_AUTO_CLEANING_PERIOD_MIN 0 2562129a08STomasz Duszynski #define SPS30_AUTO_CLEANING_PERIOD_MAX 604800 26232e0f6dSTomasz Duszynski 27232e0f6dSTomasz Duszynski enum { 28232e0f6dSTomasz Duszynski PM1, 29232e0f6dSTomasz Duszynski PM2P5, 30232e0f6dSTomasz Duszynski PM4, 31232e0f6dSTomasz Duszynski PM10, 32232e0f6dSTomasz Duszynski }; 33232e0f6dSTomasz Duszynski 3462129a08STomasz Duszynski enum { 3562129a08STomasz Duszynski RESET, 3662129a08STomasz Duszynski MEASURING, 3762129a08STomasz Duszynski }; 3862129a08STomasz Duszynski 39*8f3f1308STomasz Duszynski static s32 sps30_float_to_int_clamped(__be32 *fp) 40232e0f6dSTomasz Duszynski { 41*8f3f1308STomasz Duszynski int val = be32_to_cpup(fp); 42232e0f6dSTomasz Duszynski int mantissa = val & GENMASK(22, 0); 43232e0f6dSTomasz Duszynski /* this is fine since passed float is always non-negative */ 44232e0f6dSTomasz Duszynski int exp = val >> 23; 45232e0f6dSTomasz Duszynski int fraction, shift; 46232e0f6dSTomasz Duszynski 47232e0f6dSTomasz Duszynski /* special case 0 */ 48232e0f6dSTomasz Duszynski if (!exp && !mantissa) 49232e0f6dSTomasz Duszynski return 0; 50232e0f6dSTomasz Duszynski 51232e0f6dSTomasz Duszynski exp -= 127; 52232e0f6dSTomasz Duszynski if (exp < 0) { 53232e0f6dSTomasz Duszynski /* return values ranging from 1 to 99 */ 54232e0f6dSTomasz Duszynski return ((((1 << 23) + mantissa) * 100) >> 23) >> (-exp); 55232e0f6dSTomasz Duszynski } 56232e0f6dSTomasz Duszynski 57232e0f6dSTomasz Duszynski /* return values ranging from 100 to 300000 */ 58232e0f6dSTomasz Duszynski shift = 23 - exp; 59232e0f6dSTomasz Duszynski val = (1 << exp) + (mantissa >> shift); 60232e0f6dSTomasz Duszynski if (val >= SPS30_MAX_PM) 61232e0f6dSTomasz Duszynski return SPS30_MAX_PM * 100; 62232e0f6dSTomasz Duszynski 63232e0f6dSTomasz Duszynski fraction = mantissa & GENMASK(shift - 1, 0); 64232e0f6dSTomasz Duszynski 65232e0f6dSTomasz Duszynski return val * 100 + ((fraction * 100) >> shift); 66232e0f6dSTomasz Duszynski } 67232e0f6dSTomasz Duszynski 68232e0f6dSTomasz Duszynski static int sps30_do_meas(struct sps30_state *state, s32 *data, int size) 69232e0f6dSTomasz Duszynski { 70*8f3f1308STomasz Duszynski int i, ret; 71232e0f6dSTomasz Duszynski 7262129a08STomasz Duszynski if (state->state == RESET) { 73*8f3f1308STomasz Duszynski ret = state->ops->start_meas(state); 7462129a08STomasz Duszynski if (ret) 7562129a08STomasz Duszynski return ret; 7662129a08STomasz Duszynski 7762129a08STomasz Duszynski state->state = MEASURING; 7862129a08STomasz Duszynski } 7962129a08STomasz Duszynski 80*8f3f1308STomasz Duszynski ret = state->ops->read_meas(state, (__be32 *)data, size); 81232e0f6dSTomasz Duszynski if (ret) 82232e0f6dSTomasz Duszynski return ret; 83232e0f6dSTomasz Duszynski 84232e0f6dSTomasz Duszynski for (i = 0; i < size; i++) 85*8f3f1308STomasz Duszynski data[i] = sps30_float_to_int_clamped((__be32 *)&data[i]); 86*8f3f1308STomasz Duszynski 87*8f3f1308STomasz Duszynski return 0; 88*8f3f1308STomasz Duszynski } 89*8f3f1308STomasz Duszynski 90*8f3f1308STomasz Duszynski static int sps30_do_reset(struct sps30_state *state) 91*8f3f1308STomasz Duszynski { 92*8f3f1308STomasz Duszynski int ret; 93*8f3f1308STomasz Duszynski 94*8f3f1308STomasz Duszynski ret = state->ops->reset(state); 95*8f3f1308STomasz Duszynski if (ret) 96*8f3f1308STomasz Duszynski return ret; 97*8f3f1308STomasz Duszynski 98*8f3f1308STomasz Duszynski state->state = RESET; 99232e0f6dSTomasz Duszynski 100232e0f6dSTomasz Duszynski return 0; 101232e0f6dSTomasz Duszynski } 102232e0f6dSTomasz Duszynski 103232e0f6dSTomasz Duszynski static irqreturn_t sps30_trigger_handler(int irq, void *p) 104232e0f6dSTomasz Duszynski { 105232e0f6dSTomasz Duszynski struct iio_poll_func *pf = p; 106232e0f6dSTomasz Duszynski struct iio_dev *indio_dev = pf->indio_dev; 107232e0f6dSTomasz Duszynski struct sps30_state *state = iio_priv(indio_dev); 108232e0f6dSTomasz Duszynski int ret; 109a5bf6fddSJonathan Cameron struct { 110a5bf6fddSJonathan Cameron s32 data[4]; /* PM1, PM2P5, PM4, PM10 */ 111a5bf6fddSJonathan Cameron s64 ts; 112a5bf6fddSJonathan Cameron } scan; 113232e0f6dSTomasz Duszynski 114232e0f6dSTomasz Duszynski mutex_lock(&state->lock); 115a5bf6fddSJonathan Cameron ret = sps30_do_meas(state, scan.data, ARRAY_SIZE(scan.data)); 116232e0f6dSTomasz Duszynski mutex_unlock(&state->lock); 117232e0f6dSTomasz Duszynski if (ret) 118232e0f6dSTomasz Duszynski goto err; 119232e0f6dSTomasz Duszynski 120a5bf6fddSJonathan Cameron iio_push_to_buffers_with_timestamp(indio_dev, &scan, 121232e0f6dSTomasz Duszynski iio_get_time_ns(indio_dev)); 122232e0f6dSTomasz Duszynski err: 123232e0f6dSTomasz Duszynski iio_trigger_notify_done(indio_dev->trig); 124232e0f6dSTomasz Duszynski 125232e0f6dSTomasz Duszynski return IRQ_HANDLED; 126232e0f6dSTomasz Duszynski } 127232e0f6dSTomasz Duszynski 128232e0f6dSTomasz Duszynski static int sps30_read_raw(struct iio_dev *indio_dev, 129232e0f6dSTomasz Duszynski struct iio_chan_spec const *chan, 130232e0f6dSTomasz Duszynski int *val, int *val2, long mask) 131232e0f6dSTomasz Duszynski { 132232e0f6dSTomasz Duszynski struct sps30_state *state = iio_priv(indio_dev); 133232e0f6dSTomasz Duszynski int data[4], ret = -EINVAL; 134232e0f6dSTomasz Duszynski 135232e0f6dSTomasz Duszynski switch (mask) { 136232e0f6dSTomasz Duszynski case IIO_CHAN_INFO_PROCESSED: 137232e0f6dSTomasz Duszynski switch (chan->type) { 138232e0f6dSTomasz Duszynski case IIO_MASSCONCENTRATION: 139232e0f6dSTomasz Duszynski mutex_lock(&state->lock); 140232e0f6dSTomasz Duszynski /* read up to the number of bytes actually needed */ 141232e0f6dSTomasz Duszynski switch (chan->channel2) { 142232e0f6dSTomasz Duszynski case IIO_MOD_PM1: 143232e0f6dSTomasz Duszynski ret = sps30_do_meas(state, data, 1); 144232e0f6dSTomasz Duszynski break; 145232e0f6dSTomasz Duszynski case IIO_MOD_PM2P5: 146232e0f6dSTomasz Duszynski ret = sps30_do_meas(state, data, 2); 147232e0f6dSTomasz Duszynski break; 148232e0f6dSTomasz Duszynski case IIO_MOD_PM4: 149232e0f6dSTomasz Duszynski ret = sps30_do_meas(state, data, 3); 150232e0f6dSTomasz Duszynski break; 151232e0f6dSTomasz Duszynski case IIO_MOD_PM10: 152232e0f6dSTomasz Duszynski ret = sps30_do_meas(state, data, 4); 153232e0f6dSTomasz Duszynski break; 154232e0f6dSTomasz Duszynski } 155232e0f6dSTomasz Duszynski mutex_unlock(&state->lock); 156232e0f6dSTomasz Duszynski if (ret) 157232e0f6dSTomasz Duszynski return ret; 158232e0f6dSTomasz Duszynski 159232e0f6dSTomasz Duszynski *val = data[chan->address] / 100; 160232e0f6dSTomasz Duszynski *val2 = (data[chan->address] % 100) * 10000; 161232e0f6dSTomasz Duszynski 162232e0f6dSTomasz Duszynski return IIO_VAL_INT_PLUS_MICRO; 163232e0f6dSTomasz Duszynski default: 164232e0f6dSTomasz Duszynski return -EINVAL; 165232e0f6dSTomasz Duszynski } 166232e0f6dSTomasz Duszynski case IIO_CHAN_INFO_SCALE: 167232e0f6dSTomasz Duszynski switch (chan->type) { 168232e0f6dSTomasz Duszynski case IIO_MASSCONCENTRATION: 169232e0f6dSTomasz Duszynski switch (chan->channel2) { 170232e0f6dSTomasz Duszynski case IIO_MOD_PM1: 171232e0f6dSTomasz Duszynski case IIO_MOD_PM2P5: 172232e0f6dSTomasz Duszynski case IIO_MOD_PM4: 173232e0f6dSTomasz Duszynski case IIO_MOD_PM10: 174232e0f6dSTomasz Duszynski *val = 0; 175232e0f6dSTomasz Duszynski *val2 = 10000; 176232e0f6dSTomasz Duszynski 177232e0f6dSTomasz Duszynski return IIO_VAL_INT_PLUS_MICRO; 17859b9bb0aSJonathan Cameron default: 17959b9bb0aSJonathan Cameron return -EINVAL; 180232e0f6dSTomasz Duszynski } 181232e0f6dSTomasz Duszynski default: 182232e0f6dSTomasz Duszynski return -EINVAL; 183232e0f6dSTomasz Duszynski } 184232e0f6dSTomasz Duszynski } 185232e0f6dSTomasz Duszynski 186232e0f6dSTomasz Duszynski return -EINVAL; 187232e0f6dSTomasz Duszynski } 188232e0f6dSTomasz Duszynski 189c546d496STomasz Duszynski static ssize_t start_cleaning_store(struct device *dev, 190c546d496STomasz Duszynski struct device_attribute *attr, 191c546d496STomasz Duszynski const char *buf, size_t len) 192c546d496STomasz Duszynski { 193c546d496STomasz Duszynski struct iio_dev *indio_dev = dev_to_iio_dev(dev); 194c546d496STomasz Duszynski struct sps30_state *state = iio_priv(indio_dev); 195c546d496STomasz Duszynski int val, ret; 196c546d496STomasz Duszynski 197c546d496STomasz Duszynski if (kstrtoint(buf, 0, &val) || val != 1) 198c546d496STomasz Duszynski return -EINVAL; 199c546d496STomasz Duszynski 200c546d496STomasz Duszynski mutex_lock(&state->lock); 201*8f3f1308STomasz Duszynski ret = state->ops->clean_fan(state); 202c546d496STomasz Duszynski mutex_unlock(&state->lock); 203c546d496STomasz Duszynski if (ret) 204c546d496STomasz Duszynski return ret; 205c546d496STomasz Duszynski 206c546d496STomasz Duszynski return len; 207c546d496STomasz Duszynski } 208c546d496STomasz Duszynski 20962129a08STomasz Duszynski static ssize_t cleaning_period_show(struct device *dev, 21062129a08STomasz Duszynski struct device_attribute *attr, 21162129a08STomasz Duszynski char *buf) 21262129a08STomasz Duszynski { 21362129a08STomasz Duszynski struct iio_dev *indio_dev = dev_to_iio_dev(dev); 21462129a08STomasz Duszynski struct sps30_state *state = iio_priv(indio_dev); 215*8f3f1308STomasz Duszynski __be32 val; 21662129a08STomasz Duszynski int ret; 21762129a08STomasz Duszynski 21862129a08STomasz Duszynski mutex_lock(&state->lock); 219*8f3f1308STomasz Duszynski ret = state->ops->read_cleaning_period(state, &val); 22062129a08STomasz Duszynski mutex_unlock(&state->lock); 22162129a08STomasz Duszynski if (ret) 22262129a08STomasz Duszynski return ret; 22362129a08STomasz Duszynski 224*8f3f1308STomasz Duszynski return sprintf(buf, "%d\n", be32_to_cpu(val)); 22562129a08STomasz Duszynski } 22662129a08STomasz Duszynski 227*8f3f1308STomasz Duszynski static ssize_t cleaning_period_store(struct device *dev, struct device_attribute *attr, 22862129a08STomasz Duszynski const char *buf, size_t len) 22962129a08STomasz Duszynski { 23062129a08STomasz Duszynski struct iio_dev *indio_dev = dev_to_iio_dev(dev); 23162129a08STomasz Duszynski struct sps30_state *state = iio_priv(indio_dev); 23262129a08STomasz Duszynski int val, ret; 23362129a08STomasz Duszynski 23462129a08STomasz Duszynski if (kstrtoint(buf, 0, &val)) 23562129a08STomasz Duszynski return -EINVAL; 23662129a08STomasz Duszynski 23762129a08STomasz Duszynski if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) || 23862129a08STomasz Duszynski (val > SPS30_AUTO_CLEANING_PERIOD_MAX)) 23962129a08STomasz Duszynski return -EINVAL; 24062129a08STomasz Duszynski 24162129a08STomasz Duszynski mutex_lock(&state->lock); 242*8f3f1308STomasz Duszynski ret = state->ops->write_cleaning_period(state, cpu_to_be32(val)); 24362129a08STomasz Duszynski if (ret) { 24462129a08STomasz Duszynski mutex_unlock(&state->lock); 24562129a08STomasz Duszynski return ret; 24662129a08STomasz Duszynski } 24762129a08STomasz Duszynski 24862129a08STomasz Duszynski msleep(20); 24962129a08STomasz Duszynski 25062129a08STomasz Duszynski /* 25162129a08STomasz Duszynski * sensor requires reset in order to return up to date self cleaning 25262129a08STomasz Duszynski * period 25362129a08STomasz Duszynski */ 254*8f3f1308STomasz Duszynski ret = sps30_do_reset(state); 25562129a08STomasz Duszynski if (ret) 25662129a08STomasz Duszynski dev_warn(dev, 25762129a08STomasz Duszynski "period changed but reads will return the old value\n"); 25862129a08STomasz Duszynski 25962129a08STomasz Duszynski mutex_unlock(&state->lock); 26062129a08STomasz Duszynski 26162129a08STomasz Duszynski return len; 26262129a08STomasz Duszynski } 26362129a08STomasz Duszynski 26462129a08STomasz Duszynski static ssize_t cleaning_period_available_show(struct device *dev, 26562129a08STomasz Duszynski struct device_attribute *attr, 26662129a08STomasz Duszynski char *buf) 26762129a08STomasz Duszynski { 268643adb9aSTian Tao return sysfs_emit(buf, "[%d %d %d]\n", 26962129a08STomasz Duszynski SPS30_AUTO_CLEANING_PERIOD_MIN, 1, 27062129a08STomasz Duszynski SPS30_AUTO_CLEANING_PERIOD_MAX); 27162129a08STomasz Duszynski } 27262129a08STomasz Duszynski 273c546d496STomasz Duszynski static IIO_DEVICE_ATTR_WO(start_cleaning, 0); 27462129a08STomasz Duszynski static IIO_DEVICE_ATTR_RW(cleaning_period, 0); 27562129a08STomasz Duszynski static IIO_DEVICE_ATTR_RO(cleaning_period_available, 0); 276c546d496STomasz Duszynski 277c546d496STomasz Duszynski static struct attribute *sps30_attrs[] = { 278c546d496STomasz Duszynski &iio_dev_attr_start_cleaning.dev_attr.attr, 27962129a08STomasz Duszynski &iio_dev_attr_cleaning_period.dev_attr.attr, 28062129a08STomasz Duszynski &iio_dev_attr_cleaning_period_available.dev_attr.attr, 281c546d496STomasz Duszynski NULL 282c546d496STomasz Duszynski }; 283c546d496STomasz Duszynski 284c546d496STomasz Duszynski static const struct attribute_group sps30_attr_group = { 285c546d496STomasz Duszynski .attrs = sps30_attrs, 286c546d496STomasz Duszynski }; 287c546d496STomasz Duszynski 288232e0f6dSTomasz Duszynski static const struct iio_info sps30_info = { 289c546d496STomasz Duszynski .attrs = &sps30_attr_group, 290232e0f6dSTomasz Duszynski .read_raw = sps30_read_raw, 291232e0f6dSTomasz Duszynski }; 292232e0f6dSTomasz Duszynski 293232e0f6dSTomasz Duszynski #define SPS30_CHAN(_index, _mod) { \ 294232e0f6dSTomasz Duszynski .type = IIO_MASSCONCENTRATION, \ 295232e0f6dSTomasz Duszynski .modified = 1, \ 296232e0f6dSTomasz Duszynski .channel2 = IIO_MOD_ ## _mod, \ 297232e0f6dSTomasz Duszynski .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ 298232e0f6dSTomasz Duszynski .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 299232e0f6dSTomasz Duszynski .address = _mod, \ 300232e0f6dSTomasz Duszynski .scan_index = _index, \ 301232e0f6dSTomasz Duszynski .scan_type = { \ 302232e0f6dSTomasz Duszynski .sign = 'u', \ 303232e0f6dSTomasz Duszynski .realbits = 19, \ 304232e0f6dSTomasz Duszynski .storagebits = 32, \ 305232e0f6dSTomasz Duszynski .endianness = IIO_CPU, \ 306232e0f6dSTomasz Duszynski }, \ 307232e0f6dSTomasz Duszynski } 308232e0f6dSTomasz Duszynski 309232e0f6dSTomasz Duszynski static const struct iio_chan_spec sps30_channels[] = { 310232e0f6dSTomasz Duszynski SPS30_CHAN(0, PM1), 311232e0f6dSTomasz Duszynski SPS30_CHAN(1, PM2P5), 312232e0f6dSTomasz Duszynski SPS30_CHAN(2, PM4), 313232e0f6dSTomasz Duszynski SPS30_CHAN(3, PM10), 314232e0f6dSTomasz Duszynski IIO_CHAN_SOFT_TIMESTAMP(4), 315232e0f6dSTomasz Duszynski }; 316232e0f6dSTomasz Duszynski 317*8f3f1308STomasz Duszynski static void sps30_devm_stop_meas(void *data) 318232e0f6dSTomasz Duszynski { 319232e0f6dSTomasz Duszynski struct sps30_state *state = data; 320232e0f6dSTomasz Duszynski 321*8f3f1308STomasz Duszynski if (state->state == MEASURING) 322*8f3f1308STomasz Duszynski state->ops->stop_meas(state); 323232e0f6dSTomasz Duszynski } 324232e0f6dSTomasz Duszynski 325232e0f6dSTomasz Duszynski static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 }; 326232e0f6dSTomasz Duszynski 327*8f3f1308STomasz Duszynski int sps30_probe(struct device *dev, const char *name, void *priv, const struct sps30_ops *ops) 328232e0f6dSTomasz Duszynski { 329232e0f6dSTomasz Duszynski struct iio_dev *indio_dev; 330232e0f6dSTomasz Duszynski struct sps30_state *state; 331232e0f6dSTomasz Duszynski int ret; 332232e0f6dSTomasz Duszynski 333*8f3f1308STomasz Duszynski indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); 334232e0f6dSTomasz Duszynski if (!indio_dev) 335232e0f6dSTomasz Duszynski return -ENOMEM; 336232e0f6dSTomasz Duszynski 337*8f3f1308STomasz Duszynski dev_set_drvdata(dev, indio_dev); 338*8f3f1308STomasz Duszynski 339232e0f6dSTomasz Duszynski state = iio_priv(indio_dev); 340*8f3f1308STomasz Duszynski state->dev = dev; 341*8f3f1308STomasz Duszynski state->priv = priv; 342*8f3f1308STomasz Duszynski state->ops = ops; 343*8f3f1308STomasz Duszynski mutex_init(&state->lock); 344*8f3f1308STomasz Duszynski 345232e0f6dSTomasz Duszynski indio_dev->info = &sps30_info; 346*8f3f1308STomasz Duszynski indio_dev->name = name; 347232e0f6dSTomasz Duszynski indio_dev->channels = sps30_channels; 348232e0f6dSTomasz Duszynski indio_dev->num_channels = ARRAY_SIZE(sps30_channels); 349232e0f6dSTomasz Duszynski indio_dev->modes = INDIO_DIRECT_MODE; 350232e0f6dSTomasz Duszynski indio_dev->available_scan_masks = sps30_scan_masks; 351232e0f6dSTomasz Duszynski 352*8f3f1308STomasz Duszynski ret = sps30_do_reset(state); 353232e0f6dSTomasz Duszynski if (ret) { 354*8f3f1308STomasz Duszynski dev_err(dev, "failed to reset device\n"); 355232e0f6dSTomasz Duszynski return ret; 356232e0f6dSTomasz Duszynski } 357232e0f6dSTomasz Duszynski 358*8f3f1308STomasz Duszynski ret = state->ops->show_info(state); 359232e0f6dSTomasz Duszynski if (ret) { 360*8f3f1308STomasz Duszynski dev_err(dev, "failed to read device info\n"); 361232e0f6dSTomasz Duszynski return ret; 362232e0f6dSTomasz Duszynski } 363232e0f6dSTomasz Duszynski 364*8f3f1308STomasz Duszynski ret = devm_add_action_or_reset(dev, sps30_devm_stop_meas, state); 365232e0f6dSTomasz Duszynski if (ret) 366232e0f6dSTomasz Duszynski return ret; 367232e0f6dSTomasz Duszynski 368*8f3f1308STomasz Duszynski ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, 369232e0f6dSTomasz Duszynski sps30_trigger_handler, NULL); 370232e0f6dSTomasz Duszynski if (ret) 371232e0f6dSTomasz Duszynski return ret; 372232e0f6dSTomasz Duszynski 373*8f3f1308STomasz Duszynski return devm_iio_device_register(dev, indio_dev); 374232e0f6dSTomasz Duszynski } 375*8f3f1308STomasz Duszynski EXPORT_SYMBOL_GPL(sps30_probe); 376232e0f6dSTomasz Duszynski 377232e0f6dSTomasz Duszynski MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); 378232e0f6dSTomasz Duszynski MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver"); 379232e0f6dSTomasz Duszynski MODULE_LICENSE("GPL v2"); 380