164b3d8b1STomasz Duszynski // SPDX-License-Identifier: GPL-2.0 264b3d8b1STomasz Duszynski /* 364b3d8b1STomasz Duszynski * Sensirion SCD30 carbon dioxide sensor core driver 464b3d8b1STomasz Duszynski * 564b3d8b1STomasz Duszynski * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> 664b3d8b1STomasz Duszynski */ 764b3d8b1STomasz Duszynski #include <linux/bits.h> 864b3d8b1STomasz Duszynski #include <linux/completion.h> 964b3d8b1STomasz Duszynski #include <linux/delay.h> 1064b3d8b1STomasz Duszynski #include <linux/device.h> 1164b3d8b1STomasz Duszynski #include <linux/errno.h> 1264b3d8b1STomasz Duszynski #include <linux/export.h> 1364b3d8b1STomasz Duszynski #include <linux/iio/buffer.h> 1464b3d8b1STomasz Duszynski #include <linux/iio/iio.h> 1564b3d8b1STomasz Duszynski #include <linux/iio/sysfs.h> 1664b3d8b1STomasz Duszynski #include <linux/iio/trigger.h> 1764b3d8b1STomasz Duszynski #include <linux/iio/trigger_consumer.h> 1864b3d8b1STomasz Duszynski #include <linux/iio/triggered_buffer.h> 1964b3d8b1STomasz Duszynski #include <linux/iio/types.h> 2064b3d8b1STomasz Duszynski #include <linux/interrupt.h> 2164b3d8b1STomasz Duszynski #include <linux/irqreturn.h> 2264b3d8b1STomasz Duszynski #include <linux/jiffies.h> 2364b3d8b1STomasz Duszynski #include <linux/kernel.h> 2464b3d8b1STomasz Duszynski #include <linux/module.h> 2564b3d8b1STomasz Duszynski #include <linux/mutex.h> 2664b3d8b1STomasz Duszynski #include <linux/regulator/consumer.h> 2764b3d8b1STomasz Duszynski #include <linux/string.h> 2864b3d8b1STomasz Duszynski #include <linux/sysfs.h> 2964b3d8b1STomasz Duszynski #include <linux/types.h> 3064b3d8b1STomasz Duszynski #include <asm/byteorder.h> 3164b3d8b1STomasz Duszynski 3264b3d8b1STomasz Duszynski #include "scd30.h" 3364b3d8b1STomasz Duszynski 3464b3d8b1STomasz Duszynski #define SCD30_PRESSURE_COMP_MIN_MBAR 700 3564b3d8b1STomasz Duszynski #define SCD30_PRESSURE_COMP_MAX_MBAR 1400 3664b3d8b1STomasz Duszynski #define SCD30_PRESSURE_COMP_DEFAULT 1013 3764b3d8b1STomasz Duszynski #define SCD30_MEAS_INTERVAL_MIN_S 2 3864b3d8b1STomasz Duszynski #define SCD30_MEAS_INTERVAL_MAX_S 1800 3964b3d8b1STomasz Duszynski #define SCD30_MEAS_INTERVAL_DEFAULT SCD30_MEAS_INTERVAL_MIN_S 4064b3d8b1STomasz Duszynski #define SCD30_FRC_MIN_PPM 400 4164b3d8b1STomasz Duszynski #define SCD30_FRC_MAX_PPM 2000 4264b3d8b1STomasz Duszynski #define SCD30_TEMP_OFFSET_MAX 655360 4364b3d8b1STomasz Duszynski #define SCD30_EXTRA_TIMEOUT_PER_S 250 4464b3d8b1STomasz Duszynski 4564b3d8b1STomasz Duszynski enum { 4664b3d8b1STomasz Duszynski SCD30_CONC, 4764b3d8b1STomasz Duszynski SCD30_TEMP, 4864b3d8b1STomasz Duszynski SCD30_HR, 4964b3d8b1STomasz Duszynski }; 5064b3d8b1STomasz Duszynski 5164b3d8b1STomasz Duszynski static int scd30_command_write(struct scd30_state *state, enum scd30_cmd cmd, u16 arg) 5264b3d8b1STomasz Duszynski { 5364b3d8b1STomasz Duszynski return state->command(state, cmd, arg, NULL, 0); 5464b3d8b1STomasz Duszynski } 5564b3d8b1STomasz Duszynski 5664b3d8b1STomasz Duszynski static int scd30_command_read(struct scd30_state *state, enum scd30_cmd cmd, u16 *val) 5764b3d8b1STomasz Duszynski { 5864b3d8b1STomasz Duszynski __be16 tmp; 5964b3d8b1STomasz Duszynski int ret; 6064b3d8b1STomasz Duszynski 6164b3d8b1STomasz Duszynski ret = state->command(state, cmd, 0, &tmp, sizeof(tmp)); 6264b3d8b1STomasz Duszynski *val = be16_to_cpup(&tmp); 6364b3d8b1STomasz Duszynski 6464b3d8b1STomasz Duszynski return ret; 6564b3d8b1STomasz Duszynski } 6664b3d8b1STomasz Duszynski 6764b3d8b1STomasz Duszynski static int scd30_reset(struct scd30_state *state) 6864b3d8b1STomasz Duszynski { 6964b3d8b1STomasz Duszynski int ret; 7064b3d8b1STomasz Duszynski u16 val; 7164b3d8b1STomasz Duszynski 7264b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_RESET, 0); 7364b3d8b1STomasz Duszynski if (ret) 7464b3d8b1STomasz Duszynski return ret; 7564b3d8b1STomasz Duszynski 7664b3d8b1STomasz Duszynski /* sensor boots up within 2 secs */ 7764b3d8b1STomasz Duszynski msleep(2000); 7864b3d8b1STomasz Duszynski /* 7964b3d8b1STomasz Duszynski * Power-on-reset causes sensor to produce some glitch on i2c bus and 8064b3d8b1STomasz Duszynski * some controllers end up in error state. Try to recover by placing 8164b3d8b1STomasz Duszynski * any data on the bus. 8264b3d8b1STomasz Duszynski */ 8364b3d8b1STomasz Duszynski scd30_command_read(state, CMD_MEAS_READY, &val); 8464b3d8b1STomasz Duszynski 8564b3d8b1STomasz Duszynski return 0; 8664b3d8b1STomasz Duszynski } 8764b3d8b1STomasz Duszynski 8864b3d8b1STomasz Duszynski /* simplified float to fixed point conversion with a scaling factor of 0.01 */ 8964b3d8b1STomasz Duszynski static int scd30_float_to_fp(int float32) 9064b3d8b1STomasz Duszynski { 9164b3d8b1STomasz Duszynski int fraction, shift, 9264b3d8b1STomasz Duszynski mantissa = float32 & GENMASK(22, 0), 9364b3d8b1STomasz Duszynski sign = (float32 & BIT(31)) ? -1 : 1, 9464b3d8b1STomasz Duszynski exp = (float32 & ~BIT(31)) >> 23; 9564b3d8b1STomasz Duszynski 9664b3d8b1STomasz Duszynski /* special case 0 */ 9764b3d8b1STomasz Duszynski if (!exp && !mantissa) 9864b3d8b1STomasz Duszynski return 0; 9964b3d8b1STomasz Duszynski 10064b3d8b1STomasz Duszynski exp -= 127; 10164b3d8b1STomasz Duszynski if (exp < 0) { 10264b3d8b1STomasz Duszynski exp = -exp; 10364b3d8b1STomasz Duszynski /* return values ranging from 1 to 99 */ 10464b3d8b1STomasz Duszynski return sign * ((((BIT(23) + mantissa) * 100) >> 23) >> exp); 10564b3d8b1STomasz Duszynski } 10664b3d8b1STomasz Duszynski 10764b3d8b1STomasz Duszynski /* return values starting at 100 */ 10864b3d8b1STomasz Duszynski shift = 23 - exp; 10964b3d8b1STomasz Duszynski float32 = BIT(exp) + (mantissa >> shift); 11064b3d8b1STomasz Duszynski fraction = mantissa & GENMASK(shift - 1, 0); 11164b3d8b1STomasz Duszynski 11264b3d8b1STomasz Duszynski return sign * (float32 * 100 + ((fraction * 100) >> shift)); 11364b3d8b1STomasz Duszynski } 11464b3d8b1STomasz Duszynski 11564b3d8b1STomasz Duszynski static int scd30_read_meas(struct scd30_state *state) 11664b3d8b1STomasz Duszynski { 11764b3d8b1STomasz Duszynski int i, ret; 11864b3d8b1STomasz Duszynski 11964b3d8b1STomasz Duszynski ret = state->command(state, CMD_READ_MEAS, 0, state->meas, sizeof(state->meas)); 12064b3d8b1STomasz Duszynski if (ret) 12164b3d8b1STomasz Duszynski return ret; 12264b3d8b1STomasz Duszynski 12364b3d8b1STomasz Duszynski be32_to_cpu_array(state->meas, (__be32 *)state->meas, ARRAY_SIZE(state->meas)); 12464b3d8b1STomasz Duszynski 12564b3d8b1STomasz Duszynski for (i = 0; i < ARRAY_SIZE(state->meas); i++) 12664b3d8b1STomasz Duszynski state->meas[i] = scd30_float_to_fp(state->meas[i]); 12764b3d8b1STomasz Duszynski 12864b3d8b1STomasz Duszynski /* 12964b3d8b1STomasz Duszynski * co2 is left unprocessed while temperature and humidity are scaled 13064b3d8b1STomasz Duszynski * to milli deg C and milli percent respectively. 13164b3d8b1STomasz Duszynski */ 13264b3d8b1STomasz Duszynski state->meas[SCD30_TEMP] *= 10; 13364b3d8b1STomasz Duszynski state->meas[SCD30_HR] *= 10; 13464b3d8b1STomasz Duszynski 13564b3d8b1STomasz Duszynski return 0; 13664b3d8b1STomasz Duszynski } 13764b3d8b1STomasz Duszynski 13864b3d8b1STomasz Duszynski static int scd30_wait_meas_irq(struct scd30_state *state) 13964b3d8b1STomasz Duszynski { 14064b3d8b1STomasz Duszynski int ret, timeout; 14164b3d8b1STomasz Duszynski 14264b3d8b1STomasz Duszynski reinit_completion(&state->meas_ready); 14364b3d8b1STomasz Duszynski enable_irq(state->irq); 14464b3d8b1STomasz Duszynski timeout = msecs_to_jiffies(state->meas_interval * (1000 + SCD30_EXTRA_TIMEOUT_PER_S)); 14564b3d8b1STomasz Duszynski ret = wait_for_completion_interruptible_timeout(&state->meas_ready, timeout); 14664b3d8b1STomasz Duszynski if (ret > 0) 14764b3d8b1STomasz Duszynski ret = 0; 14864b3d8b1STomasz Duszynski else if (!ret) 14964b3d8b1STomasz Duszynski ret = -ETIMEDOUT; 15064b3d8b1STomasz Duszynski 15164b3d8b1STomasz Duszynski disable_irq(state->irq); 15264b3d8b1STomasz Duszynski 15364b3d8b1STomasz Duszynski return ret; 15464b3d8b1STomasz Duszynski } 15564b3d8b1STomasz Duszynski 15664b3d8b1STomasz Duszynski static int scd30_wait_meas_poll(struct scd30_state *state) 15764b3d8b1STomasz Duszynski { 15864b3d8b1STomasz Duszynski int timeout = state->meas_interval * SCD30_EXTRA_TIMEOUT_PER_S, tries = 5; 15964b3d8b1STomasz Duszynski 16064b3d8b1STomasz Duszynski do { 16164b3d8b1STomasz Duszynski int ret; 16264b3d8b1STomasz Duszynski u16 val; 16364b3d8b1STomasz Duszynski 16464b3d8b1STomasz Duszynski ret = scd30_command_read(state, CMD_MEAS_READY, &val); 16564b3d8b1STomasz Duszynski if (ret) 16664b3d8b1STomasz Duszynski return -EIO; 16764b3d8b1STomasz Duszynski 16864b3d8b1STomasz Duszynski /* new measurement available */ 16964b3d8b1STomasz Duszynski if (val) 17064b3d8b1STomasz Duszynski break; 17164b3d8b1STomasz Duszynski 17264b3d8b1STomasz Duszynski msleep_interruptible(timeout); 17364b3d8b1STomasz Duszynski } while (--tries); 17464b3d8b1STomasz Duszynski 17564b3d8b1STomasz Duszynski return tries ? 0 : -ETIMEDOUT; 17664b3d8b1STomasz Duszynski } 17764b3d8b1STomasz Duszynski 17864b3d8b1STomasz Duszynski static int scd30_read_poll(struct scd30_state *state) 17964b3d8b1STomasz Duszynski { 18064b3d8b1STomasz Duszynski int ret; 18164b3d8b1STomasz Duszynski 18264b3d8b1STomasz Duszynski ret = scd30_wait_meas_poll(state); 18364b3d8b1STomasz Duszynski if (ret) 18464b3d8b1STomasz Duszynski return ret; 18564b3d8b1STomasz Duszynski 18664b3d8b1STomasz Duszynski return scd30_read_meas(state); 18764b3d8b1STomasz Duszynski } 18864b3d8b1STomasz Duszynski 18964b3d8b1STomasz Duszynski static int scd30_read(struct scd30_state *state) 19064b3d8b1STomasz Duszynski { 19164b3d8b1STomasz Duszynski if (state->irq > 0) 19264b3d8b1STomasz Duszynski return scd30_wait_meas_irq(state); 19364b3d8b1STomasz Duszynski 19464b3d8b1STomasz Duszynski return scd30_read_poll(state); 19564b3d8b1STomasz Duszynski } 19664b3d8b1STomasz Duszynski 19764b3d8b1STomasz Duszynski static int scd30_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 19864b3d8b1STomasz Duszynski int *val, int *val2, long mask) 19964b3d8b1STomasz Duszynski { 20064b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 20164b3d8b1STomasz Duszynski int ret = -EINVAL; 20264b3d8b1STomasz Duszynski u16 tmp; 20364b3d8b1STomasz Duszynski 20464b3d8b1STomasz Duszynski mutex_lock(&state->lock); 20564b3d8b1STomasz Duszynski switch (mask) { 20664b3d8b1STomasz Duszynski case IIO_CHAN_INFO_RAW: 20764b3d8b1STomasz Duszynski case IIO_CHAN_INFO_PROCESSED: 20864b3d8b1STomasz Duszynski if (chan->output) { 20964b3d8b1STomasz Duszynski *val = state->pressure_comp; 21064b3d8b1STomasz Duszynski ret = IIO_VAL_INT; 21164b3d8b1STomasz Duszynski break; 21264b3d8b1STomasz Duszynski } 21364b3d8b1STomasz Duszynski 21464b3d8b1STomasz Duszynski ret = iio_device_claim_direct_mode(indio_dev); 21564b3d8b1STomasz Duszynski if (ret) 21664b3d8b1STomasz Duszynski break; 21764b3d8b1STomasz Duszynski 21864b3d8b1STomasz Duszynski ret = scd30_read(state); 21964b3d8b1STomasz Duszynski if (ret) { 22064b3d8b1STomasz Duszynski iio_device_release_direct_mode(indio_dev); 22164b3d8b1STomasz Duszynski break; 22264b3d8b1STomasz Duszynski } 22364b3d8b1STomasz Duszynski 22464b3d8b1STomasz Duszynski *val = state->meas[chan->address]; 22564b3d8b1STomasz Duszynski iio_device_release_direct_mode(indio_dev); 22664b3d8b1STomasz Duszynski ret = IIO_VAL_INT; 22764b3d8b1STomasz Duszynski break; 22864b3d8b1STomasz Duszynski case IIO_CHAN_INFO_SCALE: 22964b3d8b1STomasz Duszynski *val = 0; 23064b3d8b1STomasz Duszynski *val2 = 1; 23164b3d8b1STomasz Duszynski ret = IIO_VAL_INT_PLUS_MICRO; 23264b3d8b1STomasz Duszynski break; 23364b3d8b1STomasz Duszynski case IIO_CHAN_INFO_SAMP_FREQ: 23464b3d8b1STomasz Duszynski ret = scd30_command_read(state, CMD_MEAS_INTERVAL, &tmp); 23564b3d8b1STomasz Duszynski if (ret) 23664b3d8b1STomasz Duszynski break; 23764b3d8b1STomasz Duszynski 23864b3d8b1STomasz Duszynski *val = 0; 23964b3d8b1STomasz Duszynski *val2 = 1000000000 / tmp; 24064b3d8b1STomasz Duszynski ret = IIO_VAL_INT_PLUS_NANO; 24164b3d8b1STomasz Duszynski break; 24264b3d8b1STomasz Duszynski case IIO_CHAN_INFO_CALIBBIAS: 24364b3d8b1STomasz Duszynski ret = scd30_command_read(state, CMD_TEMP_OFFSET, &tmp); 24464b3d8b1STomasz Duszynski if (ret) 24564b3d8b1STomasz Duszynski break; 24664b3d8b1STomasz Duszynski 24764b3d8b1STomasz Duszynski *val = tmp; 24864b3d8b1STomasz Duszynski ret = IIO_VAL_INT; 24964b3d8b1STomasz Duszynski break; 25064b3d8b1STomasz Duszynski } 25164b3d8b1STomasz Duszynski mutex_unlock(&state->lock); 25264b3d8b1STomasz Duszynski 25364b3d8b1STomasz Duszynski return ret; 25464b3d8b1STomasz Duszynski } 25564b3d8b1STomasz Duszynski 25664b3d8b1STomasz Duszynski static int scd30_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 25764b3d8b1STomasz Duszynski int val, int val2, long mask) 25864b3d8b1STomasz Duszynski { 25964b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 26064b3d8b1STomasz Duszynski int ret = -EINVAL; 26164b3d8b1STomasz Duszynski 26264b3d8b1STomasz Duszynski mutex_lock(&state->lock); 26364b3d8b1STomasz Duszynski switch (mask) { 26464b3d8b1STomasz Duszynski case IIO_CHAN_INFO_SAMP_FREQ: 26564b3d8b1STomasz Duszynski if (val) 26664b3d8b1STomasz Duszynski break; 26764b3d8b1STomasz Duszynski 26864b3d8b1STomasz Duszynski val = 1000000000 / val2; 26964b3d8b1STomasz Duszynski if (val < SCD30_MEAS_INTERVAL_MIN_S || val > SCD30_MEAS_INTERVAL_MAX_S) 27064b3d8b1STomasz Duszynski break; 27164b3d8b1STomasz Duszynski 27264b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_MEAS_INTERVAL, val); 27364b3d8b1STomasz Duszynski if (ret) 27464b3d8b1STomasz Duszynski break; 27564b3d8b1STomasz Duszynski 27664b3d8b1STomasz Duszynski state->meas_interval = val; 27764b3d8b1STomasz Duszynski break; 27864b3d8b1STomasz Duszynski case IIO_CHAN_INFO_RAW: 27964b3d8b1STomasz Duszynski switch (chan->type) { 28064b3d8b1STomasz Duszynski case IIO_PRESSURE: 28164b3d8b1STomasz Duszynski if (val < SCD30_PRESSURE_COMP_MIN_MBAR || 28264b3d8b1STomasz Duszynski val > SCD30_PRESSURE_COMP_MAX_MBAR) 28364b3d8b1STomasz Duszynski break; 28464b3d8b1STomasz Duszynski 28564b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_START_MEAS, val); 28664b3d8b1STomasz Duszynski if (ret) 28764b3d8b1STomasz Duszynski break; 28864b3d8b1STomasz Duszynski 28964b3d8b1STomasz Duszynski state->pressure_comp = val; 29064b3d8b1STomasz Duszynski break; 29164b3d8b1STomasz Duszynski default: 29264b3d8b1STomasz Duszynski break; 29364b3d8b1STomasz Duszynski } 29464b3d8b1STomasz Duszynski break; 29564b3d8b1STomasz Duszynski case IIO_CHAN_INFO_CALIBBIAS: 29664b3d8b1STomasz Duszynski if (val < 0 || val > SCD30_TEMP_OFFSET_MAX) 29764b3d8b1STomasz Duszynski break; 29864b3d8b1STomasz Duszynski /* 29964b3d8b1STomasz Duszynski * Manufacturer does not explicitly specify min/max sensible 30064b3d8b1STomasz Duszynski * values hence check is omitted for simplicity. 30164b3d8b1STomasz Duszynski */ 30264b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_TEMP_OFFSET / 10, val); 30364b3d8b1STomasz Duszynski } 30464b3d8b1STomasz Duszynski mutex_unlock(&state->lock); 30564b3d8b1STomasz Duszynski 30664b3d8b1STomasz Duszynski return ret; 30764b3d8b1STomasz Duszynski } 30864b3d8b1STomasz Duszynski 30964b3d8b1STomasz Duszynski static int scd30_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 31064b3d8b1STomasz Duszynski long mask) 31164b3d8b1STomasz Duszynski { 31264b3d8b1STomasz Duszynski switch (mask) { 31364b3d8b1STomasz Duszynski case IIO_CHAN_INFO_SAMP_FREQ: 31464b3d8b1STomasz Duszynski return IIO_VAL_INT_PLUS_NANO; 31564b3d8b1STomasz Duszynski case IIO_CHAN_INFO_RAW: 31664b3d8b1STomasz Duszynski case IIO_CHAN_INFO_CALIBBIAS: 31764b3d8b1STomasz Duszynski return IIO_VAL_INT; 31864b3d8b1STomasz Duszynski } 31964b3d8b1STomasz Duszynski 32064b3d8b1STomasz Duszynski return -EINVAL; 32164b3d8b1STomasz Duszynski } 32264b3d8b1STomasz Duszynski 32364b3d8b1STomasz Duszynski static const int scd30_pressure_raw_available[] = { 32464b3d8b1STomasz Duszynski SCD30_PRESSURE_COMP_MIN_MBAR, 1, SCD30_PRESSURE_COMP_MAX_MBAR, 32564b3d8b1STomasz Duszynski }; 32664b3d8b1STomasz Duszynski 32764b3d8b1STomasz Duszynski static const int scd30_temp_calibbias_available[] = { 32864b3d8b1STomasz Duszynski 0, 10, SCD30_TEMP_OFFSET_MAX, 32964b3d8b1STomasz Duszynski }; 33064b3d8b1STomasz Duszynski 33164b3d8b1STomasz Duszynski static int scd30_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 33264b3d8b1STomasz Duszynski const int **vals, int *type, int *length, long mask) 33364b3d8b1STomasz Duszynski { 33464b3d8b1STomasz Duszynski switch (mask) { 33564b3d8b1STomasz Duszynski case IIO_CHAN_INFO_RAW: 33664b3d8b1STomasz Duszynski *vals = scd30_pressure_raw_available; 33764b3d8b1STomasz Duszynski *type = IIO_VAL_INT; 33864b3d8b1STomasz Duszynski 33964b3d8b1STomasz Duszynski return IIO_AVAIL_RANGE; 34064b3d8b1STomasz Duszynski case IIO_CHAN_INFO_CALIBBIAS: 34164b3d8b1STomasz Duszynski *vals = scd30_temp_calibbias_available; 34264b3d8b1STomasz Duszynski *type = IIO_VAL_INT; 34364b3d8b1STomasz Duszynski 34464b3d8b1STomasz Duszynski return IIO_AVAIL_RANGE; 34564b3d8b1STomasz Duszynski } 34664b3d8b1STomasz Duszynski 34764b3d8b1STomasz Duszynski return -EINVAL; 34864b3d8b1STomasz Duszynski } 34964b3d8b1STomasz Duszynski 35064b3d8b1STomasz Duszynski static ssize_t sampling_frequency_available_show(struct device *dev, struct device_attribute *attr, 35164b3d8b1STomasz Duszynski char *buf) 35264b3d8b1STomasz Duszynski { 35364b3d8b1STomasz Duszynski int i = SCD30_MEAS_INTERVAL_MIN_S; 35464b3d8b1STomasz Duszynski ssize_t len = 0; 35564b3d8b1STomasz Duszynski 35664b3d8b1STomasz Duszynski do { 35764b3d8b1STomasz Duszynski len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09u ", 1000000000 / i); 35864b3d8b1STomasz Duszynski /* 35964b3d8b1STomasz Duszynski * Not all values fit PAGE_SIZE buffer hence print every 6th 36064b3d8b1STomasz Duszynski * (each frequency differs by 6s in time domain from the 36164b3d8b1STomasz Duszynski * adjacent). Unlisted but valid ones are still accepted. 36264b3d8b1STomasz Duszynski */ 36364b3d8b1STomasz Duszynski i += 6; 36464b3d8b1STomasz Duszynski } while (i <= SCD30_MEAS_INTERVAL_MAX_S); 36564b3d8b1STomasz Duszynski 36664b3d8b1STomasz Duszynski buf[len - 1] = '\n'; 36764b3d8b1STomasz Duszynski 36864b3d8b1STomasz Duszynski return len; 36964b3d8b1STomasz Duszynski } 37064b3d8b1STomasz Duszynski 37164b3d8b1STomasz Duszynski static ssize_t calibration_auto_enable_show(struct device *dev, struct device_attribute *attr, 37264b3d8b1STomasz Duszynski char *buf) 37364b3d8b1STomasz Duszynski { 37464b3d8b1STomasz Duszynski struct iio_dev *indio_dev = dev_to_iio_dev(dev); 37564b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 37664b3d8b1STomasz Duszynski int ret; 37764b3d8b1STomasz Duszynski u16 val; 37864b3d8b1STomasz Duszynski 37964b3d8b1STomasz Duszynski mutex_lock(&state->lock); 38064b3d8b1STomasz Duszynski ret = scd30_command_read(state, CMD_ASC, &val); 38164b3d8b1STomasz Duszynski mutex_unlock(&state->lock); 38264b3d8b1STomasz Duszynski 38364b3d8b1STomasz Duszynski return ret ?: sprintf(buf, "%d\n", val); 38464b3d8b1STomasz Duszynski } 38564b3d8b1STomasz Duszynski 38664b3d8b1STomasz Duszynski static ssize_t calibration_auto_enable_store(struct device *dev, struct device_attribute *attr, 38764b3d8b1STomasz Duszynski const char *buf, size_t len) 38864b3d8b1STomasz Duszynski { 38964b3d8b1STomasz Duszynski struct iio_dev *indio_dev = dev_to_iio_dev(dev); 39064b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 39164b3d8b1STomasz Duszynski bool val; 39264b3d8b1STomasz Duszynski int ret; 39364b3d8b1STomasz Duszynski 39464b3d8b1STomasz Duszynski ret = kstrtobool(buf, &val); 39564b3d8b1STomasz Duszynski if (ret) 39664b3d8b1STomasz Duszynski return ret; 39764b3d8b1STomasz Duszynski 39864b3d8b1STomasz Duszynski mutex_lock(&state->lock); 39964b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_ASC, val); 40064b3d8b1STomasz Duszynski mutex_unlock(&state->lock); 40164b3d8b1STomasz Duszynski 40264b3d8b1STomasz Duszynski return ret ?: len; 40364b3d8b1STomasz Duszynski } 40464b3d8b1STomasz Duszynski 40564b3d8b1STomasz Duszynski static ssize_t calibration_forced_value_show(struct device *dev, struct device_attribute *attr, 40664b3d8b1STomasz Duszynski char *buf) 40764b3d8b1STomasz Duszynski { 40864b3d8b1STomasz Duszynski struct iio_dev *indio_dev = dev_to_iio_dev(dev); 40964b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 41064b3d8b1STomasz Duszynski int ret; 41164b3d8b1STomasz Duszynski u16 val; 41264b3d8b1STomasz Duszynski 41364b3d8b1STomasz Duszynski mutex_lock(&state->lock); 41464b3d8b1STomasz Duszynski ret = scd30_command_read(state, CMD_FRC, &val); 41564b3d8b1STomasz Duszynski mutex_unlock(&state->lock); 41664b3d8b1STomasz Duszynski 41764b3d8b1STomasz Duszynski return ret ?: sprintf(buf, "%d\n", val); 41864b3d8b1STomasz Duszynski } 41964b3d8b1STomasz Duszynski 42064b3d8b1STomasz Duszynski static ssize_t calibration_forced_value_store(struct device *dev, struct device_attribute *attr, 42164b3d8b1STomasz Duszynski const char *buf, size_t len) 42264b3d8b1STomasz Duszynski { 42364b3d8b1STomasz Duszynski struct iio_dev *indio_dev = dev_to_iio_dev(dev); 42464b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 42564b3d8b1STomasz Duszynski int ret; 42664b3d8b1STomasz Duszynski u16 val; 42764b3d8b1STomasz Duszynski 42864b3d8b1STomasz Duszynski ret = kstrtou16(buf, 0, &val); 42964b3d8b1STomasz Duszynski if (ret) 43064b3d8b1STomasz Duszynski return ret; 43164b3d8b1STomasz Duszynski 43264b3d8b1STomasz Duszynski if (val < SCD30_FRC_MIN_PPM || val > SCD30_FRC_MAX_PPM) 43364b3d8b1STomasz Duszynski return -EINVAL; 43464b3d8b1STomasz Duszynski 43564b3d8b1STomasz Duszynski mutex_lock(&state->lock); 43664b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_FRC, val); 43764b3d8b1STomasz Duszynski mutex_unlock(&state->lock); 43864b3d8b1STomasz Duszynski 43964b3d8b1STomasz Duszynski return ret ?: len; 44064b3d8b1STomasz Duszynski } 44164b3d8b1STomasz Duszynski 44264b3d8b1STomasz Duszynski static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0); 44364b3d8b1STomasz Duszynski static IIO_DEVICE_ATTR_RW(calibration_auto_enable, 0); 44464b3d8b1STomasz Duszynski static IIO_DEVICE_ATTR_RW(calibration_forced_value, 0); 44564b3d8b1STomasz Duszynski 44664b3d8b1STomasz Duszynski static struct attribute *scd30_attrs[] = { 44764b3d8b1STomasz Duszynski &iio_dev_attr_sampling_frequency_available.dev_attr.attr, 44864b3d8b1STomasz Duszynski &iio_dev_attr_calibration_auto_enable.dev_attr.attr, 44964b3d8b1STomasz Duszynski &iio_dev_attr_calibration_forced_value.dev_attr.attr, 45064b3d8b1STomasz Duszynski NULL 45164b3d8b1STomasz Duszynski }; 45264b3d8b1STomasz Duszynski 45364b3d8b1STomasz Duszynski static const struct attribute_group scd30_attr_group = { 45464b3d8b1STomasz Duszynski .attrs = scd30_attrs, 45564b3d8b1STomasz Duszynski }; 45664b3d8b1STomasz Duszynski 45764b3d8b1STomasz Duszynski static const struct iio_info scd30_info = { 45864b3d8b1STomasz Duszynski .attrs = &scd30_attr_group, 45964b3d8b1STomasz Duszynski .read_raw = scd30_read_raw, 46064b3d8b1STomasz Duszynski .write_raw = scd30_write_raw, 46164b3d8b1STomasz Duszynski .write_raw_get_fmt = scd30_write_raw_get_fmt, 46264b3d8b1STomasz Duszynski .read_avail = scd30_read_avail, 46364b3d8b1STomasz Duszynski }; 46464b3d8b1STomasz Duszynski 46564b3d8b1STomasz Duszynski #define SCD30_CHAN_SCAN_TYPE(_sign, _realbits) .scan_type = { \ 46664b3d8b1STomasz Duszynski .sign = _sign, \ 46764b3d8b1STomasz Duszynski .realbits = _realbits, \ 46864b3d8b1STomasz Duszynski .storagebits = 32, \ 46964b3d8b1STomasz Duszynski .endianness = IIO_CPU, \ 47064b3d8b1STomasz Duszynski } 47164b3d8b1STomasz Duszynski 47264b3d8b1STomasz Duszynski static const struct iio_chan_spec scd30_channels[] = { 47364b3d8b1STomasz Duszynski { 47464b3d8b1STomasz Duszynski /* 47564b3d8b1STomasz Duszynski * this channel is special in a sense we are pretending that 47664b3d8b1STomasz Duszynski * sensor is able to change measurement chamber pressure but in 47764b3d8b1STomasz Duszynski * fact we're just setting pressure compensation value 47864b3d8b1STomasz Duszynski */ 47964b3d8b1STomasz Duszynski .type = IIO_PRESSURE, 48064b3d8b1STomasz Duszynski .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 48164b3d8b1STomasz Duszynski .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), 48264b3d8b1STomasz Duszynski .output = 1, 48364b3d8b1STomasz Duszynski .scan_index = -1, 48464b3d8b1STomasz Duszynski }, 48564b3d8b1STomasz Duszynski { 48664b3d8b1STomasz Duszynski .type = IIO_CONCENTRATION, 48764b3d8b1STomasz Duszynski .channel2 = IIO_MOD_CO2, 48864b3d8b1STomasz Duszynski .address = SCD30_CONC, 48964b3d8b1STomasz Duszynski .scan_index = SCD30_CONC, 49064b3d8b1STomasz Duszynski .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 49164b3d8b1STomasz Duszynski BIT(IIO_CHAN_INFO_SCALE), 49264b3d8b1STomasz Duszynski .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), 49364b3d8b1STomasz Duszynski .modified = 1, 49464b3d8b1STomasz Duszynski 49564b3d8b1STomasz Duszynski SCD30_CHAN_SCAN_TYPE('u', 20), 49664b3d8b1STomasz Duszynski }, 49764b3d8b1STomasz Duszynski { 49864b3d8b1STomasz Duszynski .type = IIO_TEMP, 49964b3d8b1STomasz Duszynski .address = SCD30_TEMP, 50064b3d8b1STomasz Duszynski .scan_index = SCD30_TEMP, 50164b3d8b1STomasz Duszynski .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | 50264b3d8b1STomasz Duszynski BIT(IIO_CHAN_INFO_CALIBBIAS), 50364b3d8b1STomasz Duszynski .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS), 50464b3d8b1STomasz Duszynski .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), 50564b3d8b1STomasz Duszynski 50664b3d8b1STomasz Duszynski SCD30_CHAN_SCAN_TYPE('s', 18), 50764b3d8b1STomasz Duszynski }, 50864b3d8b1STomasz Duszynski { 50964b3d8b1STomasz Duszynski .type = IIO_HUMIDITYRELATIVE, 51064b3d8b1STomasz Duszynski .address = SCD30_HR, 51164b3d8b1STomasz Duszynski .scan_index = SCD30_HR, 51264b3d8b1STomasz Duszynski .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 51364b3d8b1STomasz Duszynski .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), 51464b3d8b1STomasz Duszynski 51564b3d8b1STomasz Duszynski SCD30_CHAN_SCAN_TYPE('u', 17), 51664b3d8b1STomasz Duszynski }, 51764b3d8b1STomasz Duszynski IIO_CHAN_SOFT_TIMESTAMP(3), 51864b3d8b1STomasz Duszynski }; 51964b3d8b1STomasz Duszynski 52064b3d8b1STomasz Duszynski int __maybe_unused scd30_suspend(struct device *dev) 52164b3d8b1STomasz Duszynski { 52264b3d8b1STomasz Duszynski struct iio_dev *indio_dev = dev_get_drvdata(dev); 52364b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 52464b3d8b1STomasz Duszynski int ret; 52564b3d8b1STomasz Duszynski 52664b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_STOP_MEAS, 0); 52764b3d8b1STomasz Duszynski if (ret) 52864b3d8b1STomasz Duszynski return ret; 52964b3d8b1STomasz Duszynski 53064b3d8b1STomasz Duszynski return regulator_disable(state->vdd); 53164b3d8b1STomasz Duszynski } 53264b3d8b1STomasz Duszynski EXPORT_SYMBOL(scd30_suspend); 53364b3d8b1STomasz Duszynski 53464b3d8b1STomasz Duszynski int __maybe_unused scd30_resume(struct device *dev) 53564b3d8b1STomasz Duszynski { 53664b3d8b1STomasz Duszynski struct iio_dev *indio_dev = dev_get_drvdata(dev); 53764b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 53864b3d8b1STomasz Duszynski int ret; 53964b3d8b1STomasz Duszynski 54064b3d8b1STomasz Duszynski ret = regulator_enable(state->vdd); 54164b3d8b1STomasz Duszynski if (ret) 54264b3d8b1STomasz Duszynski return ret; 54364b3d8b1STomasz Duszynski 54464b3d8b1STomasz Duszynski return scd30_command_write(state, CMD_START_MEAS, state->pressure_comp); 54564b3d8b1STomasz Duszynski } 54664b3d8b1STomasz Duszynski EXPORT_SYMBOL(scd30_resume); 54764b3d8b1STomasz Duszynski 54864b3d8b1STomasz Duszynski static void scd30_stop_meas(void *data) 54964b3d8b1STomasz Duszynski { 55064b3d8b1STomasz Duszynski struct scd30_state *state = data; 55164b3d8b1STomasz Duszynski 55264b3d8b1STomasz Duszynski scd30_command_write(state, CMD_STOP_MEAS, 0); 55364b3d8b1STomasz Duszynski } 55464b3d8b1STomasz Duszynski 55564b3d8b1STomasz Duszynski static void scd30_disable_regulator(void *data) 55664b3d8b1STomasz Duszynski { 55764b3d8b1STomasz Duszynski struct scd30_state *state = data; 55864b3d8b1STomasz Duszynski 55964b3d8b1STomasz Duszynski regulator_disable(state->vdd); 56064b3d8b1STomasz Duszynski } 56164b3d8b1STomasz Duszynski 56264b3d8b1STomasz Duszynski static irqreturn_t scd30_irq_handler(int irq, void *priv) 56364b3d8b1STomasz Duszynski { 56464b3d8b1STomasz Duszynski struct iio_dev *indio_dev = priv; 56564b3d8b1STomasz Duszynski 56664b3d8b1STomasz Duszynski if (iio_buffer_enabled(indio_dev)) { 56764b3d8b1STomasz Duszynski iio_trigger_poll(indio_dev->trig); 56864b3d8b1STomasz Duszynski 56964b3d8b1STomasz Duszynski return IRQ_HANDLED; 57064b3d8b1STomasz Duszynski } 57164b3d8b1STomasz Duszynski 57264b3d8b1STomasz Duszynski return IRQ_WAKE_THREAD; 57364b3d8b1STomasz Duszynski } 57464b3d8b1STomasz Duszynski 57564b3d8b1STomasz Duszynski static irqreturn_t scd30_irq_thread_handler(int irq, void *priv) 57664b3d8b1STomasz Duszynski { 57764b3d8b1STomasz Duszynski struct iio_dev *indio_dev = priv; 57864b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 57964b3d8b1STomasz Duszynski int ret; 58064b3d8b1STomasz Duszynski 58164b3d8b1STomasz Duszynski ret = scd30_read_meas(state); 58264b3d8b1STomasz Duszynski if (ret) 58364b3d8b1STomasz Duszynski goto out; 58464b3d8b1STomasz Duszynski 58564b3d8b1STomasz Duszynski complete_all(&state->meas_ready); 58664b3d8b1STomasz Duszynski out: 58764b3d8b1STomasz Duszynski return IRQ_HANDLED; 58864b3d8b1STomasz Duszynski } 58964b3d8b1STomasz Duszynski 59064b3d8b1STomasz Duszynski static irqreturn_t scd30_trigger_handler(int irq, void *p) 59164b3d8b1STomasz Duszynski { 59264b3d8b1STomasz Duszynski struct iio_poll_func *pf = p; 59364b3d8b1STomasz Duszynski struct iio_dev *indio_dev = pf->indio_dev; 59464b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 59564b3d8b1STomasz Duszynski struct { 59664b3d8b1STomasz Duszynski int data[SCD30_MEAS_COUNT]; 59764b3d8b1STomasz Duszynski s64 ts __aligned(8); 59864b3d8b1STomasz Duszynski } scan; 59964b3d8b1STomasz Duszynski int ret; 60064b3d8b1STomasz Duszynski 60164b3d8b1STomasz Duszynski mutex_lock(&state->lock); 60264b3d8b1STomasz Duszynski if (!iio_trigger_using_own(indio_dev)) 60364b3d8b1STomasz Duszynski ret = scd30_read_poll(state); 60464b3d8b1STomasz Duszynski else 60564b3d8b1STomasz Duszynski ret = scd30_read_meas(state); 60664b3d8b1STomasz Duszynski memset(&scan, 0, sizeof(scan)); 60764b3d8b1STomasz Duszynski memcpy(scan.data, state->meas, sizeof(state->meas)); 60864b3d8b1STomasz Duszynski mutex_unlock(&state->lock); 60964b3d8b1STomasz Duszynski if (ret) 61064b3d8b1STomasz Duszynski goto out; 61164b3d8b1STomasz Duszynski 61264b3d8b1STomasz Duszynski iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); 61364b3d8b1STomasz Duszynski out: 61464b3d8b1STomasz Duszynski iio_trigger_notify_done(indio_dev->trig); 61564b3d8b1STomasz Duszynski return IRQ_HANDLED; 61664b3d8b1STomasz Duszynski } 61764b3d8b1STomasz Duszynski 61864b3d8b1STomasz Duszynski static int scd30_set_trigger_state(struct iio_trigger *trig, bool state) 61964b3d8b1STomasz Duszynski { 62064b3d8b1STomasz Duszynski struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); 62164b3d8b1STomasz Duszynski struct scd30_state *st = iio_priv(indio_dev); 62264b3d8b1STomasz Duszynski 62364b3d8b1STomasz Duszynski if (state) 62464b3d8b1STomasz Duszynski enable_irq(st->irq); 62564b3d8b1STomasz Duszynski else 62664b3d8b1STomasz Duszynski disable_irq(st->irq); 62764b3d8b1STomasz Duszynski 62864b3d8b1STomasz Duszynski return 0; 62964b3d8b1STomasz Duszynski } 63064b3d8b1STomasz Duszynski 63164b3d8b1STomasz Duszynski static const struct iio_trigger_ops scd30_trigger_ops = { 63264b3d8b1STomasz Duszynski .set_trigger_state = scd30_set_trigger_state, 63364b3d8b1STomasz Duszynski .validate_device = iio_trigger_validate_own_device, 63464b3d8b1STomasz Duszynski }; 63564b3d8b1STomasz Duszynski 63664b3d8b1STomasz Duszynski static int scd30_setup_trigger(struct iio_dev *indio_dev) 63764b3d8b1STomasz Duszynski { 63864b3d8b1STomasz Duszynski struct scd30_state *state = iio_priv(indio_dev); 63964b3d8b1STomasz Duszynski struct device *dev = indio_dev->dev.parent; 64064b3d8b1STomasz Duszynski struct iio_trigger *trig; 64164b3d8b1STomasz Duszynski int ret; 64264b3d8b1STomasz Duszynski 643*15ea2878SJonathan Cameron trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, 644*15ea2878SJonathan Cameron iio_device_id(indio_dev)); 64564b3d8b1STomasz Duszynski if (!trig) { 64664b3d8b1STomasz Duszynski dev_err(dev, "failed to allocate trigger\n"); 64764b3d8b1STomasz Duszynski return -ENOMEM; 64864b3d8b1STomasz Duszynski } 64964b3d8b1STomasz Duszynski 65064b3d8b1STomasz Duszynski trig->ops = &scd30_trigger_ops; 65164b3d8b1STomasz Duszynski iio_trigger_set_drvdata(trig, indio_dev); 65264b3d8b1STomasz Duszynski 65364b3d8b1STomasz Duszynski ret = devm_iio_trigger_register(dev, trig); 65464b3d8b1STomasz Duszynski if (ret) 65564b3d8b1STomasz Duszynski return ret; 65664b3d8b1STomasz Duszynski 65764b3d8b1STomasz Duszynski indio_dev->trig = iio_trigger_get(trig); 65864b3d8b1STomasz Duszynski 659dbb8f20dSJonathan Cameron /* 660dbb8f20dSJonathan Cameron * Interrupt is enabled just before taking a fresh measurement 661dbb8f20dSJonathan Cameron * and disabled afterwards. This means we need to ensure it is not 662dbb8f20dSJonathan Cameron * enabled here to keep calls to enable/disable balanced. 663dbb8f20dSJonathan Cameron */ 66464b3d8b1STomasz Duszynski ret = devm_request_threaded_irq(dev, state->irq, scd30_irq_handler, 665dbb8f20dSJonathan Cameron scd30_irq_thread_handler, 666dbb8f20dSJonathan Cameron IRQF_TRIGGER_HIGH | IRQF_ONESHOT | 667dbb8f20dSJonathan Cameron IRQF_NO_AUTOEN, 66864b3d8b1STomasz Duszynski indio_dev->name, indio_dev); 66964b3d8b1STomasz Duszynski if (ret) 67064b3d8b1STomasz Duszynski dev_err(dev, "failed to request irq\n"); 67164b3d8b1STomasz Duszynski 67264b3d8b1STomasz Duszynski return ret; 67364b3d8b1STomasz Duszynski } 67464b3d8b1STomasz Duszynski 67564b3d8b1STomasz Duszynski int scd30_probe(struct device *dev, int irq, const char *name, void *priv, 67664b3d8b1STomasz Duszynski scd30_command_t command) 67764b3d8b1STomasz Duszynski { 67864b3d8b1STomasz Duszynski static const unsigned long scd30_scan_masks[] = { 0x07, 0x00 }; 67964b3d8b1STomasz Duszynski struct scd30_state *state; 68064b3d8b1STomasz Duszynski struct iio_dev *indio_dev; 68164b3d8b1STomasz Duszynski int ret; 68264b3d8b1STomasz Duszynski u16 val; 68364b3d8b1STomasz Duszynski 68464b3d8b1STomasz Duszynski indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); 68564b3d8b1STomasz Duszynski if (!indio_dev) 68664b3d8b1STomasz Duszynski return -ENOMEM; 68764b3d8b1STomasz Duszynski 68864b3d8b1STomasz Duszynski state = iio_priv(indio_dev); 68964b3d8b1STomasz Duszynski state->dev = dev; 69064b3d8b1STomasz Duszynski state->priv = priv; 69164b3d8b1STomasz Duszynski state->irq = irq; 69264b3d8b1STomasz Duszynski state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT; 69364b3d8b1STomasz Duszynski state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT; 69464b3d8b1STomasz Duszynski state->command = command; 69564b3d8b1STomasz Duszynski mutex_init(&state->lock); 69664b3d8b1STomasz Duszynski init_completion(&state->meas_ready); 69764b3d8b1STomasz Duszynski 69864b3d8b1STomasz Duszynski dev_set_drvdata(dev, indio_dev); 69964b3d8b1STomasz Duszynski 70064b3d8b1STomasz Duszynski indio_dev->info = &scd30_info; 70164b3d8b1STomasz Duszynski indio_dev->name = name; 70264b3d8b1STomasz Duszynski indio_dev->channels = scd30_channels; 70364b3d8b1STomasz Duszynski indio_dev->num_channels = ARRAY_SIZE(scd30_channels); 70464b3d8b1STomasz Duszynski indio_dev->modes = INDIO_DIRECT_MODE; 70564b3d8b1STomasz Duszynski indio_dev->available_scan_masks = scd30_scan_masks; 70664b3d8b1STomasz Duszynski 70764b3d8b1STomasz Duszynski state->vdd = devm_regulator_get(dev, "vdd"); 708ed175909SKrzysztof Kozlowski if (IS_ERR(state->vdd)) 709ed175909SKrzysztof Kozlowski return dev_err_probe(dev, PTR_ERR(state->vdd), "failed to get regulator\n"); 71064b3d8b1STomasz Duszynski 71164b3d8b1STomasz Duszynski ret = regulator_enable(state->vdd); 71264b3d8b1STomasz Duszynski if (ret) 71364b3d8b1STomasz Duszynski return ret; 71464b3d8b1STomasz Duszynski 71564b3d8b1STomasz Duszynski ret = devm_add_action_or_reset(dev, scd30_disable_regulator, state); 71664b3d8b1STomasz Duszynski if (ret) 71764b3d8b1STomasz Duszynski return ret; 71864b3d8b1STomasz Duszynski 71964b3d8b1STomasz Duszynski ret = scd30_reset(state); 72064b3d8b1STomasz Duszynski if (ret) { 72164b3d8b1STomasz Duszynski dev_err(dev, "failed to reset device: %d\n", ret); 72264b3d8b1STomasz Duszynski return ret; 72364b3d8b1STomasz Duszynski } 72464b3d8b1STomasz Duszynski 72564b3d8b1STomasz Duszynski if (state->irq > 0) { 72664b3d8b1STomasz Duszynski ret = scd30_setup_trigger(indio_dev); 72764b3d8b1STomasz Duszynski if (ret) { 72864b3d8b1STomasz Duszynski dev_err(dev, "failed to setup trigger: %d\n", ret); 72964b3d8b1STomasz Duszynski return ret; 73064b3d8b1STomasz Duszynski } 73164b3d8b1STomasz Duszynski } 73264b3d8b1STomasz Duszynski 73364b3d8b1STomasz Duszynski ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, scd30_trigger_handler, NULL); 73464b3d8b1STomasz Duszynski if (ret) 73564b3d8b1STomasz Duszynski return ret; 73664b3d8b1STomasz Duszynski 73764b3d8b1STomasz Duszynski ret = scd30_command_read(state, CMD_FW_VERSION, &val); 73864b3d8b1STomasz Duszynski if (ret) { 73964b3d8b1STomasz Duszynski dev_err(dev, "failed to read firmware version: %d\n", ret); 74064b3d8b1STomasz Duszynski return ret; 74164b3d8b1STomasz Duszynski } 74264b3d8b1STomasz Duszynski dev_info(dev, "firmware version: %d.%d\n", val >> 8, (char)val); 74364b3d8b1STomasz Duszynski 74464b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_MEAS_INTERVAL, state->meas_interval); 74564b3d8b1STomasz Duszynski if (ret) { 74664b3d8b1STomasz Duszynski dev_err(dev, "failed to set measurement interval: %d\n", ret); 74764b3d8b1STomasz Duszynski return ret; 74864b3d8b1STomasz Duszynski } 74964b3d8b1STomasz Duszynski 75064b3d8b1STomasz Duszynski ret = scd30_command_write(state, CMD_START_MEAS, state->pressure_comp); 75164b3d8b1STomasz Duszynski if (ret) { 75264b3d8b1STomasz Duszynski dev_err(dev, "failed to start measurement: %d\n", ret); 75364b3d8b1STomasz Duszynski return ret; 75464b3d8b1STomasz Duszynski } 75564b3d8b1STomasz Duszynski 75664b3d8b1STomasz Duszynski ret = devm_add_action_or_reset(dev, scd30_stop_meas, state); 75764b3d8b1STomasz Duszynski if (ret) 75864b3d8b1STomasz Duszynski return ret; 75964b3d8b1STomasz Duszynski 76064b3d8b1STomasz Duszynski return devm_iio_device_register(dev, indio_dev); 76164b3d8b1STomasz Duszynski } 76264b3d8b1STomasz Duszynski EXPORT_SYMBOL(scd30_probe); 76364b3d8b1STomasz Duszynski 76464b3d8b1STomasz Duszynski MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); 76564b3d8b1STomasz Duszynski MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver"); 76664b3d8b1STomasz Duszynski MODULE_LICENSE("GPL v2"); 767