diff options
Diffstat (limited to 'drivers/iio/chemical/ccs811.c')
| -rw-r--r-- | drivers/iio/chemical/ccs811.c | 118 |
1 files changed, 63 insertions, 55 deletions
diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index 3ecd633f9ed3..998c9239c4c7 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -15,6 +15,7 @@ * 4. Read error register and put the information in logs */ +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -78,6 +79,11 @@ struct ccs811_data { struct iio_trigger *drdy_trig; struct gpio_desc *wakeup_gpio; bool drdy_trig_on; + /* Ensures correct alignment of timestamp if present */ + struct { + s16 channels[2]; + aligned_s64 ts; + } scan; }; static const struct iio_chan_spec ccs811_channels[] = { @@ -209,6 +215,40 @@ static int ccs811_get_measurement(struct ccs811_data *data) return ret; } +static int ccs811_read_info_raw(struct ccs811_data *data, + struct iio_chan_spec const *chan, + int *val, int mask) +{ + int ret; + + guard(mutex)(&data->lock); + ret = ccs811_get_measurement(data); + if (ret < 0) + return ret; + + switch (chan->type) { + case IIO_VOLTAGE: + *val = be16_to_cpu(data->buffer.raw_data) & CCS811_VOLTAGE_MASK; + return IIO_VAL_INT; + case IIO_CURRENT: + *val = be16_to_cpu(data->buffer.raw_data) >> 10; + return IIO_VAL_INT; + case IIO_CONCENTRATION: + switch (chan->channel2) { + case IIO_MOD_CO2: + *val = be16_to_cpu(data->buffer.co2); + return IIO_VAL_INT; + case IIO_MOD_VOC: + *val = be16_to_cpu(data->buffer.voc); + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + static int ccs811_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -218,46 +258,12 @@ static int ccs811_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - mutex_lock(&data->lock); - ret = ccs811_get_measurement(data); - if (ret < 0) { - mutex_unlock(&data->lock); - iio_device_release_direct_mode(indio_dev); - return ret; - } + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - switch (chan->type) { - case IIO_VOLTAGE: - *val = be16_to_cpu(data->buffer.raw_data) & - CCS811_VOLTAGE_MASK; - ret = IIO_VAL_INT; - break; - case IIO_CURRENT: - *val = be16_to_cpu(data->buffer.raw_data) >> 10; - ret = IIO_VAL_INT; - break; - case IIO_CONCENTRATION: - switch (chan->channel2) { - case IIO_MOD_CO2: - *val = be16_to_cpu(data->buffer.co2); - ret = IIO_VAL_INT; - break; - case IIO_MOD_VOC: - *val = be16_to_cpu(data->buffer.voc); - ret = IIO_VAL_INT; - break; - default: - ret = -EINVAL; - } - break; - default: - ret = -EINVAL; - } - mutex_unlock(&data->lock); - iio_device_release_direct_mode(indio_dev); + ret = ccs811_read_info_raw(data, chan, val, mask); + + iio_device_release_direct(indio_dev); return ret; @@ -327,18 +333,18 @@ static irqreturn_t ccs811_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct ccs811_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; - s16 buf[8]; /* s16 eCO2 + s16 TVOC + padding + 8 byte timestamp */ int ret; - ret = i2c_smbus_read_i2c_block_data(client, CCS811_ALG_RESULT_DATA, 4, - (u8 *)&buf); + ret = i2c_smbus_read_i2c_block_data(client, CCS811_ALG_RESULT_DATA, + sizeof(data->scan.channels), + (u8 *)data->scan.channels); if (ret != 4) { dev_err(&client->dev, "cannot read sensor data\n"); goto err; } - iio_push_to_buffers_with_timestamp(indio_dev, buf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); @@ -396,9 +402,9 @@ static int ccs811_reset(struct i2c_client *client) return 0; } -static int ccs811_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ccs811_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct iio_dev *indio_dev; struct ccs811_data *data; int ret; @@ -464,7 +470,6 @@ static int ccs811_probe(struct i2c_client *client, mutex_init(&data->lock); - indio_dev->dev.parent = &client->dev; indio_dev->name = id->name; indio_dev->info = &ccs811_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -487,20 +492,19 @@ static int ccs811_probe(struct i2c_client *client, data->drdy_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->drdy_trig) { ret = -ENOMEM; goto err_poweroff; } - data->drdy_trig->dev.parent = &client->dev; data->drdy_trig->ops = &ccs811_trigger_ops; iio_trigger_set_drvdata(data->drdy_trig, indio_dev); - indio_dev->trig = data->drdy_trig; - iio_trigger_get(indio_dev->trig); ret = iio_trigger_register(data->drdy_trig); if (ret) goto err_poweroff; + + indio_dev->trig = iio_trigger_get(data->drdy_trig); } ret = iio_triggered_buffer_setup(indio_dev, NULL, @@ -529,22 +533,26 @@ err_poweroff: return ret; } -static int ccs811_remove(struct i2c_client *client) +static void ccs811_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct ccs811_data *data = iio_priv(indio_dev); + int ret; iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); if (data->drdy_trig) iio_trigger_unregister(data->drdy_trig); - return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, - CCS811_MODE_IDLE); + ret = i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, + CCS811_MODE_IDLE); + if (ret) + dev_warn(&client->dev, "Failed to power down device (%pe)\n", + ERR_PTR(ret)); } static const struct i2c_device_id ccs811_id[] = { - {"ccs811", 0}, + { "ccs811" }, { } }; MODULE_DEVICE_TABLE(i2c, ccs811_id); |
