From f339f979bb333ed51ff2d700ccd01bc20e9a9b98 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 13 Nov 2019 11:24:53 +0200 Subject: iio: buffer-dmaengine: Use dma_request_chan() directly for channel request dma_request_slave_channel_reason() is: dma_request_chan(dev, name) Signed-off-by: Peter Ujfalusi Reviewed-by: Vinod Koul Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index bea4a75e92f1..b7b5a934e9b2 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -150,7 +150,7 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, if (!dmaengine_buffer) return ERR_PTR(-ENOMEM); - chan = dma_request_slave_channel_reason(dev, channel); + chan = dma_request_chan(dev, channel); if (IS_ERR(chan)) { ret = PTR_ERR(chan); goto err_free; -- cgit v1.2.3 From ca69300173b642ba64118200172171ea5967b6c5 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 15 Nov 2019 15:57:20 +0200 Subject: iio: adc: Add support for AD7091R5 ADC AD7091R5 is 4-Channel, I2C, Ultra Low Power,12-Bit ADC. This driver will also support AD7091R2/4/8 in the future. Datasheet: Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ad7091r-5.pdf Signed-off-by: Paul Cercueil Co-developed-by: Beniamin Bia Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 7 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7091r-base.c | 260 +++++++++++++++++++++++++++++++++++++++++ drivers/iio/adc/ad7091r-base.h | 25 ++++ drivers/iio/adc/ad7091r5.c | 108 +++++++++++++++++ 5 files changed, 401 insertions(+) create mode 100644 drivers/iio/adc/ad7091r-base.c create mode 100644 drivers/iio/adc/ad7091r-base.h create mode 100644 drivers/iio/adc/ad7091r5.c (limited to 'drivers') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 5d8540b7b427..976567d4dbef 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -32,6 +32,13 @@ config AD7124 To compile this driver as a module, choose M here: the module will be called ad7124. +config AD7091R5 + tristate "Analog Devices AD7091R5 ADC Driver" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for Analog Devices AD7091R-5 ADC. + config AD7266 tristate "Analog Devices AD7265/AD7266 ADC driver" depends on SPI_MASTER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index a1f1fbec0f87..84936ec24411 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7124) += ad7124.o +obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7292) += ad7292.o diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c new file mode 100644 index 000000000000..854de7c654c2 --- /dev/null +++ b/drivers/iio/adc/ad7091r-base.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7091RX Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "ad7091r-base.h" + +#define AD7091R_REG_RESULT 0 +#define AD7091R_REG_CHANNEL 1 +#define AD7091R_REG_CONF 2 +#define AD7091R_REG_ALERT 3 +#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4) +#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) +#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) + +/* AD7091R_REG_RESULT */ +#define AD7091R_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) +#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) + +/* AD7091R_REG_CONF */ +#define AD7091R_REG_CONF_AUTO BIT(8) +#define AD7091R_REG_CONF_CMD BIT(10) + +#define AD7091R_REG_CONF_MODE_MASK \ + (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD) + +enum ad7091r_mode { + AD7091R_MODE_SAMPLE, + AD7091R_MODE_COMMAND, + AD7091R_MODE_AUTOCYCLE, +}; + +struct ad7091r_state { + struct device *dev; + struct regmap *map; + const struct ad7091r_chip_info *chip_info; + enum ad7091r_mode mode; + struct mutex lock; /*lock to prevent concurent reads */ +}; + +static int ad7091r_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) +{ + int ret, conf; + + switch (mode) { + case AD7091R_MODE_SAMPLE: + conf = 0; + break; + case AD7091R_MODE_COMMAND: + conf = AD7091R_REG_CONF_CMD; + break; + case AD7091R_MODE_AUTOCYCLE: + conf = AD7091R_REG_CONF_AUTO; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_MODE_MASK, conf); + if (ret) + return ret; + + st->mode = mode; + + return 0; +} + +static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel) +{ + unsigned int dummy; + int ret; + + /* AD7091R_REG_CHANNEL specified which channels to be converted */ + ret = regmap_write(st->map, AD7091R_REG_CHANNEL, + BIT(channel) | (BIT(channel) << 8)); + if (ret) + return ret; + + /* + * There is a latency of one conversion before the channel conversion + * sequence is updated + */ + return regmap_read(st->map, AD7091R_REG_RESULT, &dummy); +} + +static int ad7091r_read_one(struct iio_dev *iio_dev, + unsigned int channel, unsigned int *read_val) +{ + struct ad7091r_state *st = iio_priv(iio_dev); + unsigned int val; + int ret; + + ret = ad7091r_set_channel(st, channel); + if (ret) + return ret; + + ret = regmap_read(st->map, AD7091R_REG_RESULT, &val); + if (ret) + return ret; + + if (AD7091R_REG_RESULT_CH_ID(val) != channel) + return -EIO; + + *read_val = AD7091R_REG_RESULT_CONV_RESULT(val); + + return 0; +} + +static int ad7091r_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad7091r_state *st = iio_priv(iio_dev); + unsigned int read_val; + int ret; + + mutex_lock(&st->lock); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (st->mode != AD7091R_MODE_COMMAND) { + ret = -EBUSY; + goto unlock; + } + + ret = ad7091r_read_one(iio_dev, chan->channel, &read_val); + if (ret) + goto unlock; + + *val = read_val; + ret = IIO_VAL_INT; + break; + + default: + ret = -EINVAL; + break; + } + +unlock: + mutex_unlock(&st->lock); + return ret; +} + +static const struct iio_info ad7091r_info = { + .read_raw = ad7091r_read_raw, +}; + +static irqreturn_t ad7091r_event_handler(int irq, void *private) +{ + struct ad7091r_state *st = (struct ad7091r_state *) private; + struct iio_dev *iio_dev = dev_get_drvdata(st->dev); + unsigned int i, read_val; + int ret; + s64 timestamp = iio_get_time_ns(iio_dev); + + ret = regmap_read(st->map, AD7091R_REG_ALERT, &read_val); + if (ret) + return IRQ_HANDLED; + + for (i = 0; i < st->chip_info->num_channels; i++) { + if (read_val & BIT(i * 2)) + iio_push_event(iio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), timestamp); + if (read_val & BIT(i * 2 + 1)) + iio_push_event(iio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), timestamp); + } + + return IRQ_HANDLED; +} + +int ad7091r_probe(struct device *dev, const char *name, + const struct ad7091r_chip_info *chip_info, + struct regmap *map, int irq) +{ + struct iio_dev *iio_dev; + struct ad7091r_state *st; + int ret; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!iio_dev) + return -ENOMEM; + + st = iio_priv(iio_dev); + st->dev = dev; + st->chip_info = chip_info; + st->map = map; + + iio_dev->dev.parent = dev; + iio_dev->name = name; + iio_dev->info = &ad7091r_info; + iio_dev->modes = INDIO_DIRECT_MODE; + + iio_dev->num_channels = chip_info->num_channels; + iio_dev->channels = chip_info->channels; + + if (irq) { + ret = devm_request_threaded_irq(dev, irq, NULL, + ad7091r_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, st); + if (ret) + return ret; + } + + /* Use command mode by default to convert only desired channels*/ + ret = ad7091r_set_mode(st, AD7091R_MODE_COMMAND); + if (ret) + return ret; + + return devm_iio_device_register(dev, iio_dev); +} +EXPORT_SYMBOL_GPL(ad7091r_probe); + +static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD7091R_REG_RESULT: + case AD7091R_REG_ALERT: + return false; + default: + return true; + } +} + +static bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD7091R_REG_RESULT: + case AD7091R_REG_ALERT: + return true; + default: + return false; + } +} + +const struct regmap_config ad7091r_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .writeable_reg = ad7091r_writeable_reg, + .volatile_reg = ad7091r_volatile_reg, +}; +EXPORT_SYMBOL_GPL(ad7091r_regmap_config); + +MODULE_AUTHOR("Beniamin Bia "); +MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h new file mode 100644 index 000000000000..b0b4fe01a681 --- /dev/null +++ b/drivers/iio/adc/ad7091r-base.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AD7091RX Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__ +#define __DRIVERS_IIO_ADC_AD7091R_BASE_H__ + +struct device; +struct ad7091r_state; + +struct ad7091r_chip_info { + unsigned int num_channels; + const struct iio_chan_spec *channels; +}; + +extern const struct regmap_config ad7091r_regmap_config; + +int ad7091r_probe(struct device *dev, const char *name, + const struct ad7091r_chip_info *chip_info, + struct regmap *map, int irq); + +#endif /* __DRIVERS_IIO_ADC_AD7091R_BASE_H__ */ diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c new file mode 100644 index 000000000000..30ff0108a6ed --- /dev/null +++ b/drivers/iio/adc/ad7091r5.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7091R5 Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#include +#include +#include +#include + +#include "ad7091r-base.h" + +static const struct iio_event_spec ad7091r5_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .channel = idx, \ + .event_spec = ev, \ + .num_event_specs = num_ev, \ +} +static const struct iio_chan_spec ad7091r5_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), + AD7091R_CHANNEL(1, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), + AD7091R_CHANNEL(2, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), + AD7091R_CHANNEL(3, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), +}; + +static const struct iio_chan_spec ad7091r5_channels_noirq[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), +}; + +static const struct ad7091r_chip_info ad7091r5_chip_info_irq = { + .channels = ad7091r5_channels_irq, + .num_channels = ARRAY_SIZE(ad7091r5_channels_irq), +}; + +static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { + .channels = ad7091r5_channels_noirq, + .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq), +}; + +static int ad7091r5_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + const struct ad7091r_chip_info *chip_info; + struct regmap *map = devm_regmap_init_i2c(i2c, &ad7091r_regmap_config); + + if (IS_ERR(map)) + return PTR_ERR(map); + + if (i2c->irq) + chip_info = &ad7091r5_chip_info_irq; + else + chip_info = &ad7091r5_chip_info_noirq; + + return ad7091r_probe(&i2c->dev, id->name, chip_info, map, i2c->irq); +} + +static const struct of_device_id ad7091r5_dt_ids[] = { + { .compatible = "adi,ad7091r5" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids); + +static const struct i2c_device_id ad7091r5_i2c_ids[] = { + {"ad7091r5", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids); + +static struct i2c_driver ad7091r5_driver = { + .driver = { + .name = "ad7091r5", + .of_match_table = ad7091r5_dt_ids, + }, + .probe = ad7091r5_i2c_probe, + .id_table = ad7091r5_i2c_ids, +}; +module_i2c_driver(ad7091r5_driver); + +MODULE_AUTHOR("Beniamin Bia "); +MODULE_DESCRIPTION("Analog Devices AD7091R5 multi-channel ADC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 260442cc5be4e652628f01ea6ac58f542e2c8e7a Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 15 Nov 2019 15:57:21 +0200 Subject: iio: adc: ad7091r5: Add scale and external VREF support The scale can now be obtained with the "in_voltage_scale" file. By default, the scale returned corresponds to the internal VREF of 2.5V. It is possible to use an external VREF (through the REFIN/REFOUT pin of the chip), by passing a regulator to the driver. The scale will then be calculated according to the voltage reported by the regulator. Signed-off-by: Paul Cercueil Co-developed-by: Beniamin Bia Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r-base.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/iio/adc/ad7091r-base.h | 1 + drivers/iio/adc/ad7091r5.c | 5 +++++ 3 files changed, 44 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 854de7c654c2..33c40357bd5e 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "ad7091r-base.h" @@ -42,6 +43,7 @@ enum ad7091r_mode { struct ad7091r_state { struct device *dev; struct regmap *map; + struct regulator *vref; const struct ad7091r_chip_info *chip_info; enum ad7091r_mode mode; struct mutex lock; /*lock to prevent concurent reads */ @@ -141,6 +143,21 @@ static int ad7091r_read_raw(struct iio_dev *iio_dev, ret = IIO_VAL_INT; break; + case IIO_CHAN_INFO_SCALE: + if (st->vref) { + ret = regulator_get_voltage(st->vref); + if (ret < 0) + goto unlock; + + *val = ret / 1000; + } else { + *val = st->chip_info->vref_mV; + } + + *val2 = chan->scan_type.realbits; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + default: ret = -EINVAL; break; @@ -183,6 +200,13 @@ static irqreturn_t ad7091r_event_handler(int irq, void *private) return IRQ_HANDLED; } +static void ad7091r_remove(void *data) +{ + struct ad7091r_state *st = data; + + regulator_disable(st->vref); +} + int ad7091r_probe(struct device *dev, const char *name, const struct ad7091r_chip_info *chip_info, struct regmap *map, int irq) @@ -216,6 +240,20 @@ int ad7091r_probe(struct device *dev, const char *name, return ret; } + st->vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(st->vref)) { + if (PTR_ERR(st->vref) == -EPROBE_DEFER) + return -EPROBE_DEFER; + st->vref = NULL; + } else { + ret = regulator_enable(st->vref); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, ad7091r_remove, st); + if (ret) + return ret; + } + /* Use command mode by default to convert only desired channels*/ ret = ad7091r_set_mode(st, AD7091R_MODE_COMMAND); if (ret) diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index b0b4fe01a681..509748aef9b1 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -14,6 +14,7 @@ struct ad7091r_state; struct ad7091r_chip_info { unsigned int num_channels; const struct iio_chan_spec *channels; + unsigned int vref_mV; }; extern const struct regmap_config ad7091r_regmap_config; diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index 30ff0108a6ed..9665679c3ea6 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -35,10 +35,13 @@ static const struct iio_event_spec ad7091r5_events[] = { #define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ .type = IIO_VOLTAGE, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .indexed = 1, \ .channel = idx, \ .event_spec = ev, \ .num_event_specs = num_ev, \ + .scan_type.storagebits = 16, \ + .scan_type.realbits = bits, \ } static const struct iio_chan_spec ad7091r5_channels_irq[] = { AD7091R_CHANNEL(0, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), @@ -57,11 +60,13 @@ static const struct iio_chan_spec ad7091r5_channels_noirq[] = { static const struct ad7091r_chip_info ad7091r5_chip_info_irq = { .channels = ad7091r5_channels_irq, .num_channels = ARRAY_SIZE(ad7091r5_channels_irq), + .vref_mV = 2500, }; static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { .channels = ad7091r5_channels_noirq, .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq), + .vref_mV = 2500, }; static int ad7091r5_i2c_probe(struct i2c_client *i2c, -- cgit v1.2.3 From b95ed406278f7588a5504ef94cb526bb8d7f70ff Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Fri, 15 Nov 2019 15:06:21 +0100 Subject: iio: imu: inv_mpu6050: delete not existing MPU9150 spi support MPU9150 is i2c only. Update Kconfig to delete in description chips that are i2c or spi only. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 6 +++--- drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index e4c4c12236a7..963a0cafe0cb 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -14,7 +14,7 @@ config INV_MPU6050_I2C select INV_MPU6050_IIO select REGMAP_I2C help - This driver supports the Invensense MPU6000/6050/6500/6515, + This driver supports the Invensense MPU6050/6500/6515, MPU9150/9250/9255 and ICM20608/20602 motion tracking devices over I2C. This driver can be built as a module. The module will be called @@ -26,8 +26,8 @@ config INV_MPU6050_SPI select INV_MPU6050_IIO select REGMAP_SPI help - This driver supports the Invensense MPU6000/6050/6500/6515, - MPU9150/9250/9255 and ICM20608/20602 motion tracking devices + This driver supports the Invensense MPU6000/6500/6515, + MPU9250/9255 and ICM20608/20602 motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 142692fc0758..ec102d5a5c77 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -74,7 +74,6 @@ static int inv_mpu_probe(struct spi_device *spi) static const struct spi_device_id inv_mpu_id[] = { {"mpu6000", INV_MPU6000}, {"mpu6500", INV_MPU6500}, - {"mpu9150", INV_MPU9150}, {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, -- cgit v1.2.3 From a2587eb032f1679dd695b9e5113bcb6b52e46e0a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Fri, 15 Nov 2019 15:06:22 +0100 Subject: iio: imu: inv_mpu6050: add support of MPU9150 magnetometer Add support for driving MPU9150 magnetometer (AK8975) from mpu. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 57 ++++++++++++++----- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 2 + drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 80 +++++++++++++++++++-------- drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c | 1 + 4 files changed, 104 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 45e77b308238..23c0557891a0 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -914,6 +914,33 @@ static const unsigned long inv_mpu_scan_masks[] = { .ext_info = inv_ext_info, \ } +static const struct iio_chan_spec inv_mpu9150_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP), + /* + * Note that temperature should only be via polled reading only, + * not the final scan elements output. + */ + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_OFFSET) + | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = -1, + }, + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z), + + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), + + /* Magnetometer resolution is 13 bits */ + INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 13, INV_MPU9X50_SCAN_MAGN_X), + INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 13, INV_MPU9X50_SCAN_MAGN_Y), + INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 13, INV_MPU9X50_SCAN_MAGN_Z), +}; + static const struct iio_chan_spec inv_mpu9250_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP), /* @@ -1323,21 +1350,16 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, inv_mpu_bus_setup(indio_dev); switch (chip_type) { + case INV_MPU9150: + indio_dev->channels = inv_mpu9150_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu9150_channels); + indio_dev->available_scan_masks = inv_mpu9x50_scan_masks; + break; case INV_MPU9250: case INV_MPU9255: - /* - * Use magnetometer inside the chip only if there is no i2c - * auxiliary device in use. - */ - if (!st->magn_disabled) { - indio_dev->channels = inv_mpu9250_channels; - indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels); - indio_dev->available_scan_masks = inv_mpu9x50_scan_masks; - } else { - indio_dev->channels = inv_mpu_channels; - indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); - indio_dev->available_scan_masks = inv_mpu_scan_masks; - } + indio_dev->channels = inv_mpu9250_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels); + indio_dev->available_scan_masks = inv_mpu9x50_scan_masks; break; case INV_ICM20602: indio_dev->channels = inv_icm20602_channels; @@ -1350,6 +1372,15 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, indio_dev->available_scan_masks = inv_mpu_scan_masks; break; } + /* + * Use magnetometer inside the chip only if there is no i2c + * auxiliary device in use. Otherwise Going back to 6-axis only. + */ + if (st->magn_disabled) { + indio_dev->channels = inv_mpu_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); + indio_dev->available_scan_masks = inv_mpu_scan_masks; + } indio_dev->info = &mpu_info; indio_dev->modes = INDIO_BUFFER_TRIGGERED; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 389cc8505e0e..f47a28b4be23 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -77,6 +77,7 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev) case INV_ICM20602: /* no i2c auxiliary bus on the chip */ return false; + case INV_MPU9150: case INV_MPU9250: case INV_MPU9255: if (st->magn_disabled) @@ -102,6 +103,7 @@ static int inv_mpu_magn_disable(struct iio_dev *indio_dev) struct device_node *mux_node; switch (st->chip_type) { + case INV_MPU9150: case INV_MPU9250: case INV_MPU9255: mux_node = of_get_child_by_name(dev->of_node, "i2c-gate"); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 02735af152c8..4f192352521e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -12,7 +12,9 @@ #include "inv_mpu_magn.h" /* - * MPU9250 magnetometer is an AKM AK8963 chip on I2C aux bus + * MPU9xxx magnetometer are AKM chips on I2C aux bus + * MPU9150 is AK8975 + * MPU9250 is AK8963 */ #define INV_MPU_MAGN_I2C_ADDR 0x0C @@ -33,10 +35,10 @@ #define INV_MPU_MAGN_BITS_MODE_PWDN 0x00 #define INV_MPU_MAGN_BITS_MODE_SINGLE 0x01 #define INV_MPU_MAGN_BITS_MODE_FUSE 0x0F -#define INV_MPU_MAGN_BIT_OUTPUT_BIT 0x10 +#define INV_MPU9250_MAGN_BIT_OUTPUT_BIT 0x10 -#define INV_MPU_MAGN_REG_CNTL2 0x0B -#define INV_MPU_MAGN_BIT_SRST 0x01 +#define INV_MPU9250_MAGN_REG_CNTL2 0x0B +#define INV_MPU9250_MAGN_BIT_SRST 0x01 #define INV_MPU_MAGN_REG_ASAX 0x10 #define INV_MPU_MAGN_REG_ASAY 0x11 @@ -48,6 +50,7 @@ static bool inv_magn_supported(const struct inv_mpu6050_state *st) { switch (st->chip_type) { + case INV_MPU9150: case INV_MPU9250: case INV_MPU9255: return true; @@ -61,6 +64,7 @@ static int inv_magn_init(struct inv_mpu6050_state *st) { uint8_t val; uint8_t asa[3]; + int32_t sensitivity; int ret; /* check whoami */ @@ -71,12 +75,19 @@ static int inv_magn_init(struct inv_mpu6050_state *st) if (val != INV_MPU_MAGN_BITS_WIA) return -ENODEV; - /* reset chip */ - ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, - INV_MPU_MAGN_REG_CNTL2, - INV_MPU_MAGN_BIT_SRST); - if (ret) - return ret; + /* software reset for MPU925x only */ + switch (st->chip_type) { + case INV_MPU9250: + case INV_MPU9255: + ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, + INV_MPU9250_MAGN_REG_CNTL2, + INV_MPU9250_MAGN_BIT_SRST); + if (ret) + return ret; + break; + default: + break; + } /* read fuse ROM data */ ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, @@ -97,6 +108,25 @@ static int inv_magn_init(struct inv_mpu6050_state *st) if (ret) return ret; + /* + * Sensor sentivity + * 1 uT = 0.01 G and value is in micron (1e6) + * sensitvity = x uT * 0.01 * 1e6 + */ + switch (st->chip_type) { + case INV_MPU9150: + /* sensor sensitivity is 0.3 uT */ + sensitivity = 3000; + break; + case INV_MPU9250: + case INV_MPU9255: + /* sensor sensitivity in 16 bits mode: 0.15 uT */ + sensitivity = 1500; + break; + default: + return -EINVAL; + } + /* * Sensitivity adjustement and scale to Gauss * @@ -104,16 +134,11 @@ static int inv_magn_init(struct inv_mpu6050_state *st) * Factor simplification: * Hadj = H * ((ASA + 128) / 256) * - * Sensor sentivity - * 0.15 uT in 16 bits mode - * 1 uT = 0.01 G and value is in micron (1e6) - * sensitvity = 0.15 uT * 0.01 * 1e6 - * - * raw_to_gauss = Hadj * 1500 + * raw_to_gauss = Hadj * sensitivity */ - st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 128) * 1500) / 256; - st->magn_raw_to_gauss[1] = (((int32_t)asa[1] + 128) * 1500) / 256; - st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * 1500) / 256; + st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 128) * sensitivity) / 256; + st->magn_raw_to_gauss[1] = (((int32_t)asa[1] + 128) * sensitivity) / 256; + st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * sensitivity) / 256; return 0; } @@ -129,6 +154,7 @@ static int inv_magn_init(struct inv_mpu6050_state *st) */ int inv_mpu_magn_probe(struct inv_mpu6050_state *st) { + uint8_t val; int ret; /* quit if chip is not supported */ @@ -179,10 +205,17 @@ int inv_mpu_magn_probe(struct inv_mpu6050_state *st) if (ret) return ret; - /* add 16 bits mode */ - ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1), - INV_MPU_MAGN_BITS_MODE_SINGLE | - INV_MPU_MAGN_BIT_OUTPUT_BIT); + /* add 16 bits mode for MPU925x */ + val = INV_MPU_MAGN_BITS_MODE_SINGLE; + switch (st->chip_type) { + case INV_MPU9250: + case INV_MPU9255: + val |= INV_MPU9250_MAGN_BIT_OUTPUT_BIT; + break; + default: + break; + } + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1), val); if (ret) return ret; @@ -237,6 +270,7 @@ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) /* fill magnetometer orientation */ switch (st->chip_type) { + case INV_MPU9150: case INV_MPU9250: case INV_MPU9255: /* x <- y */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index d7d951927a44..a9c75bc62f18 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -50,6 +50,7 @@ static void inv_scan_query(struct iio_dev *indio_dev) struct inv_mpu6050_state *st = iio_priv(indio_dev); switch (st->chip_type) { + case INV_MPU9150: case INV_MPU9250: case INV_MPU9255: return inv_scan_query_mpu9x50(indio_dev); -- cgit v1.2.3 From a33db9475a3c7b6855c67d144dfa93a623b66cec Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 13 Nov 2019 11:09:38 +0100 Subject: iio: adc: max9611: Make enum relations more future proof MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The relations between enum values and array indices values are currently not enforced by the code, which makes them fragile w.r.t. future changes. Fix this by: 1. Using designated array initializers, to make sure array indices and enums values match, 2. Linking max9611_csa_gain enum values to the corresponding max9611_conf_ids enum values, as the latter is cast to the former in max9611_read_csa_voltage(). No change in generated code. Signed-off-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Acked-by: Jacopo Mondi Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max9611.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index da073d72f649..bf76dfb3f2c9 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -109,22 +109,17 @@ enum max9611_conf_ids { * where data shall be read from */ static const unsigned int max9611_mux_conf[][2] = { - /* CONF_SENSE_1x */ - { MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA }, - /* CONF_SENSE_4x */ - { MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA }, - /* CONF_SENSE_8x */ - { MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA }, - /* CONF_IN_VOLT */ - { MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA }, - /* CONF_TEMP */ - { MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA }, + [CONF_SENSE_1x] = { MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA }, + [CONF_SENSE_4x] = { MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA }, + [CONF_SENSE_8x] = { MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA }, + [CONF_IN_VOLT] = { MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA }, + [CONF_TEMP] = { MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA }, }; enum max9611_csa_gain { - CSA_GAIN_1x, - CSA_GAIN_4x, - CSA_GAIN_8x, + CSA_GAIN_1x = CONF_SENSE_1x, + CSA_GAIN_4x = CONF_SENSE_4x, + CSA_GAIN_8x = CONF_SENSE_8x, }; enum max9611_csa_gain_params { @@ -142,18 +137,9 @@ enum max9611_csa_gain_params { * value; use this structure to retrieve the correct LSB and offset values. */ static const unsigned int max9611_gain_conf[][2] = { - { /* [0] CSA_GAIN_1x */ - MAX9611_CSA_1X_LSB_nV, - MAX9611_CSA_1X_OFFS_RAW, - }, - { /* [1] CSA_GAIN_4x */ - MAX9611_CSA_4X_LSB_nV, - MAX9611_CSA_4X_OFFS_RAW, - }, - { /* [2] CSA_GAIN_8x */ - MAX9611_CSA_8X_LSB_nV, - MAX9611_CSA_8X_OFFS_RAW, - }, + [CSA_GAIN_1x] = { MAX9611_CSA_1X_LSB_nV, MAX9611_CSA_1X_OFFS_RAW, }, + [CSA_GAIN_4x] = { MAX9611_CSA_4X_LSB_nV, MAX9611_CSA_4X_OFFS_RAW, }, + [CSA_GAIN_8x] = { MAX9611_CSA_8X_LSB_nV, MAX9611_CSA_8X_OFFS_RAW, }, }; enum max9611_chan_addrs { -- cgit v1.2.3 From 6a9afcb198b4de323485c0ee94ff09f653012d4b Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:11 +0200 Subject: iio: imu: adis: rename txrx_lock -> state_lock The lock can be extended a bit to protect other elements that are not particular to just TX/RX. Another idea would have been to just add a new `state_lock`, but that would mean 2 locks which would be redundant, and probably cause more potential for dead-locks. What will be done in the next patches, will be to add some unlocked versions for read/write_reg functions. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 10 +++++----- drivers/iio/imu/adis_buffer.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 85de565a4e80..db562fec79b2 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -70,7 +70,7 @@ int adis_write_reg(struct adis *adis, unsigned int reg, }, }; - mutex_lock(&adis->txrx_lock); + mutex_lock(&adis->state_lock); spi_message_init(&msg); @@ -114,7 +114,7 @@ int adis_write_reg(struct adis *adis, unsigned int reg, } out_unlock: - mutex_unlock(&adis->txrx_lock); + mutex_unlock(&adis->state_lock); return ret; } @@ -166,7 +166,7 @@ int adis_read_reg(struct adis *adis, unsigned int reg, }, }; - mutex_lock(&adis->txrx_lock); + mutex_lock(&adis->state_lock); spi_message_init(&msg); if (adis->current_page != page) { @@ -211,7 +211,7 @@ int adis_read_reg(struct adis *adis, unsigned int reg, } out_unlock: - mutex_unlock(&adis->txrx_lock); + mutex_unlock(&adis->state_lock); return ret; } @@ -438,7 +438,7 @@ EXPORT_SYMBOL_GPL(adis_single_conversion); int adis_init(struct adis *adis, struct iio_dev *indio_dev, struct spi_device *spi, const struct adis_data *data) { - mutex_init(&adis->txrx_lock); + mutex_init(&adis->state_lock); adis->spi = spi; adis->data = data; iio_device_set_drvdata(indio_dev, adis); diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 4998a89d083d..3f4dd5c00b03 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -129,7 +129,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p) return -ENOMEM; if (adis->data->has_paging) { - mutex_lock(&adis->txrx_lock); + mutex_lock(&adis->state_lock); if (adis->current_page != 0) { adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); adis->tx[1] = 0; @@ -144,7 +144,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p) if (adis->data->has_paging) { adis->current_page = 0; - mutex_unlock(&adis->txrx_lock); + mutex_unlock(&adis->state_lock); } iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, -- cgit v1.2.3 From 770d465619423a946248018ea39ed9a74c8f2ff4 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:12 +0200 Subject: iio: imu: adis: add unlocked read/write function versions This will allow more flexible control to group reads & writes into a single lock (particularly the state_lock). The end-goal is to remove the indio_dev->mlock usage, and the simplest fix would have been to just add another lock, which would not be a good idea on the long-run. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index db562fec79b2..2f518e6c727d 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -26,7 +26,14 @@ #define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0) #define ADIS_GLOB_CMD_SW_RESET BIT(7) -int adis_write_reg(struct adis *adis, unsigned int reg, +/** + * __adis_write_reg() - write N bytes to register (unlocked version) + * @adis: The adis device + * @reg: The address of the lower of the two registers + * @value: The value to write to device (up to 4 bytes) + * @size: The size of the @value (in bytes) + */ +int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value, unsigned int size) { unsigned int page = reg / ADIS_PAGE_SIZE; @@ -70,8 +77,6 @@ int adis_write_reg(struct adis *adis, unsigned int reg, }, }; - mutex_lock(&adis->state_lock); - spi_message_init(&msg); if (adis->current_page != page) { @@ -96,8 +101,7 @@ int adis_write_reg(struct adis *adis, unsigned int reg, adis->tx[3] = value & 0xff; break; default: - ret = -EINVAL; - goto out_unlock; + return -EINVAL; } xfers[size].cs_change = 0; @@ -113,20 +117,18 @@ int adis_write_reg(struct adis *adis, unsigned int reg, adis->current_page = page; } -out_unlock: - mutex_unlock(&adis->state_lock); - return ret; } -EXPORT_SYMBOL_GPL(adis_write_reg); +EXPORT_SYMBOL_GPL(__adis_write_reg); /** - * adis_read_reg() - read 2 bytes from a 16-bit register + * __adis_read_reg() - read N bytes from register (unlocked version) * @adis: The adis device * @reg: The address of the lower of the two registers * @val: The value read back from the device + * @size: The size of the @val buffer */ -int adis_read_reg(struct adis *adis, unsigned int reg, +int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, unsigned int size) { unsigned int page = reg / ADIS_PAGE_SIZE; @@ -166,7 +168,6 @@ int adis_read_reg(struct adis *adis, unsigned int reg, }, }; - mutex_lock(&adis->state_lock); spi_message_init(&msg); if (adis->current_page != page) { @@ -188,15 +189,14 @@ int adis_read_reg(struct adis *adis, unsigned int reg, spi_message_add_tail(&xfers[3], &msg); break; default: - ret = -EINVAL; - goto out_unlock; + return -EINVAL; } ret = spi_sync(adis->spi, &msg); if (ret) { dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n", reg, ret); - goto out_unlock; + return ret; } else { adis->current_page = page; } @@ -210,12 +210,9 @@ int adis_read_reg(struct adis *adis, unsigned int reg, break; } -out_unlock: - mutex_unlock(&adis->state_lock); - return ret; } -EXPORT_SYMBOL_GPL(adis_read_reg); +EXPORT_SYMBOL_GPL(__adis_read_reg); #ifdef CONFIG_DEBUG_FS -- cgit v1.2.3 From 100bfa38c8cb81ccbbd17ee4f0c1164f455f6039 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:13 +0200 Subject: iio: imu: adis[16480]: group RW into a single lock in adis_enable_irq() The adis_enable_irq() does a read & a write. This change keeps a lock for the duration of both operations vs for each op. The change is also needed in adis16480, since that has it's own implementation for adis_enable_irq(). Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 17 +++++++++++------ drivers/iio/imu/adis16480.c | 4 ++-- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 2f518e6c727d..5cf7a15be6ee 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -250,12 +250,16 @@ int adis_enable_irq(struct adis *adis, bool enable) int ret = 0; uint16_t msc; - if (adis->data->enable_irq) - return adis->data->enable_irq(adis, enable); + mutex_lock(&adis->state_lock); - ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); + if (adis->data->enable_irq) { + ret = adis->data->enable_irq(adis, enable); + goto out_unlock; + } + + ret = __adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); if (ret) - goto error_ret; + goto out_unlock; msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH; msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2; @@ -264,9 +268,10 @@ int adis_enable_irq(struct adis *adis, bool enable) else msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN; - ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc); + ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc); -error_ret: +out_unlock: + mutex_unlock(&adis->state_lock); return ret; } EXPORT_SYMBOL(adis_enable_irq); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 748f8bbf184d..e943039c3f98 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -947,14 +947,14 @@ static int adis16480_enable_irq(struct adis *adis, bool enable) uint16_t val; int ret; - ret = adis_read_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, &val); + ret = __adis_read_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, &val); if (ret) return ret; val &= ~ADIS16480_DRDY_EN_MSK; val |= ADIS16480_DRDY_EN(enable); - return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, val); + return __adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, val); } static int adis16480_initial_setup(struct iio_dev *indio_dev) -- cgit v1.2.3 From 6a4d6a7d6588d8c15501542da786ebff6602475c Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:14 +0200 Subject: iio: imu: adis: create an unlocked version of adis_check_status() This one also gets re-used in certain operations, so it makes sense to have an unlocked version of this to group it with other reads/writes/operations to have a single lock for the whole state change. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 5cf7a15be6ee..b63d6e6f5415 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -277,18 +277,18 @@ out_unlock: EXPORT_SYMBOL(adis_enable_irq); /** - * adis_check_status() - Check the device for error conditions + * __adis_check_status() - Check the device for error conditions (unlocked) * @adis: The adis device * * Returns 0 on success, a negative error code otherwise */ -int adis_check_status(struct adis *adis) +int __adis_check_status(struct adis *adis) { uint16_t status; int ret; int i; - ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status); + ret = __adis_read_reg_16(adis, adis->data->diag_stat_reg, &status); if (ret) return ret; @@ -306,7 +306,7 @@ int adis_check_status(struct adis *adis) return -EIO; } -EXPORT_SYMBOL_GPL(adis_check_status); +EXPORT_SYMBOL_GPL(__adis_check_status); /** * adis_reset() - Reset the device -- cgit v1.2.3 From 762ab093cbe26451175a5a63ea06c971bf973525 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:15 +0200 Subject: iio: imu: adis: create an unlocked version of adis_reset() The reset routine may also be important to be protected by the state-lock and grouped with other operations, so create an unlocked version, so that this can be done. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index b63d6e6f5415..615f174f0e6e 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -309,23 +309,23 @@ int __adis_check_status(struct adis *adis) EXPORT_SYMBOL_GPL(__adis_check_status); /** - * adis_reset() - Reset the device + * __adis_reset() - Reset the device (unlocked version) * @adis: The adis device * * Returns 0 on success, a negative error code otherwise */ -int adis_reset(struct adis *adis) +int __adis_reset(struct adis *adis) { int ret; - ret = adis_write_reg_8(adis, adis->data->glob_cmd_reg, + ret = __adis_write_reg_8(adis, adis->data->glob_cmd_reg, ADIS_GLOB_CMD_SW_RESET); if (ret) dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret); return ret; } -EXPORT_SYMBOL_GPL(adis_reset); +EXPORT_SYMBOL_GPL(__adis_reset); static int adis_self_test(struct adis *adis) { -- cgit v1.2.3 From cb5a07f1f15cc3fd7e12bfdd48ba0e9fb585ea5e Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:16 +0200 Subject: iio: imu: adis: protect initial startup routine with state lock The initial startup routine is called by some ADIS drivers during probe, and before registering with IIO. Normally, userspace should not be able to do any access to the device (as there shouldn't be any available). This change extends the state lock to the entire initial-startup routine. Behaviourally nothing should change, but this should make the library function a bit more robust. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 615f174f0e6e..10b8922fd51b 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -331,7 +331,7 @@ static int adis_self_test(struct adis *adis) { int ret; - ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, + ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, adis->data->self_test_mask); if (ret) { dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n", @@ -341,10 +341,10 @@ static int adis_self_test(struct adis *adis) msleep(adis->data->startup_delay); - ret = adis_check_status(adis); + ret = __adis_check_status(adis); if (adis->data->self_test_no_autoclear) - adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00); + __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00); return ret; } @@ -362,19 +362,23 @@ int adis_initial_startup(struct adis *adis) { int ret; + mutex_lock(&adis->state_lock); + ret = adis_self_test(adis); if (ret) { dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n"); - adis_reset(adis); + __adis_reset(adis); msleep(adis->data->startup_delay); ret = adis_self_test(adis); if (ret) { dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n"); - return ret; + goto out_unlock; } } - return 0; +out_unlock: + mutex_unlock(&adis->state_lock); + return ret; } EXPORT_SYMBOL_GPL(adis_initial_startup); -- cgit v1.2.3 From c5485a5d6a5f7d00ca4aba6698526c5ae3f2a1a2 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:17 +0200 Subject: iio: imu: adis: group single conversion under a single state lock The single conversion function does a series of reads + writes. This change extends the use of the state_lock for the entire set of operations. Previously, indio_dev's mlock was used. This change also removes the use of this lock. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 10b8922fd51b..c53f3ed3cb97 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -404,15 +404,15 @@ int adis_single_conversion(struct iio_dev *indio_dev, unsigned int uval; int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&adis->state_lock); - ret = adis_read_reg(adis, chan->address, &uval, + ret = __adis_read_reg(adis, chan->address, &uval, chan->scan_type.storagebits / 8); if (ret) goto err_unlock; if (uval & error_mask) { - ret = adis_check_status(adis); + ret = __adis_check_status(adis); if (ret) goto err_unlock; } @@ -424,7 +424,7 @@ int adis_single_conversion(struct iio_dev *indio_dev, ret = IIO_VAL_INT; err_unlock: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&adis->state_lock); return ret; } EXPORT_SYMBOL_GPL(adis_single_conversion); -- cgit v1.2.3 From ce476cd18aaea91ee31b991774553ba5239c9ee2 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:18 +0200 Subject: iio: imu: adis16400: rework locks using ADIS library's state lock This change removes the use of indio_dev's mlock in favor using the state lock from the ADIS library. The set_freq() & get_freq() hooks are unlocked, so they require specific locking. That is because in some cases the get_freq() hook is used in combination with adis16400_set_filter(). In cases where only one read/write is done, the functions that hold the state lock are used. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16400.c | 51 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 44e46dc96e00..662cb5367c11 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -162,6 +162,7 @@ struct adis16400_chip_info { unsigned int accel_scale_micro; int temp_scale_nano; int temp_offset; + /* set_freq() & get_freq() need to avoid using ADIS lib's state lock */ int (*set_freq)(struct adis16400_state *st, unsigned int freq); int (*get_freq)(struct adis16400_state *st); }; @@ -326,7 +327,7 @@ static int adis16334_get_freq(struct adis16400_state *st) int ret; uint16_t t; - ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t); + ret = __adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t); if (ret) return ret; @@ -350,7 +351,7 @@ static int adis16334_set_freq(struct adis16400_state *st, unsigned int freq) t <<= ADIS16334_RATE_DIV_SHIFT; t |= ADIS16334_RATE_INT_CLK; - return adis_write_reg_16(&st->adis, ADIS16400_SMPL_PRD, t); + return __adis_write_reg_16(&st->adis, ADIS16400_SMPL_PRD, t); } static int adis16400_get_freq(struct adis16400_state *st) @@ -358,7 +359,7 @@ static int adis16400_get_freq(struct adis16400_state *st) int sps, ret; uint16_t t; - ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t); + ret = __adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t); if (ret) return ret; @@ -390,7 +391,7 @@ static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq) else st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST; - return adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val); + return __adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val); } static const unsigned int adis16400_3db_divisors[] = { @@ -404,7 +405,7 @@ static const unsigned int adis16400_3db_divisors[] = { [7] = 200, /* Not a valid setting */ }; -static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val) +static int __adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val) { struct adis16400_state *st = iio_priv(indio_dev); uint16_t val16; @@ -415,11 +416,11 @@ static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val) break; } - ret = adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16); + ret = __adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16); if (ret) return ret; - ret = adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG, + ret = __adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG, (val16 & ~0x07) | i); return ret; } @@ -507,32 +508,31 @@ static int adis16400_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) { struct adis16400_state *st = iio_priv(indio_dev); + struct mutex *slock = &st->adis.state_lock; int ret, sps; switch (info) { case IIO_CHAN_INFO_CALIBBIAS: - mutex_lock(&indio_dev->mlock); ret = adis_write_reg_16(&st->adis, adis16400_addresses[chan->scan_index], val); - mutex_unlock(&indio_dev->mlock); return ret; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* * Need to cache values so we can update if the frequency * changes. */ - mutex_lock(&indio_dev->mlock); + mutex_lock(slock); st->filt_int = val; /* Work out update to current value */ sps = st->variant->get_freq(st); if (sps < 0) { - mutex_unlock(&indio_dev->mlock); + mutex_unlock(slock); return sps; } - ret = adis16400_set_filter(indio_dev, sps, + ret = __adis16400_set_filter(indio_dev, sps, val * 1000 + val2 / 1000); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(slock); return ret; case IIO_CHAN_INFO_SAMP_FREQ: sps = val * 1000 + val2 / 1000; @@ -540,9 +540,9 @@ static int adis16400_write_raw(struct iio_dev *indio_dev, if (sps <= 0) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(slock); ret = st->variant->set_freq(st, sps); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(slock); return ret; default: return -EINVAL; @@ -553,6 +553,7 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) { struct adis16400_state *st = iio_priv(indio_dev); + struct mutex *slock = &st->adis.state_lock; int16_t val16; int ret; @@ -596,10 +597,8 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_CALIBBIAS: - mutex_lock(&indio_dev->mlock); ret = adis_read_reg_16(&st->adis, adis16400_addresses[chan->scan_index], &val16); - mutex_unlock(&indio_dev->mlock); if (ret) return ret; val16 = sign_extend32(val16, 11); @@ -610,27 +609,27 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, *val = st->variant->temp_offset; return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - mutex_lock(&indio_dev->mlock); + mutex_lock(slock); /* Need both the number of taps and the sampling frequency */ - ret = adis_read_reg_16(&st->adis, + ret = __adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16); if (ret) { - mutex_unlock(&indio_dev->mlock); + mutex_unlock(slock); return ret; } ret = st->variant->get_freq(st); - if (ret >= 0) { - ret /= adis16400_3db_divisors[val16 & 0x07]; - *val = ret / 1000; - *val2 = (ret % 1000) * 1000; - } - mutex_unlock(&indio_dev->mlock); + mutex_unlock(slock); if (ret) return ret; + ret /= adis16400_3db_divisors[val16 & 0x07]; + *val = ret / 1000; + *val2 = (ret % 1000) * 1000; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(slock); ret = st->variant->get_freq(st); + mutex_unlock(slock); if (ret) return ret; *val = ret / 1000; -- cgit v1.2.3 From 0aee99a1ea53de1aedcf96a4d52d6161ffba011a Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:19 +0200 Subject: iio: gyro: adis16136: rework locks using ADIS library's state lock This replaces indio_dev's mlock with the state lock/mutex from the ADIS library. The __adis16136_get_freq() function has been prefixed to mark it as unlocked. The adis16136_{set,get}_filter() functions now hold the state lock for all the ops that they do. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adis16136.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index d637d52d051a..f10c4f173898 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -185,12 +185,12 @@ static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq) return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t); } -static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq) +static int __adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq) { uint16_t t; int ret; - ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t); + ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t); if (ret) return ret; @@ -224,10 +224,13 @@ static ssize_t adis16136_read_frequency(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct adis16136 *adis16136 = iio_priv(indio_dev); + struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; int ret; - ret = adis16136_get_freq(adis16136, &freq); + mutex_lock(slock); + ret = __adis16136_get_freq(adis16136, &freq); + mutex_unlock(slock); if (ret) return ret; @@ -252,42 +255,50 @@ static const unsigned adis16136_3db_divisors[] = { static int adis16136_set_filter(struct iio_dev *indio_dev, int val) { struct adis16136 *adis16136 = iio_priv(indio_dev); + struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; int i, ret; - ret = adis16136_get_freq(adis16136, &freq); + mutex_lock(slock); + ret = __adis16136_get_freq(adis16136, &freq); if (ret) - return ret; + goto out_unlock; for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) { if (freq / adis16136_3db_divisors[i] >= val) break; } - return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i); + ret = __adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i); +out_unlock: + mutex_unlock(slock); + + return ret; } static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) { struct adis16136 *adis16136 = iio_priv(indio_dev); + struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; uint16_t val16; int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(slock); - ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16); + ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, + &val16); if (ret) goto err_unlock; - ret = adis16136_get_freq(adis16136, &freq); + ret = __adis16136_get_freq(adis16136, &freq); if (ret) goto err_unlock; *val = freq / adis16136_3db_divisors[val16 & 0x07]; err_unlock: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(slock); return ret ? ret : IIO_VAL_INT; } -- cgit v1.2.3 From d693845da31c4d1093a8165d1ac4a73eac0f9373 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:20 +0200 Subject: iio: imu: adis16480: use state lock for filter freq set It's the only operation that does 2 operations (a read & a write), so the unlocked functions can be used under a single state lock. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16480.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index e943039c3f98..f73094e8d35d 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -555,6 +555,7 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int freq) { struct adis16480 *st = iio_priv(indio_dev); + struct mutex *slock = &st->adis.state_lock; unsigned int enable_mask, offset, reg; unsigned int diff, best_diff; unsigned int i, best_freq; @@ -565,9 +566,11 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, offset = ad16480_filter_data[chan->scan_index][1]; enable_mask = BIT(offset + 2); - ret = adis_read_reg_16(&st->adis, reg, &val); + mutex_lock(slock); + + ret = __adis_read_reg_16(&st->adis, reg, &val); if (ret) - return ret; + goto out_unlock; if (freq == 0) { val &= ~enable_mask; @@ -589,7 +592,11 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, val |= enable_mask; } - return adis_write_reg_16(&st->adis, reg, val); + ret = __adis_write_reg_16(&st->adis, reg, val); +out_unlock: + mutex_unlock(slock); + + return ret; } static int adis16480_read_raw(struct iio_dev *indio_dev, -- cgit v1.2.3 From 160b37f49ec3b3adc6916f411ee3ceebea8dd13d Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 22 Nov 2019 15:24:21 +0200 Subject: iio: gyro: adis16260: replace mlock with ADIS lib's state_lock This change uses the ADIS library's state_lock to protect the state of the `max_speed_hz` change that is done during the set of the sampling frequency. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adis16260.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 207a0ce13439..726a0aa321a8 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -293,7 +293,7 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, addr = adis16260_addresses[chan->scan_index][1]; return adis_write_reg_16(adis, addr, val); case IIO_CHAN_INFO_SAMP_FREQ: - mutex_lock(&indio_dev->mlock); + mutex_lock(&adis->state_lock); if (spi_get_device_id(adis->spi)->driver_data) t = 256 / val; else @@ -308,9 +308,9 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, adis->spi->max_speed_hz = ADIS16260_SPI_SLOW; else adis->spi->max_speed_hz = ADIS16260_SPI_FAST; - ret = adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t); + ret = __adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&adis->state_lock); return ret; } return -EINVAL; -- cgit v1.2.3 From 76aa41c17befa67b8727942d134eb65af501908b Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:48 +0100 Subject: iio: max31856: add option for setting mains filter rejection frequency This sensor has an embedded notch filter for reducing interferences caused by the power mains. This filter can be tuned to reject either 50Hz or 60Hz (and harmonics). Currently the said setting is left alone (the sensor defaults to 60Hz). This patch introduces a IIO attribute that allows the user to set the said filter to the desired frequency. NOTE: this has been intentionally not tied to any DT property to allow the configuration of this setting from userspace, e.g. with a GUI or by reading a configuration file, or maybe reading a GPIO tied to a physical switch or accessing some device that can autodetect the line frequency. Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Signed-off-by: Andrea Merello Reviewed-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/max31856.c | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index 73ed550e3fc9..d97ba9ee1598 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -23,6 +23,7 @@ #define MAX31856_CR0_1SHOT BIT(6) #define MAX31856_CR0_OCFAULT BIT(4) #define MAX31856_CR0_OCFAULT_MASK GENMASK(5, 4) +#define MAX31856_CR0_FILTER_50HZ BIT(0) #define MAX31856_TC_TYPE_MASK GENMASK(3, 0) #define MAX31856_FAULT_OVUV BIT(1) #define MAX31856_FAULT_OPEN BIT(0) @@ -63,6 +64,7 @@ static const struct iio_chan_spec max31856_channels[] = { struct max31856_data { struct spi_device *spi; u32 thermocouple_type; + bool filter_50hz; }; static int max31856_read(struct max31856_data *data, u8 reg, @@ -123,6 +125,11 @@ static int max31856_init(struct max31856_data *data) reg_cr0_val &= ~MAX31856_CR0_1SHOT; reg_cr0_val |= MAX31856_CR0_AUTOCONVERT; + if (data->filter_50hz) + reg_cr0_val |= MAX31856_CR0_FILTER_50HZ; + else + reg_cr0_val &= ~MAX31856_CR0_FILTER_50HZ; + return max31856_write(data, MAX31856_CR0_REG, reg_cr0_val); } @@ -249,12 +256,54 @@ static ssize_t show_fault_oc(struct device *dev, return show_fault(dev, MAX31856_FAULT_OPEN, buf); } +static ssize_t show_filter(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max31856_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60); +} + +static ssize_t set_filter(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max31856_data *data = iio_priv(indio_dev); + unsigned int freq; + int ret; + + ret = kstrtouint(buf, 10, &freq); + if (ret) + return ret; + + switch (freq) { + case 50: + data->filter_50hz = true; + break; + case 60: + data->filter_50hz = false; + break; + default: + return -EINVAL; + } + + max31856_init(data); + return len; +} + static IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0); static IIO_DEVICE_ATTR(fault_oc, 0444, show_fault_oc, NULL, 0); +static IIO_DEVICE_ATTR(in_temp_filter_notch_center_frequency, 0644, + show_filter, set_filter, 0); static struct attribute *max31856_attributes[] = { &iio_dev_attr_fault_ovuv.dev_attr.attr, &iio_dev_attr_fault_oc.dev_attr.attr, + &iio_dev_attr_in_temp_filter_notch_center_frequency.dev_attr.attr, NULL, }; @@ -280,6 +329,7 @@ static int max31856_probe(struct spi_device *spi) data = iio_priv(indio_dev); data->spi = spi; + data->filter_50hz = false; spi_set_drvdata(spi, indio_dev); -- cgit v1.2.3 From 57a4274ce168d4e63a7b5f4a8a776f7bdd5666a9 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:50 +0100 Subject: iio: max31856: add support for configuring the HW averaging This sensor can perform samples averaging in hardware, but currently the driver leaves this setting alone (default is no averaging). This patch binds this HW setting to the "oversampling_ratio" IIO attribute and allows the user to set the averaging as desired (the HW supports averaging of 2, 5, 8 or 16 samples; in-between values are rounded up). Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Signed-off-by: Andrea Merello Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/max31856.c | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index d97ba9ee1598..8457ca9ae326 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -12,6 +12,7 @@ #include #include #include +#include #include /* * The MSB of the register value determines whether the following byte will @@ -24,6 +25,8 @@ #define MAX31856_CR0_OCFAULT BIT(4) #define MAX31856_CR0_OCFAULT_MASK GENMASK(5, 4) #define MAX31856_CR0_FILTER_50HZ BIT(0) +#define MAX31856_AVERAGING_MASK GENMASK(6, 4) +#define MAX31856_AVERAGING_SHIFT 4 #define MAX31856_TC_TYPE_MASK GENMASK(3, 0) #define MAX31856_FAULT_OVUV BIT(1) #define MAX31856_FAULT_OPEN BIT(0) @@ -51,6 +54,8 @@ static const struct iio_chan_spec max31856_channels[] = { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) }, { /* Cold Junction Temperature */ .type = IIO_TEMP, @@ -58,6 +63,8 @@ static const struct iio_chan_spec max31856_channels[] = { .modified = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) }, }; @@ -65,6 +72,7 @@ struct max31856_data { struct spi_device *spi; u32 thermocouple_type; bool filter_50hz; + int averaging; }; static int max31856_read(struct max31856_data *data, u8 reg, @@ -109,6 +117,10 @@ static int max31856_init(struct max31856_data *data) reg_cr1_val &= ~MAX31856_TC_TYPE_MASK; reg_cr1_val |= data->thermocouple_type; + + reg_cr1_val &= ~MAX31856_AVERAGING_MASK; + reg_cr1_val |= data->averaging << MAX31856_AVERAGING_SHIFT; + ret = max31856_write(data, MAX31856_CR1_REG, reg_cr1_val); if (ret) return ret; @@ -217,6 +229,9 @@ static int max31856_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_MICRO; } break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = 1 << data->averaging; + return IIO_VAL_INT; default: ret = -EINVAL; break; @@ -225,6 +240,33 @@ static int max31856_read_raw(struct iio_dev *indio_dev, return ret; } +static int max31856_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct max31856_data *data = iio_priv(indio_dev); + int msb; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val > 16 || val < 1) + return -EINVAL; + msb = fls(val) - 1; + /* Round up to next 2pow if needed */ + if (BIT(msb) < val) + msb++; + + data->averaging = msb; + max31856_init(data); + break; + + default: + return -EINVAL; + } + + return 0; +} + static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); @@ -313,6 +355,7 @@ static const struct attribute_group max31856_group = { static const struct iio_info max31856_info = { .read_raw = max31856_read_raw, + .write_raw = max31856_write_raw, .attrs = &max31856_group, }; -- cgit v1.2.3 From 8cb3403633146afe1ba81f79224679f5df2940d4 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:51 +0100 Subject: iio: core: add char type for sysfs attributes This patch introduces IIO_VAL_CHAR type for standard IIO attributes to allow for attributes that needs to be represented by character rather than a number. This is preparatory for introducing a new attribute whose purpose is to describe thermocouple type, that can be i.e. "J", "K", etc.. The char-type value is stored in the first "value" integer that is passed to the .[read/write]_raw() callbacks. Note that in order to make it possible for the IIO core to correctly parse this type (actually, to avoid integer parsing), it became mandatory for any driver that wish to use IIO_VAL_CHAR on a writable attribute to implement .write_raw_get_fmt(). Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Signed-off-by: Andrea Merello Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index f72c2dc5f703..958b5c48a86f 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -596,6 +596,8 @@ static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type, } return l; } + case IIO_VAL_CHAR: + return snprintf(buf, len, "%c", (char)vals[0]); default: return 0; } @@ -837,7 +839,8 @@ static ssize_t iio_write_channel_info(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); int ret, fract_mult = 100000; - int integer, fract; + int integer, fract = 0; + bool is_char = false; /* Assumes decimal - precision based on number of digits */ if (!indio_dev->info->write_raw) @@ -855,13 +858,24 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_INT_PLUS_NANO: fract_mult = 100000000; break; + case IIO_VAL_CHAR: + is_char = true; + break; default: return -EINVAL; } - ret = iio_str_to_fixpoint(buf, fract_mult, &integer, &fract); - if (ret) - return ret; + if (is_char) { + char ch; + + if (sscanf(buf, "%c", &ch) != 1) + return -EINVAL; + integer = ch; + } else { + ret = iio_str_to_fixpoint(buf, fract_mult, &integer, &fract); + if (ret) + return ret; + } ret = indio_dev->info->write_raw(indio_dev, this_attr->c, integer, fract, this_attr->address); -- cgit v1.2.3 From ddfb97d8f06c98fa5d80bbb6a997155a838161e9 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:52 +0100 Subject: iio: core: add thermocouple_type standard attribute We have a couple of thermocouple IIO drivers, supporting several chips. Some of them support only one specific thermocouple type (e.g. "K", "J"), one of them can be configured to work with several different thermocouple types. In certain applications thermocouples could be externally connected to the chip by the user. This patch introduces a new IIO standard attribute to report the supported thermocouple type and, where applicable, to allow it to be dynamically set using sysfs. Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Signed-off-by: Andrea Merello Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 958b5c48a86f..fa2c3b321bfd 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -161,6 +161,7 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time", [IIO_CHAN_INFO_CALIBEMISSIVITY] = "calibemissivity", [IIO_CHAN_INFO_OVERSAMPLING_RATIO] = "oversampling_ratio", + [IIO_CHAN_INFO_THERMOCOUPLE_TYPE] = "thermocouple_type", }; /** -- cgit v1.2.3 From ea4103070f03f789f305bdd1803d5d4c42f0b3b9 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:54 +0100 Subject: iio: max31856: add support for runtime-configuring the thermocouple type The sensor support various thermocouple types (e.g. J, K, N, ...). The driver allows to configure this parameter using a DT property. This is useful when i.e. the thermocouple is physically tied to the sensor and it is usually not removed, or when it is at least known in advance which sensor will be connected to the circuit. However, if the user can randomly connect any kind of thermocouples (i.e. the device exposes a connector, and the user is free to connect its own sensors), it would be more appropriate to provide a mechanism to dynamically switch from one thermocouple type to another. This can be i.e. handled in userspace by a GUI, a configuration file or a program that detects the thermocouple type by reading a GPIO, or a eeprom on the probe, or whatever. This patch adds a IIO attribute that can be used to override, at run-time, the DT-provided setting (which serves as default). Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Signed-off-by: Andrea Merello Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/max31856.c | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index 8457ca9ae326..b4cb21ab2e85 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -6,6 +6,7 @@ * Copyright (C) 2018-2019 Rockwell Collins */ +#include #include #include #include @@ -53,7 +54,8 @@ static const struct iio_chan_spec max31856_channels[] = { { /* Thermocouple Temperature */ .type = IIO_TEMP, .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) }, @@ -75,6 +77,10 @@ struct max31856_data { int averaging; }; +static const char max31856_tc_types[] = { + 'B', 'E', 'J', 'K', 'N', 'R', 'S', 'T' +}; + static int max31856_read(struct max31856_data *data, u8 reg, u8 val[], unsigned int read_size) { @@ -232,6 +238,9 @@ static int max31856_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *val = 1 << data->averaging; return IIO_VAL_INT; + case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: + *val = max31856_tc_types[data->thermocouple_type]; + return IIO_VAL_CHAR; default: ret = -EINVAL; break; @@ -240,6 +249,18 @@ static int max31856_read_raw(struct iio_dev *indio_dev, return ret; } +static int max31856_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: + return IIO_VAL_CHAR; + default: + return IIO_VAL_INT; + } +} + static int max31856_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -259,7 +280,24 @@ static int max31856_write_raw(struct iio_dev *indio_dev, data->averaging = msb; max31856_init(data); break; + case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: + { + int tc_type = -1; + int i; + + for (i = 0; i < ARRAY_SIZE(max31856_tc_types); i++) { + if (max31856_tc_types[i] == toupper(val)) { + tc_type = i; + break; + } + } + if (tc_type < 0) + return -EINVAL; + data->thermocouple_type = tc_type; + max31856_init(data); + break; + } default: return -EINVAL; } @@ -356,6 +394,7 @@ static const struct attribute_group max31856_group = { static const struct iio_info max31856_info = { .read_raw = max31856_read_raw, .write_raw = max31856_write_raw, + .write_raw_get_fmt = max31856_write_raw_get_fmt, .attrs = &max31856_group, }; -- cgit v1.2.3 From d7f6a749f30bc2296e0d92553aa9f4daad7bcf1f Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:55 +0100 Subject: iio: maxim_thermocouple: add thermocouple_type sysfs attribute We added a sysfs ABI for getting/setting the type of a thermocouple. This driver supports chips that support specific fixed thermocouple types; we cannot set it, but still we can add this sysfs attribute in RO mode to read-back the thermocouple type. This driver supports actually several chips: - max6675 - max31855[k/j/n/s/t/e/r]asa family Max6675 supports only K-type thermocouples, so we can just report that. Each chip in max31855 family supports just one specific thermocouple type (in the obvious way: i.e. max31855jasa supports J-type). This driver did accept a generic SPI ID and OF compatible "max31855" which does not give any clue about which chip is really involved (and unfortunately it seems we have no way to detect it). This patch introduces a new set of, more specific, SPI IDs and OF compatible strings to better match the chip type. The old, generic, "max31855" binding is kept for compatibility reasons, but this patch aims to deprecate it, so, should we hit it, a warning is spit. In such case the reported thermocouple type in sysfs is '?', because we have no way to know. Regarding the implementation: the thermocouple type information is stored in the driver private data and I've kept only two maxim_thermocouple_chip types in order to avoid a lot of duplications (seven chip types with just a different thermocouple type). RFT because I have no real HW to test this. Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Cc: Rob Herring Cc: Mark Rutland Cc: devicetree@vger.kernel.org Signed-off-by: Andrea Merello Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/maxim_thermocouple.c | 44 ++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index d1360605209c..8d21116c7a22 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -24,13 +25,25 @@ enum { MAX6675, MAX31855, + MAX31855K, + MAX31855J, + MAX31855N, + MAX31855S, + MAX31855T, + MAX31855E, + MAX31855R, +}; + +static const char maxim_tc_types[] = { + 'K', '?', 'K', 'J', 'N', 'S', 'T', 'E', 'R' }; static const struct iio_chan_spec max6675_channels[] = { { /* thermocouple temperature */ .type = IIO_TEMP, .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE), .scan_index = 0, .scan_type = { .sign = 's', @@ -48,7 +61,8 @@ static const struct iio_chan_spec max31855_channels[] = { .type = IIO_TEMP, .address = 2, .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE), .scan_index = 0, .scan_type = { .sign = 's', @@ -110,6 +124,7 @@ struct maxim_thermocouple_data { const struct maxim_thermocouple_chip *chip; u8 buffer[16] ____cacheline_aligned; + char tc_type; }; static int maxim_thermocouple_read(struct maxim_thermocouple_data *data, @@ -196,6 +211,10 @@ static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev, ret = IIO_VAL_INT; } break; + case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: + *val = data->tc_type; + ret = IIO_VAL_CHAR; + break; } return ret; @@ -210,8 +229,9 @@ static int maxim_thermocouple_probe(struct spi_device *spi) const struct spi_device_id *id = spi_get_device_id(spi); struct iio_dev *indio_dev; struct maxim_thermocouple_data *data; + const int chip_type = (id->driver_data == MAX6675) ? MAX6675 : MAX31855; const struct maxim_thermocouple_chip *chip = - &maxim_thermocouple_chips[id->driver_data]; + &maxim_thermocouple_chips[chip_type]; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); @@ -229,6 +249,7 @@ static int maxim_thermocouple_probe(struct spi_device *spi) data = iio_priv(indio_dev); data->spi = spi; data->chip = chip; + data->tc_type = maxim_tc_types[id->driver_data]; ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, @@ -236,12 +257,22 @@ static int maxim_thermocouple_probe(struct spi_device *spi) if (ret) return ret; + if (id->driver_data == MAX31855) + dev_warn(&spi->dev, "generic max31855 ID is deprecated\nplease use more specific part type"); + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id maxim_thermocouple_id[] = { {"max6675", MAX6675}, {"max31855", MAX31855}, + {"max31855k", MAX31855K}, + {"max31855j", MAX31855J}, + {"max31855n", MAX31855N}, + {"max31855s", MAX31855S}, + {"max31855t", MAX31855T}, + {"max31855e", MAX31855E}, + {"max31855r", MAX31855R}, {}, }; MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); @@ -249,6 +280,13 @@ MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); static const struct of_device_id maxim_thermocouple_of_match[] = { { .compatible = "maxim,max6675" }, { .compatible = "maxim,max31855" }, + { .compatible = "maxim,max31855k" }, + { .compatible = "maxim,max31855j" }, + { .compatible = "maxim,max31855n" }, + { .compatible = "maxim,max31855s" }, + { .compatible = "maxim,max31855t" }, + { .compatible = "maxim,max31855e" }, + { .compatible = "maxim,max31855r" }, { }, }; MODULE_DEVICE_TABLE(of, maxim_thermocouple_of_match); -- cgit v1.2.3 From a8b26c2ddc83fab7f582405c468df2cbb9b5348a Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Mon, 18 Nov 2019 12:58:07 +0200 Subject: iio: dac: ad7303: use regulator get optional to check for ext supply Previously, the code was using the of_read_property_bool() to check if an external regulator was provided. However, this is redundant, as it's more simple/direct to just ask the regulator is provided, via a `devm_regulator_get_optional()` call. Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad7303.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index 14bbac6bee98..15af8a1cce3e 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -202,7 +201,6 @@ static int ad7303_probe(struct spi_device *spi) const struct spi_device_id *id = spi_get_device_id(spi); struct iio_dev *indio_dev; struct ad7303_state *st; - bool ext_ref; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -224,24 +222,15 @@ static int ad7303_probe(struct spi_device *spi) if (ret) return ret; - if (spi->dev.of_node) { - ext_ref = of_property_read_bool(spi->dev.of_node, - "REF-supply"); - } else { - struct ad7303_platform_data *pdata = spi->dev.platform_data; - if (pdata && pdata->use_external_ref) - ext_ref = true; - else - ext_ref = false; - } - - if (ext_ref) { - st->vref_reg = devm_regulator_get(&spi->dev, "REF"); - if (IS_ERR(st->vref_reg)) { - ret = PTR_ERR(st->vref_reg); + st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF"); + if (IS_ERR(st->vref_reg)) { + ret = PTR_ERR(st->vref_reg); + if (ret != -ENODEV) goto err_disable_vdd_reg; - } + st->vref_reg = NULL; + } + if (st->vref_reg) { ret = regulator_enable(st->vref_reg); if (ret) goto err_disable_vdd_reg; -- cgit v1.2.3 From 7dd1cfc1398a79430e998a1f2894aa34c7fb749b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 04:19:19 +0100 Subject: iio: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/Kconfig | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 95e6f96d4529..7eaf77707b0b 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -75,26 +75,26 @@ config BMG160_SPI select REGMAP_SPI config FXAS21002C - tristate "NXP FXAS21002C Gyro Sensor" - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - select FXAS21002C_I2C if (I2C) - select FXAS21002C_SPI if (SPI) - depends on (I2C || SPI_MASTER) - help - Say yes here to build support for NXP FXAS21002C Tri-axis Gyro - Sensor driver connected via I2C or SPI. - - This driver can also be built as a module. If so, the module - will be called fxas21002c_i2c or fxas21002c_spi. + tristate "NXP FXAS21002C Gyro Sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select FXAS21002C_I2C if (I2C) + select FXAS21002C_SPI if (SPI) + depends on (I2C || SPI_MASTER) + help + Say yes here to build support for NXP FXAS21002C Tri-axis Gyro + Sensor driver connected via I2C or SPI. + + This driver can also be built as a module. If so, the module + will be called fxas21002c_i2c or fxas21002c_spi. config FXAS21002C_I2C - tristate - select REGMAP_I2C + tristate + select REGMAP_I2C config FXAS21002C_SPI - tristate - select REGMAP_SPI + tristate + select REGMAP_SPI config HID_SENSOR_GYRO_3D depends on HID_SENSOR_HUB -- cgit v1.2.3 From f3bcd06f574a0751ee40ec25b9aad6968877ae5f Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Thu, 21 Nov 2019 12:05:17 +0100 Subject: iio: trigger: stm32-timer: remove unnecessary update event There is no need to explicitly generate update event to update timer master mode. Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/stm32-timer-trigger.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index a5dfe65cd9b9..2e0d32aa8436 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -297,9 +297,6 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, strlen(master_mode_table[i]))) { regmap_update_bits(priv->regmap, TIM_CR2, mask, i << shift); - /* Make sure that registers are updated */ - regmap_update_bits(priv->regmap, TIM_EGR, - TIM_EGR_UG, TIM_EGR_UG); return len; } } -- cgit v1.2.3 From 7bb501f49ddb96365ef8d4fe882e5ceaa36215e4 Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Mon, 25 Nov 2019 09:05:02 +0100 Subject: iio: ping: add parallax ping sensors Add support for parallax ping and laser ping sensors with just one pin for trigger and echo signal. This driver is based on srf04. In contrast to it it's necessary to change direction of the pin and to request the irq just for the period when the echo is rising and falling. Because this adds a lot of cases there is this individual driver for handling this type of sensors. Add a new configuration variable CONFIG_PING to Kconfig and Makefile. Julia reported an issue with failing to unlock a mutex in some error paths. Signed-off-by: Andreas Klinger Reported-by: kbuild test robot Reported-by: Julia Lawall Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/Kconfig | 15 ++ drivers/iio/proximity/Makefile | 1 + drivers/iio/proximity/ping.c | 335 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 351 insertions(+) create mode 100644 drivers/iio/proximity/ping.c (limited to 'drivers') diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index d53601447da4..37606d400805 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -58,6 +58,21 @@ config MB1232 To compile this driver as a module, choose M here: the module will be called mb1232. +config PING + tristate "Parallax GPIO bitbanged ranger sensors" + depends on GPIOLIB + help + Say Y here to build a driver for GPIO bitbanged ranger sensors + with just one GPIO for the trigger and echo. This driver can be + used to measure the distance of objects. + + Actually supported are: + - Parallax PING))) (ultrasonic) + - Parallax LaserPING (time-of-flight) + + To compile this driver as a module, choose M here: the + module will be called ping. + config RFD77402 tristate "RFD77402 ToF sensor" depends on I2C diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 0bb5f9de13d6..c591b019304e 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_AS3935) += as3935.o obj-$(CONFIG_ISL29501) += isl29501.o obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o obj-$(CONFIG_MB1232) += mb1232.o +obj-$(CONFIG_PING) += ping.o obj-$(CONFIG_RFD77402) += rfd77402.o obj-$(CONFIG_SRF04) += srf04.o obj-$(CONFIG_SRF08) += srf08.o diff --git a/drivers/iio/proximity/ping.c b/drivers/iio/proximity/ping.c new file mode 100644 index 000000000000..34aff108dff5 --- /dev/null +++ b/drivers/iio/proximity/ping.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PING: ultrasonic sensor for distance measuring by using only one GPIOs + * + * Copyright (c) 2019 Andreas Klinger + * + * For details about the devices see: + * http://parallax.com/sites/default/files/downloads/28041-LaserPING-2m-Rangefinder-Guide.pdf + * http://parallax.com/sites/default/files/downloads/28015-PING-Documentation-v1.6.pdf + * + * the measurement cycle as timing diagram looks like: + * + * GPIO ___ ________________________ + * ping: __/ \____________/ \________________ + * ^ ^ ^ ^ + * |<->| interrupt interrupt + * udelay(5) (ts_rising) (ts_falling) + * |<---------------------->| + * . pulse time measured . + * . --> one round trip of ultra sonic waves + * ultra . . + * sonic _ _ _. . + * burst: _________/ \_/ \_/ \_________________________________________ + * . + * ultra . + * sonic _ _ _. + * echo: __________________________________/ \_/ \_/ \________________ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ping_cfg { + unsigned long trigger_pulse_us; /* length of trigger pulse */ + int laserping_error; /* support error code in */ + /* pulse width of laser */ + /* ping sensors */ + s64 timeout_ns; /* timeout in ns */ +}; + +struct ping_data { + struct device *dev; + struct gpio_desc *gpiod_ping; + struct mutex lock; + int irqnr; + ktime_t ts_rising; + ktime_t ts_falling; + struct completion rising; + struct completion falling; + const struct ping_cfg *cfg; +}; + +static const struct ping_cfg pa_ping_cfg = { + .trigger_pulse_us = 5, + .laserping_error = 0, + .timeout_ns = 18500000, /* 3 meters */ +}; + +static const struct ping_cfg pa_laser_ping_cfg = { + .trigger_pulse_us = 5, + .laserping_error = 1, + .timeout_ns = 15500000, /* 2 meters plus error codes */ +}; + +static irqreturn_t ping_handle_irq(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ping_data *data = iio_priv(indio_dev); + ktime_t now = ktime_get(); + + if (gpiod_get_value(data->gpiod_ping)) { + data->ts_rising = now; + complete(&data->rising); + } else { + data->ts_falling = now; + complete(&data->falling); + } + + return IRQ_HANDLED; +} + +static int ping_read(struct ping_data *data) +{ + int ret; + ktime_t ktime_dt; + s64 dt_ns; + u32 time_ns, distance_mm; + struct platform_device *pdev = to_platform_device(data->dev); + struct iio_dev *indio_dev = iio_priv_to_dev(data); + + /* + * just one read-echo-cycle can take place at a time + * ==> lock against concurrent reading calls + */ + mutex_lock(&data->lock); + + reinit_completion(&data->rising); + reinit_completion(&data->falling); + + gpiod_set_value(data->gpiod_ping, 1); + udelay(data->cfg->trigger_pulse_us); + gpiod_set_value(data->gpiod_ping, 0); + + ret = gpiod_direction_input(data->gpiod_ping); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + data->irqnr = gpiod_to_irq(data->gpiod_ping); + if (data->irqnr < 0) { + dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr); + mutex_unlock(&data->lock); + return data->irqnr; + } + + ret = request_irq(data->irqnr, ping_handle_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + pdev->name, indio_dev); + if (ret < 0) { + dev_err(data->dev, "request_irq: %d\n", ret); + mutex_unlock(&data->lock); + return ret; + } + + /* it should not take more than 20 ms until echo is rising */ + ret = wait_for_completion_killable_timeout(&data->rising, HZ/50); + if (ret < 0) + goto err_reset_direction; + else if (ret == 0) { + ret = -ETIMEDOUT; + goto err_reset_direction; + } + + /* it cannot take more than 50 ms until echo is falling */ + ret = wait_for_completion_killable_timeout(&data->falling, HZ/20); + if (ret < 0) + goto err_reset_direction; + else if (ret == 0) { + ret = -ETIMEDOUT; + goto err_reset_direction; + } + + ktime_dt = ktime_sub(data->ts_falling, data->ts_rising); + + free_irq(data->irqnr, indio_dev); + + ret = gpiod_direction_output(data->gpiod_ping, GPIOD_OUT_LOW); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + mutex_unlock(&data->lock); + + dt_ns = ktime_to_ns(ktime_dt); + if (dt_ns > data->cfg->timeout_ns) { + dev_dbg(data->dev, "distance out of range: dt=%lldns\n", + dt_ns); + return -EIO; + } + + time_ns = dt_ns; + + /* + * read error code of laser ping sensor and give users chance to + * figure out error by using dynamic debuggging + */ + if (data->cfg->laserping_error) { + if ((time_ns > 12500000) && (time_ns <= 13500000)) { + dev_dbg(data->dev, "target too close or to far\n"); + return -EIO; + } + if ((time_ns > 13500000) && (time_ns <= 14500000)) { + dev_dbg(data->dev, "internal sensor error\n"); + return -EIO; + } + if ((time_ns > 14500000) && (time_ns <= 15500000)) { + dev_dbg(data->dev, "internal sensor timeout\n"); + return -EIO; + } + } + + /* + * the speed as function of the temperature is approximately: + * + * speed = 331,5 + 0,6 * Temp + * with Temp in °C + * and speed in m/s + * + * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the + * temperature + * + * therefore: + * time 343,5 time * 232 + * distance = ------ * ------- = ------------ + * 10^6 2 1350800 + * with time in ns + * and distance in mm (one way) + * + * because we limit to 3 meters the multiplication with 232 just + * fits into 32 bit + */ + distance_mm = time_ns * 232 / 1350800; + + return distance_mm; + +err_reset_direction: + free_irq(data->irqnr, indio_dev); + mutex_unlock(&data->lock); + + if (gpiod_direction_output(data->gpiod_ping, GPIOD_OUT_LOW)) + dev_dbg(data->dev, "error in gpiod_direction_output\n"); + return ret; +} + +static int ping_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long info) +{ + struct ping_data *data = iio_priv(indio_dev); + int ret; + + if (channel->type != IIO_DISTANCE) + return -EINVAL; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ping_read(data); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * maximum resolution in datasheet is 1 mm + * 1 LSB is 1 mm + */ + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info ping_iio_info = { + .read_raw = ping_read_raw, +}; + +static const struct iio_chan_spec ping_chan_spec[] = { + { + .type = IIO_DISTANCE, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct of_device_id of_ping_match[] = { + { .compatible = "parallax,ping", .data = &pa_ping_cfg}, + { .compatible = "parallax,laserping", .data = &pa_ping_cfg}, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_ping_match); + +static int ping_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ping_data *data; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct ping_data)); + if (!indio_dev) { + dev_err(dev, "failed to allocate IIO device\n"); + return -ENOMEM; + } + + data = iio_priv(indio_dev); + data->dev = dev; + data->cfg = of_device_get_match_data(dev); + + mutex_init(&data->lock); + init_completion(&data->rising); + init_completion(&data->falling); + + data->gpiod_ping = devm_gpiod_get(dev, "ping", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_ping)) { + dev_err(dev, "failed to get ping-gpios: err=%ld\n", + PTR_ERR(data->gpiod_ping)); + return PTR_ERR(data->gpiod_ping); + } + + if (gpiod_cansleep(data->gpiod_ping)) { + dev_err(data->dev, "cansleep-GPIOs not supported\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = "ping"; + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &ping_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ping_chan_spec; + indio_dev->num_channels = ARRAY_SIZE(ping_chan_spec); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver ping_driver = { + .probe = ping_probe, + .driver = { + .name = "ping-gpio", + .of_match_table = of_ping_match, + }, +}; + +module_platform_driver(ping_driver); + +MODULE_AUTHOR("Andreas Klinger "); +MODULE_DESCRIPTION("PING sensors for distance measuring using one GPIOs"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ping"); -- cgit v1.2.3 From b0ec7a44393e0d7a60878e2f56412072dd992724 Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Mon, 25 Nov 2019 15:21:37 +0200 Subject: iio: adc: ad7887: Cleanup channel assignment The channels specification assignment in chip info was simplified. This patch makes supporting other devices by this driver easier. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7887.c | 82 +++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 6223043e432b..c6a3428e950a 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -43,11 +43,17 @@ enum ad7887_channels { /** * struct ad7887_chip_info - chip specifc information * @int_vref_mv: the internal reference voltage - * @channel: channel specification + * @channels: channels specification + * @num_channels: number of channels + * @dual_channels: channels specification in dual mode + * @num_dual_channels: number of channels in dual mode */ struct ad7887_chip_info { u16 int_vref_mv; - struct iio_chan_spec channel[3]; + const struct iio_chan_spec *channels; + unsigned int num_channels; + const struct iio_chan_spec *dual_channels; + unsigned int num_dual_channels; }; struct ad7887_state { @@ -183,45 +189,43 @@ static int ad7887_read_raw(struct iio_dev *indio_dev, return -EINVAL; } +#define AD7887_CHANNEL(x) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (x), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = (x), \ + .scan_index = (x), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 0, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec ad7887_channels[] = { + AD7887_CHANNEL(0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec ad7887_dual_channels[] = { + AD7887_CHANNEL(0), + AD7887_CHANNEL(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; static const struct ad7887_chip_info ad7887_chip_info_tbl[] = { /* * More devices added in future */ [ID_AD7887] = { - .channel[0] = { - .type = IIO_VOLTAGE, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - .address = 1, - .scan_index = 1, - .scan_type = { - .sign = 'u', - .realbits = 12, - .storagebits = 16, - .shift = 0, - .endianness = IIO_BE, - }, - }, - .channel[1] = { - .type = IIO_VOLTAGE, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - .address = 0, - .scan_index = 0, - .scan_type = { - .sign = 'u', - .realbits = 12, - .storagebits = 16, - .shift = 0, - .endianness = IIO_BE, - }, - }, - .channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2), + .channels = ad7887_channels, + .num_channels = ARRAY_SIZE(ad7887_channels), + .dual_channels = ad7887_dual_channels, + .num_dual_channels = ARRAY_SIZE(ad7887_dual_channels), .int_vref_mv = 2500, }, }; @@ -306,11 +310,11 @@ static int ad7887_probe(struct spi_device *spi) spi_message_init(&st->msg[AD7887_CH1]); spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]); - indio_dev->channels = st->chip_info->channel; - indio_dev->num_channels = 3; + indio_dev->channels = st->chip_info->dual_channels; + indio_dev->num_channels = st->chip_info->num_dual_channels; } else { - indio_dev->channels = &st->chip_info->channel[1]; - indio_dev->num_channels = 2; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; } ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, -- cgit v1.2.3 From 52241a082d6c08f4993180752ed5a12b39712f03 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 27 Nov 2019 21:17:38 +0100 Subject: iio: imu: inv_mpu6050: Select I2C_MUX again commit f7072198f217 ("iio: imu: Fix inv_mpu6050 dependencies") undid the explicit selection of I2C_MUX previously done by the driver, because I2C_MUX implicitly depended on HAS_IOMEM. However commit 93d710a65ef0 ("i2c: mux: fix up dependencies") cleared up the situation properly and drivers that need to select I2C_MUX can now do so again. It makes a lot of sense for a driver to select the driver infrastructure it needs so restore the natural order of things. Cc: Richard Weinberger Cc: Stephan Gerhold Signed-off-by: Linus Walleij Acked-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 963a0cafe0cb..017bc0fcc365 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -10,7 +10,8 @@ config INV_MPU6050_IIO config INV_MPU6050_I2C tristate "Invensense MPU6050 devices (I2C)" - depends on I2C_MUX + depends on I2C + select I2C_MUX select INV_MPU6050_IIO select REGMAP_I2C help -- cgit v1.2.3 From d93813520df0ff6e0aec7c8859c56a659417f9b7 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Fri, 29 Nov 2019 17:53:14 +0100 Subject: iio: adc: ad799x: add pm_ops to disable the device completely The device is always in a low-power state due to the hardware design. It wakes up upon a conversion request and goes back into the low-power state. The pm ops are added to disable the device completely and to free the regulator. Disbaling the device completely should be not that notable but freeing the regulator is important. Because if it is a shared power-rail the regulator won't be disabled during suspend-to-ram/disk and so all devices connected to that rail keeps on. Signed-off-by: Marco Felsch Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 66 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index f658012baad8..ef013af1aec0 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -167,6 +167,21 @@ static int ad799x_read_config(struct ad799x_state *st) } } +static int ad799x_update_config(struct ad799x_state *st, u16 config) +{ + int ret; + + ret = ad799x_write_config(st, config); + if (ret < 0) + return ret; + ret = ad799x_read_config(st); + if (ret < 0) + return ret; + st->config = ret; + + return 0; +} + /** * ad799x_trigger_handler() bh of trigger launched polling to ring buffer * @@ -808,13 +823,9 @@ static int ad799x_probe(struct i2c_client *client, indio_dev->channels = st->chip_config->channel; indio_dev->num_channels = chip_info->num_channels; - ret = ad799x_write_config(st, st->chip_config->default_config); - if (ret < 0) - goto error_disable_vref; - ret = ad799x_read_config(st); - if (ret < 0) + ret = ad799x_update_config(st, st->chip_config->default_config); + if (ret) goto error_disable_vref; - st->config = ret; ret = iio_triggered_buffer_setup(indio_dev, NULL, &ad799x_trigger_handler, NULL); @@ -864,6 +875,48 @@ static int ad799x_remove(struct i2c_client *client) return 0; } +static int __maybe_unused ad799x_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ad799x_state *st = iio_priv(indio_dev); + + regulator_disable(st->vref); + regulator_disable(st->reg); + + return 0; +} + +static int __maybe_unused ad799x_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ad799x_state *st = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(st->reg); + if (ret) { + dev_err(dev, "Unable to enable vcc regulator\n"); + return ret; + } + ret = regulator_enable(st->vref); + if (ret) { + regulator_disable(st->reg); + dev_err(dev, "Unable to enable vref regulator\n"); + return ret; + } + + /* resync config */ + ret = ad799x_update_config(st, st->config); + if (ret) { + regulator_disable(st->vref); + regulator_disable(st->reg); + return ret; + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(ad799x_pm_ops, ad799x_suspend, ad799x_resume); + static const struct i2c_device_id ad799x_id[] = { { "ad7991", ad7991 }, { "ad7995", ad7995 }, @@ -881,6 +934,7 @@ MODULE_DEVICE_TABLE(i2c, ad799x_id); static struct i2c_driver ad799x_driver = { .driver = { .name = "ad799x", + .pm = &ad799x_pm_ops, }, .probe = ad799x_probe, .remove = ad799x_remove, -- cgit v1.2.3 From 2c289e63944467a41c6703f46bcf2f3340713510 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:17:30 +0100 Subject: iio: ak8975: Convert to use GPIO descriptor The end-of-conversion (EOC) GPIO line is better to grab using a GPIO descriptor. We drop the pdata for this: clients using board files can use machine descriptor tables to pass this GPIO from static data. Cc: Stephan Gerhold Signed-off-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 54 ++++++++++++++------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 893bec5a0312..55cffaa82456 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -16,8 +16,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -360,7 +359,7 @@ struct ak8975_data { struct mutex lock; u8 asa[3]; long raw_to_gauss[3]; - int eoc_gpio; + struct gpio_desc *eoc_gpiod; int eoc_irq; wait_queue_head_t data_ready_queue; unsigned long flags; @@ -498,15 +497,13 @@ static int ak8975_setup_irq(struct ak8975_data *data) if (client->irq) irq = client->irq; else - irq = gpio_to_irq(data->eoc_gpio); + irq = gpiod_to_irq(data->eoc_gpiod); rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev_name(&client->dev), data); if (rc < 0) { - dev_err(&client->dev, - "irq %d request failed, (gpio %d): %d\n", - irq, data->eoc_gpio, rc); + dev_err(&client->dev, "irq %d request failed: %d\n", irq, rc); return rc; } @@ -549,7 +546,7 @@ static int ak8975_setup(struct i2c_client *client) return ret; } - if (data->eoc_gpio > 0 || client->irq > 0) { + if (data->eoc_gpiod || client->irq > 0) { ret = ak8975_setup_irq(data); if (ret < 0) { dev_err(&client->dev, @@ -574,7 +571,7 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data) /* Wait for the conversion to complete. */ while (timeout_ms) { msleep(AK8975_CONVERSION_DONE_POLL_TIME); - if (gpio_get_value(data->eoc_gpio)) + if (gpiod_get_value(data->eoc_gpiod)) break; timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME; } @@ -646,7 +643,7 @@ static int ak8975_start_read_axis(struct ak8975_data *data, /* Wait for the conversion to complete. */ if (data->eoc_irq) ret = wait_conversion_complete_interrupt(data); - else if (gpio_is_valid(data->eoc_gpio)) + else if (data->eoc_gpiod) ret = wait_conversion_complete_gpio(data); else ret = wait_conversion_complete_polled(data); @@ -856,36 +853,23 @@ static int ak8975_probe(struct i2c_client *client, { struct ak8975_data *data; struct iio_dev *indio_dev; - int eoc_gpio; + struct gpio_desc *eoc_gpiod; int err; const char *name = NULL; enum asahi_compass_chipset chipset = AK_MAX_TYPE; const struct ak8975_platform_data *pdata = dev_get_platdata(&client->dev); - /* Grab and set up the supplied GPIO. */ - if (pdata) - eoc_gpio = pdata->eoc_gpio; - else if (client->dev.of_node) - eoc_gpio = of_get_gpio(client->dev.of_node, 0); - else - eoc_gpio = -1; - - if (eoc_gpio == -EPROBE_DEFER) - return -EPROBE_DEFER; - - /* We may not have a GPIO based IRQ to scan, that is fine, we will - poll if so */ - if (gpio_is_valid(eoc_gpio)) { - err = devm_gpio_request_one(&client->dev, eoc_gpio, - GPIOF_IN, "ak_8975"); - if (err < 0) { - dev_err(&client->dev, - "failed to request GPIO %d, error %d\n", - eoc_gpio, err); - return err; - } - } + /* + * Grab and set up the supplied GPIO. + * We may not have a GPIO based IRQ to scan, that is fine, we will + * poll if so. + */ + eoc_gpiod = devm_gpiod_get_optional(&client->dev, NULL, GPIOD_IN); + if (IS_ERR(eoc_gpiod)) + return PTR_ERR(eoc_gpiod); + if (eoc_gpiod) + gpiod_set_consumer_name(eoc_gpiod, "ak_8975"); /* Register with IIO */ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); @@ -896,7 +880,7 @@ static int ak8975_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); data->client = client; - data->eoc_gpio = eoc_gpio; + data->eoc_gpiod = eoc_gpiod; data->eoc_irq = 0; if (!pdata) { -- cgit v1.2.3 From 757b4bcaa0d8fa74500b14b1655f6638ceafd056 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 1 Dec 2019 23:38:18 +0200 Subject: iio: imu: st_lsm6dsx: fix checkpatch warning Fix following checkpatch warning: CHECK: Alignment should match open parenthesis +static int st_lsm6dsx_read_event(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 11b2c7bc8041..a4ed72aef93e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1587,11 +1587,11 @@ static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, int state) } static int st_lsm6dsx_read_event(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); struct st_lsm6dsx_hw *hw = sensor->hw; -- cgit v1.2.3 From b4cc7e83dff352cb7ecdf561500d815bce0e3f5b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:19:43 +0100 Subject: iio: as3935: Drop GPIO includes The driver includes and yet fails to use symbols from any of the include files, so drop these includes. Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index b591c63bd6c4..bac9a433dd19 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -22,8 +21,6 @@ #include #include #include -#include - #define AS3935_AFE_GAIN 0x00 #define AS3935_AFE_MASK 0x3F -- cgit v1.2.3 From cbe5997907235f71e6b0ab281b40e0cd151222d4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:21:23 +0100 Subject: iio: si1145: Drop GPIO include The driver include yet does not use any of the symbols from the header, so drop the include. Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1145.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index 982bba0c54e7..0476c2bc8138 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From 340db461f9acbd3621aad8f1ffca811847aa0165 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:23:01 +0100 Subject: iio: ad2s1200: Drop legacy include This driver is using the GPIO descriptor API but yet includes the legacy header for no reason. Drop the surplus include. Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/resolver/ad2s1200.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c index 17b89623418c..a391f46ee06b 100644 --- a/drivers/iio/resolver/ad2s1200.c +++ b/drivers/iio/resolver/ad2s1200.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 7544cd4c8ab72f9a18d3828a10524ce9740858a1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:24:52 +0100 Subject: iio: apds9960: Drop GPIO includes The driver includes and yet fails to use symbols from any of the include files, so drop these includes. Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/light/apds9960.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index c5dfb9a6b5a1..52f86bc777dd 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -24,7 +23,6 @@ #include #include #include -#include #define APDS9960_REGMAP_NAME "apds9960_regmap" #define APDS9960_DRV_NAME "apds9960" -- cgit v1.2.3 From dcfb6dbdfbe0ee209ec5e7b79ac45adce3a31e3a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:26:03 +0100 Subject: iio: itg3200: Drop GPIO include The driver include yet does not use any of the symbols from the header, so drop the include. Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/itg3200_core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c index 981ae2291505..b3afa556f973 100644 --- a/drivers/iio/gyro/itg3200_core.c +++ b/drivers/iio/gyro/itg3200_core.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 4a89d2f47ccdce69597ba2f92e0b55c9a1f1d1ea Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:38:30 +0100 Subject: iio: adf4350: Convert to use GPIO descriptor The lock detect GPIO line is better to grab using a GPIO descriptor. We drop the pdata for this: clients using board files can use machine descriptor tables to pass this GPIO from static data. Cc: Michael Hennerich Signed-off-by: Linus Walleij Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4350.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index ae0ca09ae062..1c2dc9b00f31 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -14,11 +14,10 @@ #include #include #include -#include +#include #include #include #include -#include #include #include @@ -34,6 +33,7 @@ enum { struct adf4350_state { struct spi_device *spi; struct regulator *reg; + struct gpio_desc *lock_detect_gpiod; struct adf4350_platform_data *pdata; struct clk *clk; unsigned long clkin; @@ -61,7 +61,6 @@ static struct adf4350_platform_data default_pdata = { .r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0), .r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) | ADF4350_REG4_MUTE_TILL_LOCK_EN, - .gpio_lock_detect = -1, }; static int adf4350_sync_config(struct adf4350_state *st) @@ -317,8 +316,8 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev, (u64)st->fpfd; do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel)); /* PLL unlocked? return error */ - if (gpio_is_valid(st->pdata->gpio_lock_detect)) - if (!gpio_get_value(st->pdata->gpio_lock_detect)) { + if (st->lock_detect_gpiod) + if (!gpiod_get_value(st->lock_detect_gpiod)) { dev_dbg(&st->spi->dev, "PLL un-locked\n"); ret = -EBUSY; } @@ -381,7 +380,6 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) struct device_node *np = dev->of_node; struct adf4350_platform_data *pdata; unsigned int tmp; - int ret; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -401,12 +399,6 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) of_property_read_u32(np, "adi,reference-div-factor", &tmp); pdata->ref_div_factor = tmp; - ret = of_get_gpio(np, 0); - if (ret < 0) - pdata->gpio_lock_detect = -1; - else - pdata->gpio_lock_detect = ret; - pdata->ref_doubler_en = of_property_read_bool(np, "adi,reference-doubler-enable"); pdata->ref_div2_en = of_property_read_bool(np, @@ -561,16 +553,10 @@ static int adf4350_probe(struct spi_device *spi) memset(st->regs_hw, 0xFF, sizeof(st->regs_hw)); - if (gpio_is_valid(pdata->gpio_lock_detect)) { - ret = devm_gpio_request(&spi->dev, pdata->gpio_lock_detect, - indio_dev->name); - if (ret) { - dev_err(&spi->dev, "fail to request lock detect GPIO-%d", - pdata->gpio_lock_detect); - goto error_disable_reg; - } - gpio_direction_input(pdata->gpio_lock_detect); - } + st->lock_detect_gpiod = devm_gpiod_get_optional(&spi->dev, NULL, + GPIOD_IN); + if (IS_ERR(st->lock_detect_gpiod)) + return PTR_ERR(st->lock_detect_gpiod); if (pdata->power_up_frequency) { ret = adf4350_set_freq(st, pdata->power_up_frequency); -- cgit v1.2.3 From b747e352499e4fca6e2e824852f5d91f4e29bf0e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:58:48 +0100 Subject: iio: ad5592r: Drop surplus GPIO header This driver uses all the modern GPIO APIs from and so just drop the unused legacy header . Signed-off-by: Linus Walleij Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5592r-base.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 2d897e64c6a9..e2110113e884 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From 5750ebab14907f05aa9904ef2f3c1bb8dcb1fd2b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 10:38:06 +0100 Subject: iio: ad7266: Convert to use GPIO descriptors The AD7266 have no in-tree users making use of the platform data mechanism to pass address GPIO lines when not using a fixed address, so we can easily convert this to use GPIO descriptors instead of the platform data integers currently passed. Lowercase the labels "ad0".."ad2" as this will make a better fit for platform descriptions like device tree that prefer lowercase names such as "ad0-gpios" rather than "AD0-gpios". Board files and other static users of this device can pass the same GPIO descriptors using machine descriptor tables if need be. Cc: Alison Schofield Cc: Lars-Peter Clausen Signed-off-by: Linus Walleij Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7266.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index c31b8eabb894..c8524f098883 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include @@ -34,7 +34,7 @@ struct ad7266_state { enum ad7266_range range; enum ad7266_mode mode; bool fixed_addr; - struct gpio gpios[3]; + struct gpio_desc *gpios[3]; /* * DMA (thus cache coherency maintenance) requires the @@ -117,7 +117,7 @@ static void ad7266_select_input(struct ad7266_state *st, unsigned int nr) } for (i = 0; i < 3; ++i) - gpio_set_value(st->gpios[i].gpio, (bool)(nr & BIT(i))); + gpiod_set_value(st->gpios[i], (bool)(nr & BIT(i))); } static int ad7266_update_scan_mode(struct iio_dev *indio_dev, @@ -376,7 +376,7 @@ static void ad7266_init_channels(struct iio_dev *indio_dev) } static const char * const ad7266_gpio_labels[] = { - "AD0", "AD1", "AD2", + "ad0", "ad1", "ad2", }; static int ad7266_probe(struct spi_device *spi) @@ -419,14 +419,14 @@ static int ad7266_probe(struct spi_device *spi) if (!st->fixed_addr) { for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) { - st->gpios[i].gpio = pdata->addr_gpios[i]; - st->gpios[i].flags = GPIOF_OUT_INIT_LOW; - st->gpios[i].label = ad7266_gpio_labels[i]; + st->gpios[i] = devm_gpiod_get(&spi->dev, + ad7266_gpio_labels[i], + GPIOD_OUT_LOW); + if (IS_ERR(st->gpios[i])) { + ret = PTR_ERR(st->gpios[i]); + goto error_disable_reg; + } } - ret = gpio_request_array(st->gpios, - ARRAY_SIZE(st->gpios)); - if (ret) - goto error_disable_reg; } } else { st->fixed_addr = true; @@ -465,7 +465,7 @@ static int ad7266_probe(struct spi_device *spi) ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, &ad7266_trigger_handler, &iio_triggered_buffer_setup_ops); if (ret) - goto error_free_gpios; + goto error_disable_reg; ret = iio_device_register(indio_dev); if (ret) @@ -475,9 +475,6 @@ static int ad7266_probe(struct spi_device *spi) error_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); -error_free_gpios: - if (!st->fixed_addr) - gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios)); error_disable_reg: if (!IS_ERR(st->reg)) regulator_disable(st->reg); @@ -492,8 +489,6 @@ static int ad7266_remove(struct spi_device *spi) iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); - if (!st->fixed_addr) - gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios)); if (!IS_ERR(st->reg)) regulator_disable(st->reg); -- cgit v1.2.3 From 9eda18273754ca9bb7204786866666137c8e7d55 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 10:18:37 +0100 Subject: iio: atlas-ph-sensor: Drop GPIO include The driver includes yet fails to use symbols from any the header so drop the include. Signed-off-by: Linus Walleij Reviewed-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-ph-sensor.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index 6c175eb1c7a7..b7c20c74239b 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 7c93f54e5bfb04ae90e330f8276c9311b03b1086 Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Tue, 3 Dec 2019 12:17:13 +0200 Subject: iio: adc: Move AD7091R5 entry in a alphabetical order in Makefile Ad7091R5 was added in a non alphabetical order after AD7124 in Makefile and KConfig. This patch fixes that and place Ad7091R5 before AD7124. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 14 +++++++------- drivers/iio/adc/Makefile | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 976567d4dbef..27bb4e56eaea 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -21,6 +21,13 @@ config AD_SIGMA_DELTA select IIO_BUFFER select IIO_TRIGGERED_BUFFER +config AD7091R5 + tristate "Analog Devices AD7091R5 ADC Driver" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for Analog Devices AD7091R-5 ADC. + config AD7124 tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver" depends on SPI_MASTER @@ -32,13 +39,6 @@ config AD7124 To compile this driver as a module, choose M here: the module will be called ad7124. -config AD7091R5 - tristate "Analog Devices AD7091R5 ADC Driver" - depends on I2C - select REGMAP_I2C - help - Say yes here to build support for Analog Devices AD7091R-5 ADC. - config AD7266 tristate "Analog Devices AD7265/AD7266 ADC driver" depends on SPI_MASTER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 84936ec24411..bf8354cdbc34 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -6,8 +6,8 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o -obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o +obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7292) += ad7292.o -- cgit v1.2.3 From 4cf01d6d4eea2e3841aeaa4cb977e73ab5efab10 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 2 Dec 2019 09:53:50 +0100 Subject: iio: ssp_sensors: Convert to use GPIO descriptors These three GPIO lines used by the Samsung sensor hub is pretty straight-forward to convert to use GPIO descriptors. Cc: Karol Wrona Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/common/ssp_sensors/ssp.h | 14 +++++++------- drivers/iio/common/ssp_sensors/ssp_dev.c | 29 +++++++++-------------------- drivers/iio/common/ssp_sensors/ssp_spi.c | 8 ++++---- 3 files changed, 20 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/common/ssp_sensors/ssp.h b/drivers/iio/common/ssp_sensors/ssp.h index 0a381bb1ae6f..abb832795619 100644 --- a/drivers/iio/common/ssp_sensors/ssp.h +++ b/drivers/iio/common/ssp_sensors/ssp.h @@ -7,7 +7,7 @@ #define __SSP_SENSORHUB_H__ #include -#include +#include #include #include #include @@ -168,9 +168,9 @@ struct ssp_sensorhub_info { * @fw_dl_state: firmware download state * @comm_lock: lock protecting the handshake * @pending_lock: lock protecting pending list and completion - * @mcu_reset_gpio: mcu reset line - * @ap_mcu_gpio: ap to mcu gpio line - * @mcu_ap_gpio: mcu to ap gpio line + * @mcu_reset_gpiod: mcu reset line + * @ap_mcu_gpiod: ap to mcu gpio line + * @mcu_ap_gpiod: mcu to ap gpio line * @pending_list: pending list for messages queued to be sent/read * @sensor_devs: registered IIO devices table * @enable_refcount: enable reference count for wdt (watchdog timer) @@ -212,9 +212,9 @@ struct ssp_data { struct mutex comm_lock; struct mutex pending_lock; - int mcu_reset_gpio; - int ap_mcu_gpio; - int mcu_ap_gpio; + struct gpio_desc *mcu_reset_gpiod; + struct gpio_desc *ap_mcu_gpiod; + struct gpio_desc *mcu_ap_gpiod; struct list_head pending_list; diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index 9c70553994c6..a94dbcf491ce 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include "ssp.h" @@ -61,9 +60,9 @@ static const struct mfd_cell sensorhub_sensor_devs[] = { static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data) { - gpio_set_value(data->mcu_reset_gpio, 0); + gpiod_set_value(data->mcu_reset_gpiod, 0); usleep_range(1000, 1200); - gpio_set_value(data->mcu_reset_gpio, 1); + gpiod_set_value(data->mcu_reset_gpiod, 1); msleep(50); } @@ -441,7 +440,6 @@ MODULE_DEVICE_TABLE(of, ssp_of_match); static struct ssp_data *ssp_parse_dt(struct device *dev) { - int ret; struct ssp_data *data; struct device_node *node = dev->of_node; const struct of_device_id *match; @@ -450,26 +448,17 @@ static struct ssp_data *ssp_parse_dt(struct device *dev) if (!data) return NULL; - data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0); - if (data->mcu_ap_gpio < 0) - return NULL; - - data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0); - if (data->ap_mcu_gpio < 0) - return NULL; - - data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0); - if (data->mcu_reset_gpio < 0) + data->mcu_ap_gpiod = devm_gpiod_get(dev, "mcu-ap", GPIOD_IN); + if (IS_ERR(data->mcu_ap_gpiod)) return NULL; - ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH, - "ap-mcu-gpios"); - if (ret) + data->ap_mcu_gpiod = devm_gpiod_get(dev, "ap-mcu", GPIOD_OUT_HIGH); + if (IS_ERR(data->ap_mcu_gpiod)) return NULL; - ret = devm_gpio_request_one(dev, data->mcu_reset_gpio, - GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios"); - if (ret) + data->mcu_reset_gpiod = devm_gpiod_get(dev, "mcu-reset", + GPIOD_OUT_HIGH); + if (IS_ERR(data->mcu_reset_gpiod)) return NULL; match = of_match_node(ssp_of_match, node); diff --git a/drivers/iio/common/ssp_sensors/ssp_spi.c b/drivers/iio/common/ssp_sensors/ssp_spi.c index 7db3d5886e3e..4864c38b8d1c 100644 --- a/drivers/iio/common/ssp_sensors/ssp_spi.c +++ b/drivers/iio/common/ssp_sensors/ssp_spi.c @@ -155,9 +155,9 @@ static int ssp_check_lines(struct ssp_data *data, bool state) { int delay_cnt = 0; - gpio_set_value_cansleep(data->ap_mcu_gpio, state); + gpiod_set_value_cansleep(data->ap_mcu_gpiod, state); - while (gpio_get_value_cansleep(data->mcu_ap_gpio) != state) { + while (gpiod_get_value_cansleep(data->mcu_ap_gpiod) != state) { usleep_range(3000, 3500); if (data->shut_down || delay_cnt++ > 500) { @@ -165,7 +165,7 @@ static int ssp_check_lines(struct ssp_data *data, bool state) __func__, state); if (!state) - gpio_set_value_cansleep(data->ap_mcu_gpio, 1); + gpiod_set_value_cansleep(data->ap_mcu_gpiod, 1); return -ETIMEDOUT; } @@ -197,7 +197,7 @@ static int ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg, status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE); if (status < 0) { - gpio_set_value_cansleep(data->ap_mcu_gpio, 1); + gpiod_set_value_cansleep(data->ap_mcu_gpiod, 1); dev_err(SSP_DEV, "%s spi_write fail\n", __func__); goto _error_locked; } -- cgit v1.2.3 From cc06e67d8fa55d000caeb4613e8873aed2c171ff Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 2 Dec 2019 10:02:19 +0100 Subject: iio: adc: stm32-adc: Add check on overrun interrupt Enable overrun interrupt on STM32 ADC. In case data register hasn't been read (by CPU or DMA), overrun condition is detected when there's new conversion data available. Stop grabbing data and log an error message. Use a threaded irq to avoid printing the error message from hard irq context. Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 14 +++++----- drivers/iio/adc/stm32-adc-core.h | 9 +++++++ drivers/iio/adc/stm32-adc.c | 55 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 6537f4f776c5..97655d7fc11a 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -280,21 +280,21 @@ out: static const struct stm32_adc_common_regs stm32f4_adc_common_regs = { .csr = STM32F4_ADC_CSR, .ccr = STM32F4_ADC_CCR, - .eoc1_msk = STM32F4_EOC1, - .eoc2_msk = STM32F4_EOC2, - .eoc3_msk = STM32F4_EOC3, + .eoc1_msk = STM32F4_EOC1 | STM32F4_OVR1, + .eoc2_msk = STM32F4_EOC2 | STM32F4_OVR2, + .eoc3_msk = STM32F4_EOC3 | STM32F4_OVR3, .ier = STM32F4_ADC_CR1, - .eocie_msk = STM32F4_EOCIE, + .eocie_msk = STM32F4_EOCIE | STM32F4_OVRIE, }; /* STM32H7 common registers definitions */ static const struct stm32_adc_common_regs stm32h7_adc_common_regs = { .csr = STM32H7_ADC_CSR, .ccr = STM32H7_ADC_CCR, - .eoc1_msk = STM32H7_EOC_MST, - .eoc2_msk = STM32H7_EOC_SLV, + .eoc1_msk = STM32H7_EOC_MST | STM32H7_OVR_MST, + .eoc2_msk = STM32H7_EOC_SLV | STM32H7_OVR_SLV, .ier = STM32H7_ADC_IER, - .eocie_msk = STM32H7_EOCIE, + .eocie_msk = STM32H7_EOCIE | STM32H7_OVRIE, }; static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = { diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index 2579d514c2a3..2322809bfd2f 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -51,10 +51,12 @@ #define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04) /* STM32F4_ADC_SR - bit fields */ +#define STM32F4_OVR BIT(5) #define STM32F4_STRT BIT(4) #define STM32F4_EOC BIT(1) /* STM32F4_ADC_CR1 - bit fields */ +#define STM32F4_OVRIE BIT(26) #define STM32F4_RES_SHIFT 24 #define STM32F4_RES_MASK GENMASK(25, 24) #define STM32F4_SCAN BIT(8) @@ -72,8 +74,11 @@ #define STM32F4_ADON BIT(0) /* STM32F4_ADC_CSR - bit fields */ +#define STM32F4_OVR3 BIT(21) #define STM32F4_EOC3 BIT(17) +#define STM32F4_OVR2 BIT(13) #define STM32F4_EOC2 BIT(9) +#define STM32F4_OVR1 BIT(5) #define STM32F4_EOC1 BIT(1) /* STM32F4_ADC_CCR - bit fields */ @@ -103,10 +108,12 @@ /* STM32H7_ADC_ISR - bit fields */ #define STM32MP1_VREGREADY BIT(12) +#define STM32H7_OVR BIT(4) #define STM32H7_EOC BIT(2) #define STM32H7_ADRDY BIT(0) /* STM32H7_ADC_IER - bit fields */ +#define STM32H7_OVRIE STM32H7_OVR #define STM32H7_EOCIE STM32H7_EOC /* STM32H7_ADC_CR - bit fields */ @@ -155,7 +162,9 @@ enum stm32h7_adc_dmngt { #define STM32H7_LINCALFACT_MASK GENMASK(29, 0) /* STM32H7_ADC_CSR - bit fields */ +#define STM32H7_OVR_SLV BIT(20) #define STM32H7_EOC_SLV BIT(18) +#define STM32H7_OVR_MST BIT(4) #define STM32H7_EOC_MST BIT(2) /* STM32H7_ADC_CCR - bit fields */ diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 3b291d72701c..5f05bf9f16ea 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -117,7 +117,9 @@ struct stm32_adc_regs { * struct stm32_adc_regspec - stm32 registers definition * @dr: data register offset * @ier_eoc: interrupt enable register & eocie bitfield + * @ier_ovr: interrupt enable register & overrun bitfield * @isr_eoc: interrupt status register & eoc bitfield + * @isr_ovr: interrupt status register & overrun bitfield * @sqr: reference to sequence registers array * @exten: trigger control register & bitfield * @extsel: trigger selection register & bitfield @@ -128,7 +130,9 @@ struct stm32_adc_regs { struct stm32_adc_regspec { const u32 dr; const struct stm32_adc_regs ier_eoc; + const struct stm32_adc_regs ier_ovr; const struct stm32_adc_regs isr_eoc; + const struct stm32_adc_regs isr_ovr; const struct stm32_adc_regs *sqr; const struct stm32_adc_regs exten; const struct stm32_adc_regs extsel; @@ -337,7 +341,9 @@ static const unsigned int stm32f4_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = { static const struct stm32_adc_regspec stm32f4_adc_regspec = { .dr = STM32F4_ADC_DR, .ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE }, + .ier_ovr = { STM32F4_ADC_CR1, STM32F4_OVRIE }, .isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC }, + .isr_ovr = { STM32F4_ADC_SR, STM32F4_OVR }, .sqr = stm32f4_sq, .exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT }, .extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK, @@ -429,7 +435,9 @@ static const unsigned int stm32h7_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = { static const struct stm32_adc_regspec stm32h7_adc_regspec = { .dr = STM32H7_ADC_DR, .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE }, + .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE }, .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC }, + .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR }, .sqr = stm32h7_sq, .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT }, .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK, @@ -506,6 +514,18 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc) adc->cfg->regs->ier_eoc.mask); } +static void stm32_adc_ovr_irq_enable(struct stm32_adc *adc) +{ + stm32_adc_set_bits(adc, adc->cfg->regs->ier_ovr.reg, + adc->cfg->regs->ier_ovr.mask); +} + +static void stm32_adc_ovr_irq_disable(struct stm32_adc *adc) +{ + stm32_adc_clr_bits(adc, adc->cfg->regs->ier_ovr.reg, + adc->cfg->regs->ier_ovr.mask); +} + static void stm32_adc_set_res(struct stm32_adc *adc) { const struct stm32_adc_regs *res = &adc->cfg->regs->res; @@ -1205,6 +1225,19 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, } } +static irqreturn_t stm32_adc_threaded_isr(int irq, void *data) +{ + struct stm32_adc *adc = data; + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + const struct stm32_adc_regspec *regs = adc->cfg->regs; + u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg); + + if (status & regs->isr_ovr.mask) + dev_err(&indio_dev->dev, "Overrun, stopping: restart needed\n"); + + return IRQ_HANDLED; +} + static irqreturn_t stm32_adc_isr(int irq, void *data) { struct stm32_adc *adc = data; @@ -1212,6 +1245,19 @@ static irqreturn_t stm32_adc_isr(int irq, void *data) const struct stm32_adc_regspec *regs = adc->cfg->regs; u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg); + if (status & regs->isr_ovr.mask) { + /* + * Overrun occurred on regular conversions: data for wrong + * channel may be read. Unconditionally disable interrupts + * to stop processing data and print error message. + * Restarting the capture can be done by disabling, then + * re-enabling it (e.g. write 0, then 1 to buffer/enable). + */ + stm32_adc_ovr_irq_disable(adc); + stm32_adc_conv_irq_disable(adc); + return IRQ_WAKE_THREAD; + } + if (status & regs->isr_eoc.mask) { /* Reading DR also clears EOC status flag */ adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr); @@ -1441,6 +1487,8 @@ static int __stm32_adc_buffer_postenable(struct iio_dev *indio_dev) /* Reset adc buffer index */ adc->bufi = 0; + stm32_adc_ovr_irq_enable(adc); + if (!adc->dma_chan) stm32_adc_conv_irq_enable(adc); @@ -1481,6 +1529,8 @@ static void __stm32_adc_buffer_predisable(struct iio_dev *indio_dev) if (!adc->dma_chan) stm32_adc_conv_irq_disable(adc); + stm32_adc_ovr_irq_disable(adc); + if (adc->dma_chan) dmaengine_terminate_sync(adc->dma_chan); @@ -1818,8 +1868,9 @@ static int stm32_adc_probe(struct platform_device *pdev) if (adc->irq < 0) return adc->irq; - ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr, - 0, pdev->name, adc); + ret = devm_request_threaded_irq(&pdev->dev, adc->irq, stm32_adc_isr, + stm32_adc_threaded_isr, + 0, pdev->name, adc); if (ret) { dev_err(&pdev->dev, "failed to request IRQ\n"); return ret; -- cgit v1.2.3 From 09a78f7dfac7b3a333fecd0839403f844b1a308e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 2 Dec 2019 12:23:28 +0200 Subject: iio: humidity: hts221: move register definitions to sensor structs Move some register definitions to hts221_avg_list, hts221_avg_list and hts221_channels since they are used only there and simplify driver code Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_core.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index 4922444771c6..9003671f14fb 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -24,13 +24,6 @@ #define HTS221_REG_CNTRL1_ADDR 0x20 #define HTS221_REG_CNTRL2_ADDR 0x21 -#define HTS221_REG_AVG_ADDR 0x10 -#define HTS221_REG_H_OUT_L 0x28 -#define HTS221_REG_T_OUT_L 0x2a - -#define HTS221_HUMIDITY_AVG_MASK 0x07 -#define HTS221_TEMP_AVG_MASK 0x38 - #define HTS221_ODR_MASK 0x03 #define HTS221_BDU_MASK BIT(2) #define HTS221_ENABLE_MASK BIT(7) @@ -66,8 +59,8 @@ static const struct hts221_odr hts221_odr_table[] = { static const struct hts221_avg hts221_avg_list[] = { { - .addr = HTS221_REG_AVG_ADDR, - .mask = HTS221_HUMIDITY_AVG_MASK, + .addr = 0x10, + .mask = 0x07, .avg_avl = { 4, /* 0.4 %RH */ 8, /* 0.3 %RH */ @@ -80,8 +73,8 @@ static const struct hts221_avg hts221_avg_list[] = { }, }, { - .addr = HTS221_REG_AVG_ADDR, - .mask = HTS221_TEMP_AVG_MASK, + .addr = 0x10, + .mask = 0x38, .avg_avl = { 2, /* 0.08 degC */ 4, /* 0.05 degC */ @@ -98,7 +91,7 @@ static const struct hts221_avg hts221_avg_list[] = { static const struct iio_chan_spec hts221_channels[] = { { .type = IIO_HUMIDITYRELATIVE, - .address = HTS221_REG_H_OUT_L, + .address = 0x28, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | @@ -114,7 +107,7 @@ static const struct iio_chan_spec hts221_channels[] = { }, { .type = IIO_TEMP, - .address = HTS221_REG_T_OUT_L, + .address = 0x2a, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | -- cgit v1.2.3 From 32bd4324601dc15a205f2b734e67ee5fd740ee66 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 4 Dec 2019 07:45:35 +0100 Subject: iio: adc: ti-ads7950: Fix a typo in an error message Fix a typo: s/get get/to get/ Signed-off-by: Christophe JAILLET Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 2e66e4d586ff..f9edc1207f75 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -602,7 +602,7 @@ static int ti_ads7950_probe(struct spi_device *spi) st->reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(st->reg)) { - dev_err(&spi->dev, "Failed get get regulator \"vref\"\n"); + dev_err(&spi->dev, "Failed to get regulator \"vref\"\n"); ret = PTR_ERR(st->reg); goto error_destroy_mutex; } -- cgit v1.2.3 From 0cd9ff1535f4339710dfd577a7d91b96fd4b423b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 5 Dec 2019 19:46:36 +0200 Subject: iio: adc: ti-ads1015: Get rid of legacy platform data Platform data is a legacy interface to supply device properties to the driver. In this case we even don't have in-kernel users for it. Just remove it for good. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index a550b132cfb7..3b123b4f0b99 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -21,8 +21,6 @@ #include #include -#include - #include #include #include @@ -33,6 +31,8 @@ #define ADS1015_DRV_NAME "ads1015" +#define ADS1015_CHANNELS 8 + #define ADS1015_CONV_REG 0x00 #define ADS1015_CFG_REG 0x01 #define ADS1015_LO_THRESH_REG 0x02 @@ -219,6 +219,12 @@ static const struct iio_event_spec ads1015_events[] = { .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \ } +struct ads1015_channel_data { + bool enabled; + unsigned int pga; + unsigned int data_rate; +}; + struct ads1015_thresh_data { unsigned int comp_queue; int high_thresh; @@ -903,14 +909,6 @@ static void ads1015_get_channels_config(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct ads1015_data *data = iio_priv(indio_dev); - struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev); - - /* prefer platform data */ - if (pdata) { - memcpy(data->channel_data, pdata->channel_data, - sizeof(data->channel_data)); - return; - } #ifdef CONFIG_OF if (!ads1015_get_channels_config_of(client)) -- cgit v1.2.3 From 64335c4a6720017c45e0c07313a7a819e55a084a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 5 Dec 2019 19:46:37 +0200 Subject: iio: adc: ti-ads1015: Make use of device property API Make use of device property API in this driver so that both OF based system and ACPI based system can use this driver. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 57 ++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 3b123b4f0b99..5ea4f45d6bad 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -12,10 +12,10 @@ */ #include -#include #include #include #include +#include #include #include #include @@ -77,6 +77,7 @@ #define ADS1015_DEFAULT_CHAN 0 enum chip_ids { + ADSXXXX = 0, ADS1015, ADS1115, }; @@ -843,65 +844,58 @@ static const struct iio_info ads1115_info = { .attrs = &ads1115_attribute_group, }; -#ifdef CONFIG_OF -static int ads1015_get_channels_config_of(struct i2c_client *client) +static int ads1015_client_get_channels_config(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct ads1015_data *data = iio_priv(indio_dev); - struct device_node *node; + struct device *dev = &client->dev; + struct fwnode_handle *node; + int i = -1; - if (!client->dev.of_node || - !of_get_next_child(client->dev.of_node, NULL)) - return -EINVAL; - - for_each_child_of_node(client->dev.of_node, node) { + device_for_each_child_node(dev, node) { u32 pval; unsigned int channel; unsigned int pga = ADS1015_DEFAULT_PGA; unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; - if (of_property_read_u32(node, "reg", &pval)) { - dev_err(&client->dev, "invalid reg on %pOF\n", - node); + if (fwnode_property_read_u32(node, "reg", &pval)) { + dev_err(dev, "invalid reg on %pfw\n", node); continue; } channel = pval; if (channel >= ADS1015_CHANNELS) { - dev_err(&client->dev, - "invalid channel index %d on %pOF\n", + dev_err(dev, "invalid channel index %d on %pfw\n", channel, node); continue; } - if (!of_property_read_u32(node, "ti,gain", &pval)) { + if (!fwnode_property_read_u32(node, "ti,gain", &pval)) { pga = pval; if (pga > 6) { - dev_err(&client->dev, "invalid gain on %pOF\n", - node); - of_node_put(node); + dev_err(dev, "invalid gain on %pfw\n", node); + fwnode_handle_put(node); return -EINVAL; } } - if (!of_property_read_u32(node, "ti,datarate", &pval)) { + if (!fwnode_property_read_u32(node, "ti,datarate", &pval)) { data_rate = pval; if (data_rate > 7) { - dev_err(&client->dev, - "invalid data_rate on %pOF\n", - node); - of_node_put(node); + dev_err(dev, "invalid data_rate on %pfw\n", node); + fwnode_handle_put(node); return -EINVAL; } } data->channel_data[channel].pga = pga; data->channel_data[channel].data_rate = data_rate; + + i++; } - return 0; + return i < 0 ? -EINVAL : 0; } -#endif static void ads1015_get_channels_config(struct i2c_client *client) { @@ -910,10 +904,9 @@ static void ads1015_get_channels_config(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct ads1015_data *data = iio_priv(indio_dev); -#ifdef CONFIG_OF - if (!ads1015_get_channels_config_of(client)) + if (!ads1015_client_get_channels_config(client)) return; -#endif + /* fallback on default configuration */ for (k = 0; k < ADS1015_CHANNELS; ++k) { data->channel_data[k].pga = ADS1015_DEFAULT_PGA; @@ -951,9 +944,8 @@ static int ads1015_probe(struct i2c_client *client, indio_dev->name = ADS1015_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - if (client->dev.of_node) - chip = (enum chip_ids)of_device_get_match_data(&client->dev); - else + chip = (enum chip_ids)device_get_match_data(&client->dev); + if (chip == ADSXXXX) chip = id->driver_data; switch (chip) { case ADS1015: @@ -968,6 +960,9 @@ static int ads1015_probe(struct i2c_client *client, indio_dev->info = &ads1115_info; data->data_rate = (unsigned int *) &ads1115_data_rate; break; + default: + dev_err(&client->dev, "Unknown chip %d\n", chip); + return -EINVAL; } data->event_channel = ADS1015_CHANNELS; -- cgit v1.2.3 From 69548b7c2c4f1ab9a135f2050d9683c7691ef701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 9 Dec 2019 21:32:47 +0100 Subject: iio: adc: ltc2497: split protocol independent part in a separate module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to share most of this driver for the ltc2496 driver added in the next commit that is an SPI variant of the ltc2497. Initially I named the generic part ltc249x, but wild card names are frowned upon, so the generic part is called ltc2497-core even though it's not obvious that this is then to be reused for the ltc2496 driver. Signed-off-by: Uwe Kleine-König Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Makefile | 2 +- drivers/iio/adc/ltc2497-core.c | 243 +++++++++++++++++++++++++++++++++++++++++ drivers/iio/adc/ltc2497.c | 234 +++++---------------------------------- drivers/iio/adc/ltc2497.h | 18 +++ 4 files changed, 288 insertions(+), 209 deletions(-) create mode 100644 drivers/iio/adc/ltc2497-core.c create mode 100644 drivers/iio/adc/ltc2497.h (limited to 'drivers') diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index bf8354cdbc34..5f8d7ac6bbe2 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -51,7 +51,7 @@ obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o obj-$(CONFIG_LTC2471) += ltc2471.o obj-$(CONFIG_LTC2485) += ltc2485.o -obj-$(CONFIG_LTC2497) += ltc2497.o +obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX11100) += max11100.o obj-$(CONFIG_MAX1118) += max1118.o diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c new file mode 100644 index 000000000000..f5f7039caacc --- /dev/null +++ b/drivers/iio/adc/ltc2497-core.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2497-core.c - Common code for Analog Devices/Linear Technology + * LTC2496 and LTC2497 ADCs + * + * Copyright (C) 2017 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include + +#include "ltc2497.h" + +#define LTC2497_SGL BIT(4) +#define LTC2497_DIFF 0 +#define LTC2497_SIGN BIT(3) + +static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata) +{ + s64 time_elapsed; + + time_elapsed = ktime_ms_delta(ktime_get(), ddata->time_prev); + + if (time_elapsed < LTC2497_CONVERSION_TIME_MS) { + /* delay if conversion time not passed + * since last read or write + */ + if (msleep_interruptible( + LTC2497_CONVERSION_TIME_MS - time_elapsed)) + return -ERESTARTSYS; + + return 0; + } + + if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) { + /* We're in automatic mode - + * so the last reading is still not outdated + */ + return 0; + } + + return 1; +} + +static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, int *val) +{ + int ret; + + ret = ltc2497core_wait_conv(ddata); + if (ret < 0) + return ret; + + if (ret || ddata->addr_prev != address) { + ret = ddata->result_and_measure(ddata, address, NULL); + if (ret < 0) + return ret; + ddata->addr_prev = address; + + if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS)) + return -ERESTARTSYS; + } + + ret = ddata->result_and_measure(ddata, address, val); + if (ret < 0) + return ret; + + ddata->time_prev = ktime_get(); + + return ret; +} + +static int ltc2497core_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = ltc2497core_read(ddata, chan->address, val); + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(ddata->ref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = 17; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +#define LTC2497_CHAN(_chan, _addr, _ds_name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = (_ds_name), \ +} + +#define LTC2497_CHAN_DIFF(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \ + .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\ + .address = (_addr | _chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .differential = 1, \ +} + +static const struct iio_chan_spec ltc2497core_channel[] = { + LTC2497_CHAN(0, LTC2497_SGL, "CH0"), + LTC2497_CHAN(1, LTC2497_SGL, "CH1"), + LTC2497_CHAN(2, LTC2497_SGL, "CH2"), + LTC2497_CHAN(3, LTC2497_SGL, "CH3"), + LTC2497_CHAN(4, LTC2497_SGL, "CH4"), + LTC2497_CHAN(5, LTC2497_SGL, "CH5"), + LTC2497_CHAN(6, LTC2497_SGL, "CH6"), + LTC2497_CHAN(7, LTC2497_SGL, "CH7"), + LTC2497_CHAN(8, LTC2497_SGL, "CH8"), + LTC2497_CHAN(9, LTC2497_SGL, "CH9"), + LTC2497_CHAN(10, LTC2497_SGL, "CH10"), + LTC2497_CHAN(11, LTC2497_SGL, "CH11"), + LTC2497_CHAN(12, LTC2497_SGL, "CH12"), + LTC2497_CHAN(13, LTC2497_SGL, "CH13"), + LTC2497_CHAN(14, LTC2497_SGL, "CH14"), + LTC2497_CHAN(15, LTC2497_SGL, "CH15"), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN), +}; + +static const struct iio_info ltc2497core_info = { + .read_raw = ltc2497core_read_raw, +}; + +int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + int ret; + + indio_dev->dev.parent = dev; + indio_dev->name = dev_name(dev); + indio_dev->info = <c2497core_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2497core_channel; + indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel); + + ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL); + if (ret < 0) + return ret; + + ddata->ref = devm_regulator_get(dev, "vref"); + if (IS_ERR(ddata->ref)) { + if (PTR_ERR(ddata->ref) != -EPROBE_DEFER) + dev_err(dev, "Failed to get vref regulator: %pe\n", + ddata->ref); + + return PTR_ERR(ddata->ref); + } + + ret = regulator_enable(ddata->ref); + if (ret < 0) { + dev_err(dev, "Failed to enable vref regulator: %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (dev->platform_data) { + struct iio_map *plat_data; + + plat_data = (struct iio_map *)dev->platform_data; + + ret = iio_map_array_register(indio_dev, plat_data); + if (ret) { + dev_err(&indio_dev->dev, "iio map err: %d\n", ret); + goto err_regulator_disable; + } + } + + ddata->addr_prev = LTC2497_CONFIG_DEFAULT; + ddata->time_prev = ktime_get(); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto err_array_unregister; + + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + +err_regulator_disable: + regulator_disable(ddata->ref); + + return ret; +} +EXPORT_SYMBOL_NS(ltc2497core_probe, LTC2497); + +void ltc2497core_remove(struct iio_dev *indio_dev) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + iio_map_array_unregister(indio_dev); + + regulator_disable(ddata->ref); +} +EXPORT_SYMBOL_NS(ltc2497core_remove, LTC2497); + +MODULE_DESCRIPTION("common code for LTC2496/LTC2497 drivers"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index 470406032720..5db63d7c6bc5 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -7,27 +7,18 @@ * Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf */ -#include #include #include #include -#include #include #include -#include -#define LTC2497_ENABLE 0xA0 -#define LTC2497_SGL BIT(4) -#define LTC2497_DIFF 0 -#define LTC2497_SIGN BIT(3) -#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE -#define LTC2497_CONVERSION_TIME_MS 150ULL +#include "ltc2497.h" -struct ltc2497_st { +struct ltc2497_driverdata { + /* this must be the first member */ + struct ltc2497core_driverdata common_ddata; struct i2c_client *client; - struct regulator *ref; - ktime_t time_prev; - u8 addr_prev; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. @@ -35,232 +26,59 @@ struct ltc2497_st { __be32 buf ____cacheline_aligned; }; -static int ltc2497_wait_conv(struct ltc2497_st *st) +static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, + u8 address, int *val) { - s64 time_elapsed; - - time_elapsed = ktime_ms_delta(ktime_get(), st->time_prev); - - if (time_elapsed < LTC2497_CONVERSION_TIME_MS) { - /* delay if conversion time not passed - * since last read or write - */ - if (msleep_interruptible( - LTC2497_CONVERSION_TIME_MS - time_elapsed)) - return -ERESTARTSYS; - - return 0; - } - - if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) { - /* We're in automatic mode - - * so the last reading is stil not outdated - */ - return 0; - } - - return 1; -} - -static int ltc2497_read(struct ltc2497_st *st, u8 address, int *val) -{ - struct i2c_client *client = st->client; - int ret; - - ret = ltc2497_wait_conv(st); - if (ret < 0) - return ret; - - if (ret || st->addr_prev != address) { - ret = i2c_smbus_write_byte(st->client, - LTC2497_ENABLE | address); - if (ret < 0) - return ret; - st->addr_prev = address; - if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS)) - return -ERESTARTSYS; - } - ret = i2c_master_recv(client, (char *)&st->buf, 3); - if (ret < 0) { - dev_err(&client->dev, "i2c_master_recv failed\n"); - return ret; - } - st->time_prev = ktime_get(); - - /* convert and shift the result, - * and finally convert from offset binary to signed integer - */ - *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17); - - return ret; -} - -static int ltc2497_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) -{ - struct ltc2497_st *st = iio_priv(indio_dev); + struct ltc2497_driverdata *st = + container_of(ddata, struct ltc2497_driverdata, common_ddata); int ret; - switch (mask) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - ret = ltc2497_read(st, chan->address, val); - mutex_unlock(&indio_dev->mlock); - if (ret < 0) - return ret; - - return IIO_VAL_INT; - - case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(st->ref); - if (ret < 0) + if (val) { + ret = i2c_master_recv(st->client, (char *)&st->buf, 3); + if (ret < 0) { + dev_err(&st->client->dev, "i2c_master_recv failed\n"); return ret; + } - *val = ret / 1000; - *val2 = 17; - - return IIO_VAL_FRACTIONAL_LOG2; - - default: - return -EINVAL; + *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17); } -} -#define LTC2497_CHAN(_chan, _addr, _ds_name) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = (_chan), \ - .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .datasheet_name = (_ds_name), \ -} - -#define LTC2497_CHAN_DIFF(_chan, _addr) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \ - .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\ - .address = (_addr | _chan), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .differential = 1, \ + ret = i2c_smbus_write_byte(st->client, + LTC2497_ENABLE | address); + if (ret) + dev_err(&st->client->dev, "i2c transfer failed: %pe\n", + ERR_PTR(ret)); + return ret; } -static const struct iio_chan_spec ltc2497_channel[] = { - LTC2497_CHAN(0, LTC2497_SGL, "CH0"), - LTC2497_CHAN(1, LTC2497_SGL, "CH1"), - LTC2497_CHAN(2, LTC2497_SGL, "CH2"), - LTC2497_CHAN(3, LTC2497_SGL, "CH3"), - LTC2497_CHAN(4, LTC2497_SGL, "CH4"), - LTC2497_CHAN(5, LTC2497_SGL, "CH5"), - LTC2497_CHAN(6, LTC2497_SGL, "CH6"), - LTC2497_CHAN(7, LTC2497_SGL, "CH7"), - LTC2497_CHAN(8, LTC2497_SGL, "CH8"), - LTC2497_CHAN(9, LTC2497_SGL, "CH9"), - LTC2497_CHAN(10, LTC2497_SGL, "CH10"), - LTC2497_CHAN(11, LTC2497_SGL, "CH11"), - LTC2497_CHAN(12, LTC2497_SGL, "CH12"), - LTC2497_CHAN(13, LTC2497_SGL, "CH13"), - LTC2497_CHAN(14, LTC2497_SGL, "CH14"), - LTC2497_CHAN(15, LTC2497_SGL, "CH15"), - LTC2497_CHAN_DIFF(0, LTC2497_DIFF), - LTC2497_CHAN_DIFF(1, LTC2497_DIFF), - LTC2497_CHAN_DIFF(2, LTC2497_DIFF), - LTC2497_CHAN_DIFF(3, LTC2497_DIFF), - LTC2497_CHAN_DIFF(4, LTC2497_DIFF), - LTC2497_CHAN_DIFF(5, LTC2497_DIFF), - LTC2497_CHAN_DIFF(6, LTC2497_DIFF), - LTC2497_CHAN_DIFF(7, LTC2497_DIFF), - LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN), - LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN), -}; - -static const struct iio_info ltc2497_info = { - .read_raw = ltc2497_read_raw, -}; - static int ltc2497_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct iio_dev *indio_dev; - struct ltc2497_st *st; - struct iio_map *plat_data; - int ret; + struct ltc2497_driverdata *st; + struct device *dev = &client->dev; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) return -EOPNOTSUPP; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); st->client = client; + st->common_ddata.result_and_measure = ltc2497_result_and_measure; - indio_dev->dev.parent = &client->dev; - indio_dev->name = id->name; - indio_dev->info = <c2497_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ltc2497_channel; - indio_dev->num_channels = ARRAY_SIZE(ltc2497_channel); - - st->ref = devm_regulator_get(&client->dev, "vref"); - if (IS_ERR(st->ref)) - return PTR_ERR(st->ref); - - ret = regulator_enable(st->ref); - if (ret < 0) - return ret; - - if (client->dev.platform_data) { - plat_data = ((struct iio_map *)client->dev.platform_data); - ret = iio_map_array_register(indio_dev, plat_data); - if (ret) { - dev_err(&indio_dev->dev, "iio map err: %d\n", ret); - goto err_regulator_disable; - } - } - - ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT); - if (ret < 0) - goto err_array_unregister; - - st->addr_prev = LTC2497_CONFIG_DEFAULT; - st->time_prev = ktime_get(); - - ret = iio_device_register(indio_dev); - if (ret < 0) - goto err_array_unregister; - - return 0; - -err_array_unregister: - iio_map_array_unregister(indio_dev); - -err_regulator_disable: - regulator_disable(st->ref); - - return ret; + return ltc2497core_probe(dev, indio_dev); } static int ltc2497_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ltc2497_st *st = iio_priv(indio_dev); - iio_map_array_unregister(indio_dev); - iio_device_unregister(indio_dev); - regulator_disable(st->ref); + ltc2497core_remove(indio_dev); return 0; } diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h new file mode 100644 index 000000000000..d0b42dd6b8ad --- /dev/null +++ b/drivers/iio/adc/ltc2497.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define LTC2497_ENABLE 0xA0 +#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE +#define LTC2497_CONVERSION_TIME_MS 150ULL + +struct ltc2497core_driverdata { + struct regulator *ref; + ktime_t time_prev; + u8 addr_prev; + int (*result_and_measure)(struct ltc2497core_driverdata *ddata, + u8 address, int *val); +}; + +int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev); +void ltc2497core_remove(struct iio_dev *indio_dev); + +MODULE_IMPORT_NS(LTC2497); -- cgit v1.2.3 From e4c5c4dfaa88e49f33e8c11b52c65c630c0b12a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 9 Dec 2019 21:32:48 +0100 Subject: iio: adc: new driver to support Linear technology's ltc2496 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This chip is similar to the LTC2497 ADC, it just uses SPI instead of I2C and so has a slightly different protocol. Only the actual hardware access is different. The spi protocol is different enough to not be able to map the differences via a regmap. Also generalize the entry in MAINTAINER to cover the newly introduced file. Signed-off-by: Uwe Kleine-König Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 +++++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2496.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 drivers/iio/adc/ltc2496.c (limited to 'drivers') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 27bb4e56eaea..82e33082958c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -530,6 +530,16 @@ config LTC2485 To compile this driver as a module, choose M here: the module will be called ltc2485. +config LTC2496 + tristate "Linear Technology LTC2496 ADC driver" + depends on SPI + help + Say yes here to build support for Linear Technology LTC2496 + 16-Bit 8-/16-Channel Delta Sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2496. + config LTC2497 tristate "Linear Technology LTC2497 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 5f8d7ac6bbe2..919228900df9 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o obj-$(CONFIG_LTC2471) += ltc2471.o obj-$(CONFIG_LTC2485) += ltc2485.o +obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX11100) += max11100.o diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c new file mode 100644 index 000000000000..88a30156a849 --- /dev/null +++ b/drivers/iio/adc/ltc2496.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2496.c - Driver for Analog Devices/Linear Technology LTC2496 ADC + * + * Based on ltc2497.c which has + * Copyright (C) 2017 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/2496fc.pdf + */ + +#include +#include +#include +#include +#include + +#include "ltc2497.h" + +struct ltc2496_driverdata { + /* this must be the first member */ + struct ltc2497core_driverdata common_ddata; + struct spi_device *spi; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + unsigned char rxbuf[3] ____cacheline_aligned; + unsigned char txbuf[3]; +}; + +static int ltc2496_result_and_measure(struct ltc2497core_driverdata *ddata, + u8 address, int *val) +{ + struct ltc2496_driverdata *st = + container_of(ddata, struct ltc2496_driverdata, common_ddata); + struct spi_transfer t = { + .tx_buf = st->txbuf, + .rx_buf = st->rxbuf, + .len = sizeof(st->txbuf), + }; + int ret; + + st->txbuf[0] = LTC2497_ENABLE | address; + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret < 0) { + dev_err(&st->spi->dev, "spi_sync_transfer failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (val) + *val = ((st->rxbuf[0] & 0x3f) << 12 | + st->rxbuf[1] << 4 | st->rxbuf[2] >> 4) - + (1 << 17); + + return 0; +} + +static int ltc2496_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ltc2496_driverdata *st; + struct device *dev = &spi->dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->common_ddata.result_and_measure = ltc2496_result_and_measure; + + return ltc2497core_probe(dev, indio_dev); +} + +static int ltc2496_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + ltc2497core_remove(indio_dev); + + return 0; +} + +static const struct of_device_id ltc2496_of_match[] = { + { .compatible = "lltc,ltc2496", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltc2496_of_match); + +static struct spi_driver ltc2496_driver = { + .driver = { + .name = "ltc2496", + .of_match_table = of_match_ptr(ltc2496_of_match), + }, + .probe = ltc2496_probe, + .remove = ltc2496_remove, +}; +module_spi_driver(ltc2496_driver); + +MODULE_AUTHOR("Uwe Kleine-König "); +MODULE_DESCRIPTION("Linear Technology LTC2496 ADC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 969fdd86caa5be3e57d9e4b6ba4541ec5a54eae8 Mon Sep 17 00:00:00 2001 From: Rodrigo Carvalho Date: Sat, 7 Dec 2019 01:53:38 -0300 Subject: staging: iio: accel: adis16240: enforce SPI mode on probe function According to the datasheet, this driver supports only SPI mode 3, so we should enforce it on probe function. Signed-off-by: Rodrigo Carvalho Signed-off-by: Jonathan Cameron --- drivers/staging/iio/accel/adis16240.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index a480409090c0..794f063e6c86 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -399,6 +399,13 @@ static int adis16240_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(adis16240_channels); indio_dev->modes = INDIO_DIRECT_MODE; + spi->mode = SPI_MODE_3; + ret = spi_setup(spi); + if (ret) { + dev_err(&spi->dev, "spi_setup failed!\n"); + return ret; + } + ret = adis_init(st, indio_dev, spi, &adis16240_data); if (ret) return ret; -- cgit v1.2.3 From 465c811f1f201a7554a95cf003bc06a239606e9b Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Fri, 20 Dec 2019 16:00:50 +0000 Subject: iio: accel: Add driver for the BMA400 Add a IIO driver for the Bosch BMA400 3-axes ultra-low power accelerometer. The driver supports reading from the acceleration and temperature registers. The driver also supports reading and configuring the output data rate, oversampling ratio, and scale. Signed-off-by: Dan Robertson Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 16 + drivers/iio/accel/Makefile | 2 + drivers/iio/accel/bma400.h | 95 +++++ drivers/iio/accel/bma400_core.c | 823 ++++++++++++++++++++++++++++++++++++++++ drivers/iio/accel/bma400_i2c.c | 61 +++ 5 files changed, 997 insertions(+) create mode 100644 drivers/iio/accel/bma400.h create mode 100644 drivers/iio/accel/bma400_core.c create mode 100644 drivers/iio/accel/bma400_i2c.c (limited to 'drivers') diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index d4ef35aeb579..670e60568033 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -112,6 +112,22 @@ config BMA220 To compile this driver as a module, choose M here: the module will be called bma220_spi. +config BMA400 + tristate "Bosch BMA400 3-Axis Accelerometer Driver" + select REGMAP + select BMA400_I2C if I2C + help + Say Y here if you want to build a driver for the Bosch BMA400 + triaxial acceleration sensor. + + To compile this driver as a module, choose M here: the + module will be called bma400_core and you will also get + bma400_i2c if I2C is enabled. + +config BMA400_I2C + tristate + depends on BMA400 + config BMC150_ACCEL tristate "Bosch BMC150 Accelerometer Driver" select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 56bd0215e0d4..3a051cf37f40 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_BMA220) += bma220_spi.o +obj-$(CONFIG_BMA400) += bma400_core.o +obj-$(CONFIG_BMA400_I2C) += bma400_i2c.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h new file mode 100644 index 000000000000..15c0e307d2c4 --- /dev/null +++ b/drivers/iio/accel/bma400.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Register constants and other forward declarations needed by the bma400 + * sources. + * + * Copyright 2019 Dan Robertson + */ + +#ifndef _BMA400_H_ +#define _BMA400_H_ + +#include +#include + +/* + * Read-Only Registers + */ + +/* Status and ID registers */ +#define BMA400_CHIP_ID_REG 0x00 +#define BMA400_ERR_REG 0x02 +#define BMA400_STATUS_REG 0x03 + +/* Acceleration registers */ +#define BMA400_X_AXIS_LSB_REG 0x04 +#define BMA400_X_AXIS_MSB_REG 0x05 +#define BMA400_Y_AXIS_LSB_REG 0x06 +#define BMA400_Y_AXIS_MSB_REG 0x07 +#define BMA400_Z_AXIS_LSB_REG 0x08 +#define BMA400_Z_AXIS_MSB_REG 0x09 + +/* Sensor time registers */ +#define BMA400_SENSOR_TIME0 0x0a +#define BMA400_SENSOR_TIME1 0x0b +#define BMA400_SENSOR_TIME2 0x0c + +/* Event and interrupt registers */ +#define BMA400_EVENT_REG 0x0d +#define BMA400_INT_STAT0_REG 0x0e +#define BMA400_INT_STAT1_REG 0x0f +#define BMA400_INT_STAT2_REG 0x10 + +/* Temperature register */ +#define BMA400_TEMP_DATA_REG 0x11 + +/* FIFO length and data registers */ +#define BMA400_FIFO_LENGTH0_REG 0x12 +#define BMA400_FIFO_LENGTH1_REG 0x13 +#define BMA400_FIFO_DATA_REG 0x14 + +/* Step count registers */ +#define BMA400_STEP_CNT0_REG 0x15 +#define BMA400_STEP_CNT1_REG 0x16 +#define BMA400_STEP_CNT3_REG 0x17 +#define BMA400_STEP_STAT_REG 0x18 + +/* + * Read-write configuration registers + */ +#define BMA400_ACC_CONFIG0_REG 0x19 +#define BMA400_ACC_CONFIG1_REG 0x1a +#define BMA400_ACC_CONFIG2_REG 0x1b +#define BMA400_CMD_REG 0x7e + +/* Chip ID of BMA 400 devices found in the chip ID register. */ +#define BMA400_ID_REG_VAL 0x90 + +#define BMA400_LP_OSR_SHIFT 5 +#define BMA400_NP_OSR_SHIFT 4 +#define BMA400_SCALE_SHIFT 6 + +#define BMA400_TWO_BITS_MASK GENMASK(1, 0) +#define BMA400_LP_OSR_MASK GENMASK(6, 5) +#define BMA400_NP_OSR_MASK GENMASK(5, 4) +#define BMA400_ACC_ODR_MASK GENMASK(3, 0) +#define BMA400_ACC_SCALE_MASK GENMASK(7, 6) + +#define BMA400_ACC_ODR_MIN_RAW 0x05 +#define BMA400_ACC_ODR_LP_RAW 0x06 +#define BMA400_ACC_ODR_MAX_RAW 0x0b + +#define BMA400_ACC_ODR_MAX_HZ 800 +#define BMA400_ACC_ODR_MIN_WHOLE_HZ 25 +#define BMA400_ACC_ODR_MIN_HZ 12 + +#define BMA400_SCALE_MIN 38357 +#define BMA400_SCALE_MAX 306864 + +extern const struct regmap_config bma400_regmap_config; + +int bma400_probe(struct device *dev, struct regmap *regmap, const char *name); + +int bma400_remove(struct device *dev); + +#endif diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c new file mode 100644 index 000000000000..b0a31294c09e --- /dev/null +++ b/drivers/iio/accel/bma400_core.c @@ -0,0 +1,823 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Core IIO driver for Bosch BMA400 triaxial acceleration sensor. + * + * Copyright 2019 Dan Robertson + * + * TODO: + * - Support for power management + * - Support events and interrupts + * - Create channel for step count + * - Create channel for sensor time + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bma400.h" + +/* + * The G-range selection may be one of 2g, 4g, 8, or 16g. The scale may + * be selected with the acc_range bits of the ACC_CONFIG1 register. + * NB: This buffer is populated in the device init. + */ +static int bma400_scales[8]; + +/* + * See the ACC_CONFIG1 section of the datasheet. + * NB: This buffer is populated in the device init. + */ +static int bma400_sample_freqs[14]; + +static const int bma400_osr_range[] = { 0, 1, 3 }; + +/* See the ACC_CONFIG0 section of the datasheet */ +enum bma400_power_mode { + POWER_MODE_SLEEP = 0x00, + POWER_MODE_LOW = 0x01, + POWER_MODE_NORMAL = 0x02, + POWER_MODE_INVALID = 0x03, +}; + +struct bma400_sample_freq { + int hz; + int uhz; +}; + +struct bma400_data { + struct device *dev; + struct regmap *regmap; + struct mutex mutex; /* data register lock */ + struct iio_mount_matrix orientation; + enum bma400_power_mode power_mode; + struct bma400_sample_freq sample_freq; + int oversampling_ratio; + int scale; +}; + +static bool bma400_is_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMA400_CHIP_ID_REG: + case BMA400_ERR_REG: + case BMA400_STATUS_REG: + case BMA400_X_AXIS_LSB_REG: + case BMA400_X_AXIS_MSB_REG: + case BMA400_Y_AXIS_LSB_REG: + case BMA400_Y_AXIS_MSB_REG: + case BMA400_Z_AXIS_LSB_REG: + case BMA400_Z_AXIS_MSB_REG: + case BMA400_SENSOR_TIME0: + case BMA400_SENSOR_TIME1: + case BMA400_SENSOR_TIME2: + case BMA400_EVENT_REG: + case BMA400_INT_STAT0_REG: + case BMA400_INT_STAT1_REG: + case BMA400_INT_STAT2_REG: + case BMA400_TEMP_DATA_REG: + case BMA400_FIFO_LENGTH0_REG: + case BMA400_FIFO_LENGTH1_REG: + case BMA400_FIFO_DATA_REG: + case BMA400_STEP_CNT0_REG: + case BMA400_STEP_CNT1_REG: + case BMA400_STEP_CNT3_REG: + case BMA400_STEP_STAT_REG: + return false; + default: + return true; + } +} + +static bool bma400_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMA400_ERR_REG: + case BMA400_STATUS_REG: + case BMA400_X_AXIS_LSB_REG: + case BMA400_X_AXIS_MSB_REG: + case BMA400_Y_AXIS_LSB_REG: + case BMA400_Y_AXIS_MSB_REG: + case BMA400_Z_AXIS_LSB_REG: + case BMA400_Z_AXIS_MSB_REG: + case BMA400_SENSOR_TIME0: + case BMA400_SENSOR_TIME1: + case BMA400_SENSOR_TIME2: + case BMA400_EVENT_REG: + case BMA400_INT_STAT0_REG: + case BMA400_INT_STAT1_REG: + case BMA400_INT_STAT2_REG: + case BMA400_TEMP_DATA_REG: + case BMA400_FIFO_LENGTH0_REG: + case BMA400_FIFO_LENGTH1_REG: + case BMA400_FIFO_DATA_REG: + case BMA400_STEP_CNT0_REG: + case BMA400_STEP_CNT1_REG: + case BMA400_STEP_CNT3_REG: + case BMA400_STEP_STAT_REG: + return true; + default: + return false; + } +} + +const struct regmap_config bma400_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BMA400_CMD_REG, + .cache_type = REGCACHE_RBTREE, + .writeable_reg = bma400_is_writable_reg, + .volatile_reg = bma400_is_volatile_reg, +}; +EXPORT_SYMBOL(bma400_regmap_config); + +static const struct iio_mount_matrix * +bma400_accel_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct bma400_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info bma400_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma400_accel_get_mount_matrix), + { } +}; + +#define BMA400_ACC_CHANNEL(_axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = bma400_ext_info, \ +} + +static const struct iio_chan_spec bma400_channels[] = { + BMA400_ACC_CHANNEL(X), + BMA400_ACC_CHANNEL(Y), + BMA400_ACC_CHANNEL(Z), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, +}; + +static int bma400_get_temp_reg(struct bma400_data *data, int *val, int *val2) +{ + unsigned int raw_temp; + int host_temp; + int ret; + + if (data->power_mode == POWER_MODE_SLEEP) + return -EBUSY; + + ret = regmap_read(data->regmap, BMA400_TEMP_DATA_REG, &raw_temp); + if (ret) + return ret; + + host_temp = sign_extend32(raw_temp, 7); + /* + * The formula for the TEMP_DATA register in the datasheet + * is: x * 0.5 + 23 + */ + *val = (host_temp >> 1) + 23; + *val2 = (host_temp & 0x1) * 500000; + return IIO_VAL_INT_PLUS_MICRO; +} + +static int bma400_get_accel_reg(struct bma400_data *data, + const struct iio_chan_spec *chan, + int *val) +{ + __le16 raw_accel; + int lsb_reg; + int ret; + + if (data->power_mode == POWER_MODE_SLEEP) + return -EBUSY; + + switch (chan->channel2) { + case IIO_MOD_X: + lsb_reg = BMA400_X_AXIS_LSB_REG; + break; + case IIO_MOD_Y: + lsb_reg = BMA400_Y_AXIS_LSB_REG; + break; + case IIO_MOD_Z: + lsb_reg = BMA400_Z_AXIS_LSB_REG; + break; + default: + dev_err(data->dev, "invalid axis channel modifier\n"); + return -EINVAL; + } + + /* bulk read two registers, with the base being the LSB register */ + ret = regmap_bulk_read(data->regmap, lsb_reg, &raw_accel, + sizeof(raw_accel)); + if (ret) + return ret; + + *val = sign_extend32(le16_to_cpu(raw_accel), 11); + return IIO_VAL_INT; +} + +static void bma400_output_data_rate_from_raw(int raw, unsigned int *val, + unsigned int *val2) +{ + *val = BMA400_ACC_ODR_MAX_HZ >> (BMA400_ACC_ODR_MAX_RAW - raw); + if (raw > BMA400_ACC_ODR_MIN_RAW) + *val2 = 0; + else + *val2 = 500000; +} + +static int bma400_get_accel_output_data_rate(struct bma400_data *data) +{ + unsigned int val; + unsigned int odr; + int ret; + + switch (data->power_mode) { + case POWER_MODE_LOW: + /* + * Runs at a fixed rate in low-power mode. See section 4.3 + * in the datasheet. + */ + bma400_output_data_rate_from_raw(BMA400_ACC_ODR_LP_RAW, + &data->sample_freq.hz, + &data->sample_freq.uhz); + return 0; + case POWER_MODE_NORMAL: + /* + * In normal mode the ODR can be found in the ACC_CONFIG1 + * register. + */ + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val); + if (ret) + goto error; + + odr = val & BMA400_ACC_ODR_MASK; + if (odr < BMA400_ACC_ODR_MIN_RAW || + odr > BMA400_ACC_ODR_MAX_RAW) { + ret = -EINVAL; + goto error; + } + + bma400_output_data_rate_from_raw(odr, &data->sample_freq.hz, + &data->sample_freq.uhz); + return 0; + case POWER_MODE_SLEEP: + data->sample_freq.hz = 0; + data->sample_freq.uhz = 0; + return 0; + default: + ret = 0; + goto error; + } +error: + data->sample_freq.hz = -1; + data->sample_freq.uhz = -1; + return ret; +} + +static int bma400_set_accel_output_data_rate(struct bma400_data *data, + int hz, int uhz) +{ + unsigned int idx; + unsigned int odr; + unsigned int val; + int ret; + + if (hz >= BMA400_ACC_ODR_MIN_WHOLE_HZ) { + if (uhz || hz > BMA400_ACC_ODR_MAX_HZ) + return -EINVAL; + + /* Note this works because MIN_WHOLE_HZ is odd */ + idx = __ffs(hz); + + if (hz >> idx != BMA400_ACC_ODR_MIN_WHOLE_HZ) + return -EINVAL; + + idx += BMA400_ACC_ODR_MIN_RAW + 1; + } else if (hz == BMA400_ACC_ODR_MIN_HZ && uhz == 500000) { + idx = BMA400_ACC_ODR_MIN_RAW; + } else { + return -EINVAL; + } + + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val); + if (ret) + return ret; + + /* preserve the range and normal mode osr */ + odr = (~BMA400_ACC_ODR_MASK & val) | idx; + + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG, odr); + if (ret) + return ret; + + bma400_output_data_rate_from_raw(idx, &data->sample_freq.hz, + &data->sample_freq.uhz); + return 0; +} + +static int bma400_get_accel_oversampling_ratio(struct bma400_data *data) +{ + unsigned int val; + unsigned int osr; + int ret; + + /* + * The oversampling ratio is stored in a different register + * based on the power-mode. In normal mode the OSR is stored + * in ACC_CONFIG1. In low-power mode it is stored in + * ACC_CONFIG0. + */ + switch (data->power_mode) { + case POWER_MODE_LOW: + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG, &val); + if (ret) { + data->oversampling_ratio = -1; + return ret; + } + + osr = (val & BMA400_LP_OSR_MASK) >> BMA400_LP_OSR_SHIFT; + + data->oversampling_ratio = osr; + return 0; + case POWER_MODE_NORMAL: + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val); + if (ret) { + data->oversampling_ratio = -1; + return ret; + } + + osr = (val & BMA400_NP_OSR_MASK) >> BMA400_NP_OSR_SHIFT; + + data->oversampling_ratio = osr; + return 0; + case POWER_MODE_SLEEP: + data->oversampling_ratio = 0; + return 0; + default: + data->oversampling_ratio = -1; + return -EINVAL; + } +} + +static int bma400_set_accel_oversampling_ratio(struct bma400_data *data, + int val) +{ + unsigned int acc_config; + int ret; + + if (val & ~BMA400_TWO_BITS_MASK) + return -EINVAL; + + /* + * The oversampling ratio is stored in a different register + * based on the power-mode. + */ + switch (data->power_mode) { + case POWER_MODE_LOW: + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG, + &acc_config); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG0_REG, + (acc_config & ~BMA400_LP_OSR_MASK) | + (val << BMA400_LP_OSR_SHIFT)); + if (ret) { + dev_err(data->dev, "Failed to write out OSR\n"); + return ret; + } + + data->oversampling_ratio = val; + return 0; + case POWER_MODE_NORMAL: + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, + &acc_config); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG, + (acc_config & ~BMA400_NP_OSR_MASK) | + (val << BMA400_NP_OSR_SHIFT)); + if (ret) { + dev_err(data->dev, "Failed to write out OSR\n"); + return ret; + } + + data->oversampling_ratio = val; + return 0; + default: + return -EINVAL; + } + return ret; +} + +static int bma400_accel_scale_to_raw(struct bma400_data *data, + unsigned int val) +{ + int raw; + + if (val == 0) + return -EINVAL; + + /* Note this works because BMA400_SCALE_MIN is odd */ + raw = __ffs(val); + + if (val >> raw != BMA400_SCALE_MIN) + return -EINVAL; + + return raw; +} + +static int bma400_get_accel_scale(struct bma400_data *data) +{ + unsigned int raw_scale; + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val); + if (ret) + return ret; + + raw_scale = (val & BMA400_ACC_SCALE_MASK) >> BMA400_SCALE_SHIFT; + if (raw_scale > BMA400_TWO_BITS_MASK) + return -EINVAL; + + data->scale = BMA400_SCALE_MIN << raw_scale; + + return 0; +} + +static int bma400_set_accel_scale(struct bma400_data *data, unsigned int val) +{ + unsigned int acc_config; + int raw; + int ret; + + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &acc_config); + if (ret) + return ret; + + raw = bma400_accel_scale_to_raw(data, val); + if (raw < 0) + return raw; + + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG, + (acc_config & ~BMA400_ACC_SCALE_MASK) | + (raw << BMA400_SCALE_SHIFT)); + if (ret) + return ret; + + data->scale = val; + return 0; +} + +static int bma400_get_power_mode(struct bma400_data *data) +{ + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, BMA400_STATUS_REG, &val); + if (ret) { + dev_err(data->dev, "Failed to read status register\n"); + return ret; + } + + data->power_mode = (val >> 1) & BMA400_TWO_BITS_MASK; + return 0; +} + +static int bma400_set_power_mode(struct bma400_data *data, + enum bma400_power_mode mode) +{ + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG, &val); + if (ret) + return ret; + + if (data->power_mode == mode) + return 0; + + if (mode == POWER_MODE_INVALID) + return -EINVAL; + + /* Preserve the low-power oversample ratio etc */ + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG0_REG, + mode | (val & ~BMA400_TWO_BITS_MASK)); + if (ret) { + dev_err(data->dev, "Failed to write to power-mode\n"); + return ret; + } + + data->power_mode = mode; + + /* + * Update our cached osr and odr based on the new + * power-mode. + */ + bma400_get_accel_output_data_rate(data); + bma400_get_accel_oversampling_ratio(data); + return 0; +} + +static void bma400_init_tables(void) +{ + int raw; + int i; + + for (i = 0; i + 1 < ARRAY_SIZE(bma400_sample_freqs); i += 2) { + raw = (i / 2) + 5; + bma400_output_data_rate_from_raw(raw, &bma400_sample_freqs[i], + &bma400_sample_freqs[i + 1]); + } + + for (i = 0; i + 1 < ARRAY_SIZE(bma400_scales); i += 2) { + raw = i / 2; + bma400_scales[i] = 0; + bma400_scales[i + 1] = BMA400_SCALE_MIN << raw; + } +} + +static int bma400_init(struct bma400_data *data) +{ + unsigned int val; + int ret; + + /* Try to read chip_id register. It must return 0x90. */ + ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val); + if (ret) { + dev_err(data->dev, "Failed to read chip id register\n"); + goto out; + } + + if (val != BMA400_ID_REG_VAL) { + dev_err(data->dev, "Chip ID mismatch\n"); + ret = -ENODEV; + goto out; + } + + ret = bma400_get_power_mode(data); + if (ret) { + dev_err(data->dev, "Failed to get the initial power-mode\n"); + goto out; + } + + if (data->power_mode != POWER_MODE_NORMAL) { + ret = bma400_set_power_mode(data, POWER_MODE_NORMAL); + if (ret) { + dev_err(data->dev, "Failed to wake up the device\n"); + goto out; + } + /* + * TODO: The datasheet waits 1500us here in the example, but + * lists 2/ODR as the wakeup time. + */ + usleep_range(1500, 2000); + } + + bma400_init_tables(); + + ret = bma400_get_accel_output_data_rate(data); + if (ret) + goto out; + + ret = bma400_get_accel_oversampling_ratio(data); + if (ret) + goto out; + + ret = bma400_get_accel_scale(data); + if (ret) + goto out; + + /* + * Once the interrupt engine is supported we might use the + * data_src_reg, but for now ensure this is set to the + * variable ODR filter selectable by the sample frequency + * channel. + */ + return regmap_write(data->regmap, BMA400_ACC_CONFIG2_REG, 0x00); + +out: + return ret; +} + +static int bma400_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct bma400_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&data->mutex); + ret = bma400_get_temp_reg(data, val, val2); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->mutex); + ret = bma400_get_accel_reg(data, chan, val); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_ACCEL: + if (data->sample_freq.hz < 0) + return -EINVAL; + + *val = data->sample_freq.hz; + *val2 = data->sample_freq.uhz; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + /* + * Runs at a fixed sampling frequency. See Section 4.4 + * of the datasheet. + */ + *val = 6; + *val2 = 250000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = data->scale; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + /* + * TODO: We could avoid this logic and returning -EINVAL here if + * we set both the low-power and normal mode OSR registers when + * we configure the device. + */ + if (data->oversampling_ratio < 0) + return -EINVAL; + + *val = data->oversampling_ratio; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int bma400_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = bma400_scales; + *length = ARRAY_SIZE(bma400_scales); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *vals = bma400_osr_range; + *length = ARRAY_SIZE(bma400_osr_range); + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = bma400_sample_freqs; + *length = ARRAY_SIZE(bma400_sample_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int bma400_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct bma400_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * The sample frequency is readonly for the temperature + * register and a fixed value in low-power mode. + */ + if (chan->type != IIO_ACCEL) + return -EINVAL; + + mutex_lock(&data->mutex); + ret = bma400_set_accel_output_data_rate(data, val, val2); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_SCALE: + if (val != 0 || val2 > BMA400_SCALE_MAX) + return -EINVAL; + + mutex_lock(&data->mutex); + ret = bma400_set_accel_scale(data, val2); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + mutex_lock(&data->mutex); + ret = bma400_set_accel_oversampling_ratio(data, val); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } +} + +static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info bma400_info = { + .read_raw = bma400_read_raw, + .read_avail = bma400_read_avail, + .write_raw = bma400_write_raw, + .write_raw_get_fmt = bma400_write_raw_get_fmt, +}; + +int bma400_probe(struct device *dev, struct regmap *regmap, const char *name) +{ + struct iio_dev *indio_dev; + struct bma400_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->regmap = regmap; + data->dev = dev; + + ret = bma400_init(data); + if (ret) + return ret; + + ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation); + if (ret) + return ret; + + mutex_init(&data->mutex); + indio_dev->dev.parent = dev; + indio_dev->name = name; + indio_dev->info = &bma400_info; + indio_dev->channels = bma400_channels; + indio_dev->num_channels = ARRAY_SIZE(bma400_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + dev_set_drvdata(dev, indio_dev); + + return iio_device_register(indio_dev); +} +EXPORT_SYMBOL(bma400_probe); + +int bma400_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bma400_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + ret = bma400_set_power_mode(data, POWER_MODE_SLEEP); + mutex_unlock(&data->mutex); + + iio_device_unregister(indio_dev); + + return ret; +} +EXPORT_SYMBOL(bma400_remove); + +MODULE_AUTHOR("Dan Robertson "); +MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c new file mode 100644 index 000000000000..9dcb7cc9996e --- /dev/null +++ b/drivers/iio/accel/bma400_i2c.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C IIO driver for Bosch BMA400 triaxial acceleration sensor. + * + * Copyright 2019 Dan Robertson + * + * I2C address is either 0x14 or 0x15 depending on SDO + */ +#include +#include +#include +#include + +#include "bma400.h" + +static int bma400_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &bma400_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "failed to create regmap\n"); + return PTR_ERR(regmap); + } + + return bma400_probe(&client->dev, regmap, id->name); +} + +static int bma400_i2c_remove(struct i2c_client *client) +{ + return bma400_remove(&client->dev); +} + +static const struct i2c_device_id bma400_i2c_ids[] = { + { "bma400", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bma400_i2c_ids); + +static const struct of_device_id bma400_of_i2c_match[] = { + { .compatible = "bosch,bma400" }, + { } +}; +MODULE_DEVICE_TABLE(of, bma400_of_i2c_match); + +static struct i2c_driver bma400_i2c_driver = { + .driver = { + .name = "bma400", + .of_match_table = bma400_of_i2c_match, + }, + .probe = bma400_i2c_probe, + .remove = bma400_i2c_remove, + .id_table = bma400_i2c_ids, +}; + +module_i2c_driver(bma400_i2c_driver); + +MODULE_AUTHOR("Dan Robertson "); +MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (I2C)"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3cf7ded15e404655d4bcdcac76e0fe2247946d05 Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Fri, 20 Dec 2019 16:00:51 +0000 Subject: iio: accel: bma400: basic regulator support Add support for the VDD and VDDIO regulators using the regulator framework. Signed-off-by: Dan Robertson Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma400.h | 4 ++++ drivers/iio/accel/bma400_core.c | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h index 15c0e307d2c4..5ad10db9819f 100644 --- a/drivers/iio/accel/bma400.h +++ b/drivers/iio/accel/bma400.h @@ -86,6 +86,10 @@ #define BMA400_SCALE_MIN 38357 #define BMA400_SCALE_MAX 306864 +#define BMA400_NUM_REGULATORS 2 +#define BMA400_VDD_REGULATOR 0 +#define BMA400_VDDIO_REGULATOR 1 + extern const struct regmap_config bma400_regmap_config; int bma400_probe(struct device *dev, struct regmap *regmap, const char *name); diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index b0a31294c09e..ab4a158b35af 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "bma400.h" @@ -53,6 +54,7 @@ struct bma400_sample_freq { struct bma400_data { struct device *dev; struct regmap *regmap; + struct regulator_bulk_data regulators[BMA400_NUM_REGULATORS]; struct mutex mutex; /* data register lock */ struct iio_mount_matrix orientation; enum bma400_power_mode power_mode; @@ -576,17 +578,38 @@ static int bma400_init(struct bma400_data *data) goto out; } + data->regulators[BMA400_VDD_REGULATOR].supply = "vdd"; + data->regulators[BMA400_VDDIO_REGULATOR].supply = "vddio"; + ret = devm_regulator_bulk_get(data->dev, + ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(data->dev, + "Failed to get regulators: %d\n", + ret); + + goto out; + } + ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) { + dev_err(data->dev, "Failed to enable regulators: %d\n", + ret); + goto out; + } + ret = bma400_get_power_mode(data); if (ret) { dev_err(data->dev, "Failed to get the initial power-mode\n"); - goto out; + goto err_reg_disable; } if (data->power_mode != POWER_MODE_NORMAL) { ret = bma400_set_power_mode(data, POWER_MODE_NORMAL); if (ret) { dev_err(data->dev, "Failed to wake up the device\n"); - goto out; + goto err_reg_disable; } /* * TODO: The datasheet waits 1500us here in the example, but @@ -599,15 +622,15 @@ static int bma400_init(struct bma400_data *data) ret = bma400_get_accel_output_data_rate(data); if (ret) - goto out; + goto err_reg_disable; ret = bma400_get_accel_oversampling_ratio(data); if (ret) - goto out; + goto err_reg_disable; ret = bma400_get_accel_scale(data); if (ret) - goto out; + goto err_reg_disable; /* * Once the interrupt engine is supported we might use the @@ -617,6 +640,9 @@ static int bma400_init(struct bma400_data *data) */ return regmap_write(data->regmap, BMA400_ACC_CONFIG2_REG, 0x00); +err_reg_disable: + regulator_bulk_disable(ARRAY_SIZE(data->regulators), + data->regulators); out: return ret; } @@ -812,6 +838,9 @@ int bma400_remove(struct device *dev) ret = bma400_set_power_mode(data, POWER_MODE_SLEEP); mutex_unlock(&data->mutex); + regulator_bulk_disable(ARRAY_SIZE(data->regulators), + data->regulators); + iio_device_unregister(indio_dev); return ret; -- cgit v1.2.3 From 2df57429d8bee8d26c860d78039c9aaf2b9daffe Mon Sep 17 00:00:00 2001 From: Daniel Junho Date: Tue, 17 Dec 2019 08:11:55 -0300 Subject: iio: adc: ad7923: Remove the unused defines Removes the unused define AD7923_CHANNEL_x from the code. Signed-off-by: Daniel Junho Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7923.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 3212eb4c0f25..969c06b7d2b7 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -29,10 +29,6 @@ #define AD7923_PM_MODE_AS (1) /* auto shutdown */ #define AD7923_PM_MODE_FS (2) /* full shutdown */ #define AD7923_PM_MODE_OPS (3) /* normal operation */ -#define AD7923_CHANNEL_0 (0) /* analog input 0 */ -#define AD7923_CHANNEL_1 (1) /* analog input 1 */ -#define AD7923_CHANNEL_2 (2) /* analog input 2 */ -#define AD7923_CHANNEL_3 (3) /* analog input 3 */ #define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */ #define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */ #define AD7923_SEQUENCE_ON (3) /* continuous sequence */ -- cgit v1.2.3 From c87de1ba35e4e4225f3e10b09d22c6490e6cd848 Mon Sep 17 00:00:00 2001 From: Daniel Junho Date: Tue, 17 Dec 2019 08:11:56 -0300 Subject: iio: adc: ad7923: Fix checkpatch warning Fix checkpatch warning: WARNING: Prefer 'unsigned int' to bare use of 'unsigned' +static int ad7923_scan_direct(struct ad7923_state *st, unsigned ch) Signed-off-by: Daniel Junho Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7923.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 969c06b7d2b7..e535cec9fc02 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -184,7 +184,7 @@ done: return IRQ_HANDLED; } -static int ad7923_scan_direct(struct ad7923_state *st, unsigned ch) +static int ad7923_scan_direct(struct ad7923_state *st, unsigned int ch) { int ret, cmd; -- cgit v1.2.3 From 233e7de42fcc3ba6abdc09a58a1be5577a09aba9 Mon Sep 17 00:00:00 2001 From: Daniel Junho Date: Tue, 17 Dec 2019 08:11:57 -0300 Subject: iio: adc: ad7923: Add of_device_id table Accomplish device tree compatibility to driver AD7923 by adding of_device_id table and making a subsequent call to MODULE_DEVICE_TABLE. Signed-off-by: Daniel Junho Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7923.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index e535cec9fc02..6d56fa0b9e30 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -348,9 +348,19 @@ static const struct spi_device_id ad7923_id[] = { }; MODULE_DEVICE_TABLE(spi, ad7923_id); +static const struct of_device_id ad7923_of_match[] = { + { .compatible = "adi,ad7904", }, + { .compatible = "adi,ad7914", }, + { .compatible = "adi,ad7923", }, + { .compatible = "adi,ad7924", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7923_of_match); + static struct spi_driver ad7923_driver = { .driver = { .name = "ad7923", + .of_match_table = ad7923_of_match, }, .probe = ad7923_probe, .remove = ad7923_remove, -- cgit v1.2.3 From 851644a60d200c9a294de5a5594004bcf13d34c7 Mon Sep 17 00:00:00 2001 From: Daniel Junho Date: Tue, 17 Dec 2019 08:11:58 -0300 Subject: iio: adc: ad7923: Add support for the ad7908/ad7918/ad7928 The ad7928 is software compatible with the ad7923. The ad7908 and ad7918 are the 8 and 10-bit versions of the ad7928. Signed-off-by: Daniel Junho Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7923.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 6d56fa0b9e30..1d124c87c6ac 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * AD7904/AD7914/AD7923/AD7924 SPI ADC driver + * AD7904/AD7914/AD7923/AD7924/AD7908/AD7918/AD7928 SPI ADC driver * * Copyright 2011 Analog Devices Inc (from AD7923 Driver) * Copyright 2012 CS Systemes d'Information @@ -33,7 +33,6 @@ #define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */ #define AD7923_SEQUENCE_ON (3) /* continuous sequence */ -#define AD7923_MAX_CHAN 4 #define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */ #define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */ @@ -74,6 +73,9 @@ enum ad7923_id { AD7904, AD7914, AD7924, + AD7908, + AD7918, + AD7928 }; #define AD7923_V_CHAN(index, bits) \ @@ -102,9 +104,25 @@ const struct iio_chan_spec name ## _channels[] = { \ IIO_CHAN_SOFT_TIMESTAMP(4), \ } +#define DECLARE_AD7908_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + AD7923_V_CHAN(0, bits), \ + AD7923_V_CHAN(1, bits), \ + AD7923_V_CHAN(2, bits), \ + AD7923_V_CHAN(3, bits), \ + AD7923_V_CHAN(4, bits), \ + AD7923_V_CHAN(5, bits), \ + AD7923_V_CHAN(6, bits), \ + AD7923_V_CHAN(7, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + static DECLARE_AD7923_CHANNELS(ad7904, 8); static DECLARE_AD7923_CHANNELS(ad7914, 10); static DECLARE_AD7923_CHANNELS(ad7924, 12); +static DECLARE_AD7908_CHANNELS(ad7908, 8); +static DECLARE_AD7908_CHANNELS(ad7918, 10); +static DECLARE_AD7908_CHANNELS(ad7928, 12); static const struct ad7923_chip_info ad7923_chip_info[] = { [AD7904] = { @@ -119,6 +137,18 @@ static const struct ad7923_chip_info ad7923_chip_info[] = { .channels = ad7924_channels, .num_channels = ARRAY_SIZE(ad7924_channels), }, + [AD7908] = { + .channels = ad7908_channels, + .num_channels = ARRAY_SIZE(ad7908_channels), + }, + [AD7918] = { + .channels = ad7918_channels, + .num_channels = ARRAY_SIZE(ad7918_channels), + }, + [AD7928] = { + .channels = ad7928_channels, + .num_channels = ARRAY_SIZE(ad7928_channels), + }, }; /** @@ -131,7 +161,11 @@ static int ad7923_update_scan_mode(struct iio_dev *indio_dev, int i, cmd, len; len = 0; - for_each_set_bit(i, active_scan_mask, AD7923_MAX_CHAN) { + /* + * For this driver the last channel is always the software timestamp so + * skip that one. + */ + for_each_set_bit(i, active_scan_mask, indio_dev->num_channels - 1) { cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) | AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) | st->settings; @@ -344,6 +378,9 @@ static const struct spi_device_id ad7923_id[] = { {"ad7914", AD7914}, {"ad7923", AD7924}, {"ad7924", AD7924}, + {"ad7908", AD7908}, + {"ad7918", AD7918}, + {"ad7928", AD7928}, {} }; MODULE_DEVICE_TABLE(spi, ad7923_id); @@ -353,6 +390,9 @@ static const struct of_device_id ad7923_of_match[] = { { .compatible = "adi,ad7914", }, { .compatible = "adi,ad7923", }, { .compatible = "adi,ad7924", }, + { .compatible = "adi,ad7908", }, + { .compatible = "adi,ad7918", }, + { .compatible = "adi,ad7928", }, { }, }; MODULE_DEVICE_TABLE(of, ad7923_of_match); @@ -370,5 +410,5 @@ module_spi_driver(ad7923_driver); MODULE_AUTHOR("Michael Hennerich "); MODULE_AUTHOR("Patrick Vasseur "); -MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC"); +MODULE_DESCRIPTION("Analog Devices AD7923 and similar ADC"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 07bf07e2cc7d8658de138b8a217cb0a936d26e7e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 20:19:23 +0200 Subject: iio: st_lsm6dsx: Mark predefined constants with __maybe_unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we put static variable to a header file it's copied to each module that includes the header. But not all of them are actually used it. Mark predefined constants with __maybe_unused to calm a compiler down: In file included from drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c:17: .../st_lsm6dsx/st_lsm6dsx.h:399:28: warning: ‘st_lsm6dsx_available_scan_masks’ defined but not used [-Wunused-const-variable=] 399 | static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0}; | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../st_lsm6dsx/st_lsm6dsx.h:392:36: warning: ‘st_lsm6dsx_event’ defined but not used [-Wunused-const-variable=] 392 | static const struct iio_event_spec st_lsm6dsx_event = { | ^~~~~~~~~~~~~~~~ ... Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index c605b153be41..f1189eef6e37 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -391,14 +391,17 @@ struct st_lsm6dsx_hw { const struct st_lsm6dsx_settings *settings; }; -static const struct iio_event_spec st_lsm6dsx_event = { +static __maybe_unused const struct iio_event_spec st_lsm6dsx_event = { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_EITHER, .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE) }; -static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0}; +static __maybe_unused const unsigned long st_lsm6dsx_available_scan_masks[] = { + 0x7, 0x0, +}; + extern const struct dev_pm_ops st_lsm6dsx_pm_ops; int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, -- cgit v1.2.3 From e3da43077046d3b44da0f66b2f202e25ba9ca694 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 20:19:24 +0200 Subject: iio: st_lsm6dsx: Drop unneeded OF code There is no need to have OF guard against ID table. Drop it for good. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 3 +-- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index cd47ec1fedcb..0fb32131afce 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include "st_lsm6dsx.h" @@ -122,7 +121,7 @@ static struct i2c_driver st_lsm6dsx_driver = { .driver = { .name = "st_lsm6dsx_i2c", .pm = &st_lsm6dsx_pm_ops, - .of_match_table = of_match_ptr(st_lsm6dsx_i2c_of_match), + .of_match_table = st_lsm6dsx_i2c_of_match, }, .probe = st_lsm6dsx_i2c_probe, .id_table = st_lsm6dsx_i2c_id_table, diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 67ff36eac247..eb1086e4a951 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include "st_lsm6dsx.h" @@ -122,7 +121,7 @@ static struct spi_driver st_lsm6dsx_driver = { .driver = { .name = "st_lsm6dsx_spi", .pm = &st_lsm6dsx_pm_ops, - .of_match_table = of_match_ptr(st_lsm6dsx_spi_of_match), + .of_match_table = st_lsm6dsx_spi_of_match, }, .probe = st_lsm6dsx_spi_probe, .id_table = st_lsm6dsx_spi_id_table, -- cgit v1.2.3 From 03d4c566c51def8151dee1e87dae6479722890d2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 20:19:25 +0200 Subject: iio: st_lsm6dsx: Make use of device properties Device property API allows to gather device resources from different sources, such as ACPI. Convert the drivers to unleash the power of device property API. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index a4ed72aef93e..95d7eb1d9cdb 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -1804,14 +1805,14 @@ static const struct iio_info st_lsm6dsx_gyro_info = { .hwfifo_set_watermark = st_lsm6dsx_set_watermark, }; -static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin) +static int st_lsm6dsx_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin) { - struct device_node *np = hw->dev->of_node; + struct device *dev = hw->dev; - if (!np) + if (!dev_fwnode(dev)) return -EINVAL; - return of_property_read_u32(np, "st,drdy-int-pin", drdy_pin); + return device_property_read_u32(dev, "st,drdy-int-pin", drdy_pin); } static int @@ -1820,7 +1821,7 @@ st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, { int err = 0, drdy_pin; - if (st_lsm6dsx_of_get_drdy_pin(hw, &drdy_pin) < 0) { + if (st_lsm6dsx_get_drdy_pin(hw, &drdy_pin) < 0) { struct st_sensors_platform_data *pdata; struct device *dev = hw->dev; @@ -1849,15 +1850,15 @@ st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw) { const struct st_lsm6dsx_shub_settings *hub_settings; - struct device_node *np = hw->dev->of_node; struct st_sensors_platform_data *pdata; + struct device *dev = hw->dev; unsigned int data; int err = 0; hub_settings = &hw->settings->shub_settings; - pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; - if ((np && of_property_read_bool(np, "st,pullups")) || + pdata = (struct st_sensors_platform_data *)dev->platform_data; + if ((dev_fwnode(dev) && device_property_read_bool(dev, "st,pullups")) || (pdata && pdata->pullups)) { err = st_lsm6dsx_set_page(hw, true); if (err < 0) @@ -2135,9 +2136,9 @@ static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) { - struct device_node *np = hw->dev->of_node; struct st_sensors_platform_data *pdata; const struct st_lsm6dsx_reg *reg; + struct device *dev = hw->dev; unsigned long irq_type; bool irq_active_low; int err; @@ -2165,8 +2166,8 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) if (err < 0) return err; - pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; - if ((np && of_property_read_bool(np, "drive-open-drain")) || + pdata = (struct st_sensors_platform_data *)dev->platform_data; + if ((dev_fwnode(dev) && device_property_read_bool(dev, "drive-open-drain")) || (pdata && pdata->open_drain)) { reg = &hw->settings->irq_config.od; err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, @@ -2196,7 +2197,6 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, { struct st_sensors_platform_data *pdata = dev->platform_data; const struct st_lsm6dsx_shub_settings *hub_settings; - struct device_node *np = dev->of_node; struct st_lsm6dsx_hw *hw; const char *name = NULL; int i, err; @@ -2259,7 +2259,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } - if ((np && of_property_read_bool(np, "wakeup-source")) || + if ((dev_fwnode(dev) && device_property_read_bool(dev, "wakeup-source")) || (pdata && pdata->wakeup_source)) device_init_wakeup(dev, true); -- cgit v1.2.3 From d9fa3fd4bc48881684d9d1a1c709187a01fc9a21 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 19:38:45 +0200 Subject: iio: light: st_uvis25: Drop unneeded header inclusion There is no evidence that the driver supports or needs ACPI APIs. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/st_uvis25_i2c.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c index dacbac6a6662..4889bbeb0c73 100644 --- a/drivers/iio/light/st_uvis25_i2c.c +++ b/drivers/iio/light/st_uvis25_i2c.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From 4c3e7367bd7477f0335ab7f40505f9e03bf193a3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 19:38:46 +0200 Subject: iio: accel: st_accel: Drop unnecessary #else branch for ACPI ACPI_PTR() takes care of the argument in case of !CONFIG_ACPI. Remove unnecessary #else branch. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_i2c.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 50fa0fc32baa..0f4aef5448e7 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -119,8 +119,6 @@ static const struct acpi_device_id st_accel_acpi_match[] = { { }, }; MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match); -#else -#define st_accel_acpi_match NULL #endif static const struct i2c_device_id st_accel_id_table[] = { -- cgit v1.2.3 From 40ca77d57f0163d3bd2ae826613a69039be0d052 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 19:38:47 +0200 Subject: iio: pressure: st_press: Drop unnecessary #else branch for ACPI ACPI_PTR() takes care of the argument in case of !CONFIG_ACPI. Remove unnecessary #else branch. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/st_pressure_i2c.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 71d2ed6b4948..6203bc9d5c2d 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -61,8 +61,6 @@ static const struct acpi_device_id st_press_acpi_match[] = { { }, }; MODULE_DEVICE_TABLE(acpi, st_press_acpi_match); -#else -#define st_press_acpi_match NULL #endif static const struct i2c_device_id st_press_id_table[] = { -- cgit v1.2.3 From f22b1d7588b1cc115d15462bb0f118be971369fb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 19:38:48 +0200 Subject: iio: gyro: st_gyro: Mark gyro_pdata with __maybe_unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we put static variable to a header file it's copied to each module that includes the header. But not all of them are actually used it. Mark gyro_pdata with __maybe_unused to calm a compiler down: In file included from drivers/iio/gyro/st_gyro_i2c.c:18: drivers/iio/gyro/st_gyro.h:31:46: warning: ‘gyro_pdata’ defined but not used [-Wunused-const-variable=] 31 | static const struct st_sensors_platform_data gyro_pdata = { | ^~~~~~~~~~ ... Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/st_gyro.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h index 592f6b34e987..fd9171cc3aba 100644 --- a/drivers/iio/gyro/st_gyro.h +++ b/drivers/iio/gyro/st_gyro.h @@ -28,7 +28,7 @@ * struct st_sensors_platform_data - gyro platform data * @drdy_int_pin: DRDY on gyros is available only on INT2 pin. */ -static const struct st_sensors_platform_data gyro_pdata = { +static __maybe_unused const struct st_sensors_platform_data gyro_pdata = { .drdy_int_pin = 2, }; -- cgit v1.2.3 From 2e8bd34a16f833c6e4d2fd725bac2cdb8faf61fc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 19:38:49 +0200 Subject: iio: accel: st_accel: Mark default_accel_pdata with __maybe_unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we put static variable to a header file it's copied to each module that includes the header. But not all of them are actually used it. Mark default_accel_pdata with __maybe_unused to calm a compiler down: In file included from drivers/iio/accel/st_accel_i2c.c:19: drivers/iio/accel/st_accel.h:67:46: warning: ‘default_accel_pdata’ defined but not used [-Wunused-const-variable=] 67 | static const struct st_sensors_platform_data default_accel_pdata = { | ^~~~~~~~~~~~~~~~~~~ ... Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index af09943f38c9..5b13e293cade 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -64,7 +64,7 @@ enum st_accel_type { * struct st_sensors_platform_data - default accel platform data * @drdy_int_pin: default accel DRDY is available on INT1 pin. */ -static const struct st_sensors_platform_data default_accel_pdata = { +static __maybe_unused const struct st_sensors_platform_data default_accel_pdata = { .drdy_int_pin = 1, }; -- cgit v1.2.3 From be4ac8321ff2c0085573500ce819e80cfa38d770 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 19:38:50 +0200 Subject: iio: pressure: st_press: Mark default_press_pdata with __maybe_unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we put static variable to a header file it's copied to each module that includes the header. But not all of them are actually used it. Mark default_press_pdata with __maybe_unused to calm a compiler down: In file included from drivers/iio/pressure/st_pressure_spi.c:18: drivers/iio/pressure/st_pressure.h:40:46: warning: ‘default_press_pdata’ defined but not used [-Wunused-const-variable=] 40 | static const struct st_sensors_platform_data default_press_pdata = { | ^~~~~~~~~~~~~~~~~~~ ... Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/st_pressure.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index c2e47a6c3118..5c746ff6087e 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -37,7 +37,7 @@ enum st_press_type { * struct st_sensors_platform_data - default press platform data * @drdy_int_pin: default press DRDY is available on INT1 pin. */ -static const struct st_sensors_platform_data default_press_pdata = { +static __maybe_unused const struct st_sensors_platform_data default_press_pdata = { .drdy_int_pin = 1, }; -- cgit v1.2.3 From de54fff151335d0c24f53ff843c67a1364f70f42 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Dec 2019 19:38:51 +0200 Subject: iio: st_sensors: Describe function parameters in kernel-doc Kernel documentation script complains that some of the function parameters are not described: .../common/st_sensors/st_sensors_trigger.c:29: warning: Function parameter or member 'indio_dev' not described in 'st_sensors_new_samples_available' .../common/st_sensors/st_sensors_trigger.c:29: warning: Function parameter or member 'sdata' not described in 'st_sensors_new_samples_available' Describe function parameters where it's appropriate. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_trigger.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 4a2efa00f7f2..e817537cdfb5 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -19,6 +19,9 @@ /** * st_sensors_new_samples_available() - check if more samples came in + * @indio_dev: IIO device reference. + * @sdata: Sensor data. + * * returns: * 0 - no new samples available * 1 - new samples available -- cgit v1.2.3 From 0d15190f53b4e21aa3d15f1ace4c0807a269a763 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sun, 15 Dec 2019 16:00:45 -0800 Subject: iio: chemical: atlas-ph-sensor: rename atlas-ph-sensor to atlas-sensor Since the orginal scope of the driver was to only support the pH product from Atlas it has evolved to other sensors. Rename the file, driver name, and regmap to atlas-sensor which reflects this, although keep CONFIG_ATLAS_PH_SENSOR to not cause regressions with current configurations. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/Makefile | 2 +- drivers/iio/chemical/atlas-ph-sensor.c | 684 --------------------------------- drivers/iio/chemical/atlas-sensor.c | 684 +++++++++++++++++++++++++++++++++ 3 files changed, 685 insertions(+), 685 deletions(-) delete mode 100644 drivers/iio/chemical/atlas-ph-sensor.c create mode 100644 drivers/iio/chemical/atlas-sensor.c (limited to 'drivers') diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index f97270bc4034..33d3a595dda9 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -4,7 +4,7 @@ # # When adding new entries keep the list in alphabetical order -obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o +obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-sensor.o obj-$(CONFIG_BME680) += bme680_core.o obj-$(CONFIG_BME680_I2C) += bme680_i2c.o obj-$(CONFIG_BME680_SPI) += bme680_spi.o diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c deleted file mode 100644 index b7c20c74239b..000000000000 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ /dev/null @@ -1,684 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor - * - * Copyright (C) 2015-2018 Matt Ranostay - * Author: Matt Ranostay - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ATLAS_REGMAP_NAME "atlas_ph_regmap" -#define ATLAS_DRV_NAME "atlas_ph" - -#define ATLAS_REG_DEV_TYPE 0x00 -#define ATLAS_REG_DEV_VERSION 0x01 - -#define ATLAS_REG_INT_CONTROL 0x04 -#define ATLAS_REG_INT_CONTROL_EN BIT(3) - -#define ATLAS_REG_PWR_CONTROL 0x06 - -#define ATLAS_REG_PH_CALIB_STATUS 0x0d -#define ATLAS_REG_PH_CALIB_STATUS_MASK 0x07 -#define ATLAS_REG_PH_CALIB_STATUS_LOW BIT(0) -#define ATLAS_REG_PH_CALIB_STATUS_MID BIT(1) -#define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2) - -#define ATLAS_REG_EC_CALIB_STATUS 0x0f -#define ATLAS_REG_EC_CALIB_STATUS_MASK 0x0f -#define ATLAS_REG_EC_CALIB_STATUS_DRY BIT(0) -#define ATLAS_REG_EC_CALIB_STATUS_SINGLE BIT(1) -#define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2) -#define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3) - -#define ATLAS_REG_PH_TEMP_DATA 0x0e -#define ATLAS_REG_PH_DATA 0x16 - -#define ATLAS_REG_EC_PROBE 0x08 -#define ATLAS_REG_EC_TEMP_DATA 0x10 -#define ATLAS_REG_EC_DATA 0x18 -#define ATLAS_REG_TDS_DATA 0x1c -#define ATLAS_REG_PSS_DATA 0x20 - -#define ATLAS_REG_ORP_CALIB_STATUS 0x0d -#define ATLAS_REG_ORP_DATA 0x0e - -#define ATLAS_PH_INT_TIME_IN_MS 450 -#define ATLAS_EC_INT_TIME_IN_MS 650 -#define ATLAS_ORP_INT_TIME_IN_MS 450 - -enum { - ATLAS_PH_SM, - ATLAS_EC_SM, - ATLAS_ORP_SM, -}; - -struct atlas_data { - struct i2c_client *client; - struct iio_trigger *trig; - struct atlas_device *chip; - struct regmap *regmap; - struct irq_work work; - - __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ -}; - -static const struct regmap_config atlas_regmap_config = { - .name = ATLAS_REGMAP_NAME, - .reg_bits = 8, - .val_bits = 8, -}; - -static const struct iio_chan_spec atlas_ph_channels[] = { - { - .type = IIO_PH, - .address = ATLAS_REG_PH_DATA, - .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .scan_index = 0, - .scan_type = { - .sign = 'u', - .realbits = 32, - .storagebits = 32, - .endianness = IIO_BE, - }, - }, - IIO_CHAN_SOFT_TIMESTAMP(1), - { - .type = IIO_TEMP, - .address = ATLAS_REG_PH_TEMP_DATA, - .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .output = 1, - .scan_index = -1 - }, -}; - -#define ATLAS_EC_CHANNEL(_idx, _addr) \ - {\ - .type = IIO_CONCENTRATION, \ - .indexed = 1, \ - .channel = _idx, \ - .address = _addr, \ - .info_mask_separate = \ - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \ - .scan_index = _idx + 1, \ - .scan_type = { \ - .sign = 'u', \ - .realbits = 32, \ - .storagebits = 32, \ - .endianness = IIO_BE, \ - }, \ - } - -static const struct iio_chan_spec atlas_ec_channels[] = { - { - .type = IIO_ELECTRICALCONDUCTIVITY, - .address = ATLAS_REG_EC_DATA, - .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .scan_index = 0, - .scan_type = { - .sign = 'u', - .realbits = 32, - .storagebits = 32, - .endianness = IIO_BE, - }, - }, - ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA), - ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA), - IIO_CHAN_SOFT_TIMESTAMP(3), - { - .type = IIO_TEMP, - .address = ATLAS_REG_EC_TEMP_DATA, - .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .output = 1, - .scan_index = -1 - }, -}; - -static const struct iio_chan_spec atlas_orp_channels[] = { - { - .type = IIO_VOLTAGE, - .address = ATLAS_REG_ORP_DATA, - .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .scan_index = 0, - .scan_type = { - .sign = 's', - .realbits = 32, - .storagebits = 32, - .endianness = IIO_BE, - }, - }, - IIO_CHAN_SOFT_TIMESTAMP(1), -}; - -static int atlas_check_ph_calibration(struct atlas_data *data) -{ - struct device *dev = &data->client->dev; - int ret; - unsigned int val; - - ret = regmap_read(data->regmap, ATLAS_REG_PH_CALIB_STATUS, &val); - if (ret) - return ret; - - if (!(val & ATLAS_REG_PH_CALIB_STATUS_MASK)) { - dev_warn(dev, "device has not been calibrated\n"); - return 0; - } - - if (!(val & ATLAS_REG_PH_CALIB_STATUS_LOW)) - dev_warn(dev, "device missing low point calibration\n"); - - if (!(val & ATLAS_REG_PH_CALIB_STATUS_MID)) - dev_warn(dev, "device missing mid point calibration\n"); - - if (!(val & ATLAS_REG_PH_CALIB_STATUS_HIGH)) - dev_warn(dev, "device missing high point calibration\n"); - - return 0; -} - -static int atlas_check_ec_calibration(struct atlas_data *data) -{ - struct device *dev = &data->client->dev; - int ret; - unsigned int val; - __be16 rval; - - ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &rval, 2); - if (ret) - return ret; - - val = be16_to_cpu(rval); - dev_info(dev, "probe set to K = %d.%.2d", val / 100, val % 100); - - ret = regmap_read(data->regmap, ATLAS_REG_EC_CALIB_STATUS, &val); - if (ret) - return ret; - - if (!(val & ATLAS_REG_EC_CALIB_STATUS_MASK)) { - dev_warn(dev, "device has not been calibrated\n"); - return 0; - } - - if (!(val & ATLAS_REG_EC_CALIB_STATUS_DRY)) - dev_warn(dev, "device missing dry point calibration\n"); - - if (val & ATLAS_REG_EC_CALIB_STATUS_SINGLE) { - dev_warn(dev, "device using single point calibration\n"); - } else { - if (!(val & ATLAS_REG_EC_CALIB_STATUS_LOW)) - dev_warn(dev, "device missing low point calibration\n"); - - if (!(val & ATLAS_REG_EC_CALIB_STATUS_HIGH)) - dev_warn(dev, "device missing high point calibration\n"); - } - - return 0; -} - -static int atlas_check_orp_calibration(struct atlas_data *data) -{ - struct device *dev = &data->client->dev; - int ret; - unsigned int val; - - ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val); - if (ret) - return ret; - - if (!val) - dev_warn(dev, "device has not been calibrated\n"); - - return 0; -}; - -struct atlas_device { - const struct iio_chan_spec *channels; - int num_channels; - int data_reg; - - int (*calibration)(struct atlas_data *data); - int delay; -}; - -static struct atlas_device atlas_devices[] = { - [ATLAS_PH_SM] = { - .channels = atlas_ph_channels, - .num_channels = 3, - .data_reg = ATLAS_REG_PH_DATA, - .calibration = &atlas_check_ph_calibration, - .delay = ATLAS_PH_INT_TIME_IN_MS, - }, - [ATLAS_EC_SM] = { - .channels = atlas_ec_channels, - .num_channels = 5, - .data_reg = ATLAS_REG_EC_DATA, - .calibration = &atlas_check_ec_calibration, - .delay = ATLAS_EC_INT_TIME_IN_MS, - }, - [ATLAS_ORP_SM] = { - .channels = atlas_orp_channels, - .num_channels = 2, - .data_reg = ATLAS_REG_ORP_DATA, - .calibration = &atlas_check_orp_calibration, - .delay = ATLAS_ORP_INT_TIME_IN_MS, - }, -}; - -static int atlas_set_powermode(struct atlas_data *data, int on) -{ - return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on); -} - -static int atlas_set_interrupt(struct atlas_data *data, bool state) -{ - return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL, - ATLAS_REG_INT_CONTROL_EN, - state ? ATLAS_REG_INT_CONTROL_EN : 0); -} - -static int atlas_buffer_postenable(struct iio_dev *indio_dev) -{ - struct atlas_data *data = iio_priv(indio_dev); - int ret; - - ret = iio_triggered_buffer_postenable(indio_dev); - if (ret) - return ret; - - ret = pm_runtime_get_sync(&data->client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&data->client->dev); - return ret; - } - - return atlas_set_interrupt(data, true); -} - -static int atlas_buffer_predisable(struct iio_dev *indio_dev) -{ - struct atlas_data *data = iio_priv(indio_dev); - int ret; - - ret = atlas_set_interrupt(data, false); - if (ret) - return ret; - - pm_runtime_mark_last_busy(&data->client->dev); - ret = pm_runtime_put_autosuspend(&data->client->dev); - if (ret) - return ret; - - return iio_triggered_buffer_predisable(indio_dev); -} - -static const struct iio_trigger_ops atlas_interrupt_trigger_ops = { -}; - -static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { - .postenable = atlas_buffer_postenable, - .predisable = atlas_buffer_predisable, -}; - -static void atlas_work_handler(struct irq_work *work) -{ - struct atlas_data *data = container_of(work, struct atlas_data, work); - - iio_trigger_poll(data->trig); -} - -static irqreturn_t atlas_trigger_handler(int irq, void *private) -{ - struct iio_poll_func *pf = private; - struct iio_dev *indio_dev = pf->indio_dev; - struct atlas_data *data = iio_priv(indio_dev); - int ret; - - ret = regmap_bulk_read(data->regmap, data->chip->data_reg, - (u8 *) &data->buffer, - sizeof(__be32) * (data->chip->num_channels - 2)); - - if (!ret) - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns(indio_dev)); - - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -static irqreturn_t atlas_interrupt_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct atlas_data *data = iio_priv(indio_dev); - - irq_work_queue(&data->work); - - return IRQ_HANDLED; -} - -static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val) -{ - struct device *dev = &data->client->dev; - int suspended = pm_runtime_suspended(dev); - int ret; - - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); - return ret; - } - - if (suspended) - msleep(data->chip->delay); - - ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val)); - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - - return ret; -} - -static int atlas_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) -{ - struct atlas_data *data = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_RAW: { - int ret; - __be32 reg; - - switch (chan->type) { - case IIO_TEMP: - ret = regmap_bulk_read(data->regmap, chan->address, - (u8 *) ®, sizeof(reg)); - break; - case IIO_PH: - case IIO_CONCENTRATION: - case IIO_ELECTRICALCONDUCTIVITY: - case IIO_VOLTAGE: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = atlas_read_measurement(data, chan->address, ®); - - iio_device_release_direct_mode(indio_dev); - break; - default: - ret = -EINVAL; - } - - if (!ret) { - *val = be32_to_cpu(reg); - ret = IIO_VAL_INT; - } - return ret; - } - case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_TEMP: - *val = 10; - return IIO_VAL_INT; - case IIO_PH: - *val = 1; /* 0.001 */ - *val2 = 1000; - break; - case IIO_ELECTRICALCONDUCTIVITY: - *val = 1; /* 0.00001 */ - *val2 = 100000; - break; - case IIO_CONCENTRATION: - *val = 0; /* 0.000000001 */ - *val2 = 1000; - return IIO_VAL_INT_PLUS_NANO; - case IIO_VOLTAGE: - *val = 1; /* 0.1 */ - *val2 = 10; - break; - default: - return -EINVAL; - } - return IIO_VAL_FRACTIONAL; - } - - return -EINVAL; -} - -static int atlas_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct atlas_data *data = iio_priv(indio_dev); - __be32 reg = cpu_to_be32(val / 10); - - if (val2 != 0 || val < 0 || val > 20000) - return -EINVAL; - - if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_TEMP) - return -EINVAL; - - return regmap_bulk_write(data->regmap, chan->address, - ®, sizeof(reg)); -} - -static const struct iio_info atlas_info = { - .read_raw = atlas_read_raw, - .write_raw = atlas_write_raw, -}; - -static const struct i2c_device_id atlas_id[] = { - { "atlas-ph-sm", ATLAS_PH_SM}, - { "atlas-ec-sm", ATLAS_EC_SM}, - { "atlas-orp-sm", ATLAS_ORP_SM}, - {} -}; -MODULE_DEVICE_TABLE(i2c, atlas_id); - -static const struct of_device_id atlas_dt_ids[] = { - { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, - { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, }, - { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, }, - { } -}; -MODULE_DEVICE_TABLE(of, atlas_dt_ids); - -static int atlas_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct atlas_data *data; - struct atlas_device *chip; - const struct of_device_id *of_id; - struct iio_trigger *trig; - struct iio_dev *indio_dev; - int ret; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); - if (!indio_dev) - return -ENOMEM; - - of_id = of_match_device(atlas_dt_ids, &client->dev); - if (!of_id) - chip = &atlas_devices[id->driver_data]; - else - chip = &atlas_devices[(unsigned long)of_id->data]; - - indio_dev->info = &atlas_info; - indio_dev->name = ATLAS_DRV_NAME; - indio_dev->channels = chip->channels; - indio_dev->num_channels = chip->num_channels; - indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE; - indio_dev->dev.parent = &client->dev; - - trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); - - if (!trig) - return -ENOMEM; - - data = iio_priv(indio_dev); - data->client = client; - data->trig = trig; - data->chip = chip; - trig->dev.parent = indio_dev->dev.parent; - trig->ops = &atlas_interrupt_trigger_ops; - iio_trigger_set_drvdata(trig, indio_dev); - - i2c_set_clientdata(client, indio_dev); - - data->regmap = devm_regmap_init_i2c(client, &atlas_regmap_config); - if (IS_ERR(data->regmap)) { - dev_err(&client->dev, "regmap initialization failed\n"); - return PTR_ERR(data->regmap); - } - - ret = pm_runtime_set_active(&client->dev); - if (ret) - return ret; - - if (client->irq <= 0) { - dev_err(&client->dev, "no valid irq defined\n"); - return -EINVAL; - } - - ret = chip->calibration(data); - if (ret) - return ret; - - ret = iio_trigger_register(trig); - if (ret) { - dev_err(&client->dev, "failed to register trigger\n"); - return ret; - } - - ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, - &atlas_trigger_handler, &atlas_buffer_setup_ops); - if (ret) { - dev_err(&client->dev, "cannot setup iio trigger\n"); - goto unregister_trigger; - } - - init_irq_work(&data->work, atlas_work_handler); - - /* interrupt pin toggles on new conversion */ - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, atlas_interrupt_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "atlas_irq", - indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); - goto unregister_buffer; - } - - ret = atlas_set_powermode(data, 1); - if (ret) { - dev_err(&client->dev, "cannot power device on"); - goto unregister_buffer; - } - - pm_runtime_enable(&client->dev); - pm_runtime_set_autosuspend_delay(&client->dev, 2500); - pm_runtime_use_autosuspend(&client->dev); - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&client->dev, "unable to register device\n"); - goto unregister_pm; - } - - return 0; - -unregister_pm: - pm_runtime_disable(&client->dev); - atlas_set_powermode(data, 0); - -unregister_buffer: - iio_triggered_buffer_cleanup(indio_dev); - -unregister_trigger: - iio_trigger_unregister(data->trig); - - return ret; -} - -static int atlas_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct atlas_data *data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - iio_trigger_unregister(data->trig); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); - - return atlas_set_powermode(data, 0); -} - -#ifdef CONFIG_PM -static int atlas_runtime_suspend(struct device *dev) -{ - struct atlas_data *data = - iio_priv(i2c_get_clientdata(to_i2c_client(dev))); - - return atlas_set_powermode(data, 0); -} - -static int atlas_runtime_resume(struct device *dev) -{ - struct atlas_data *data = - iio_priv(i2c_get_clientdata(to_i2c_client(dev))); - - return atlas_set_powermode(data, 1); -} -#endif - -static const struct dev_pm_ops atlas_pm_ops = { - SET_RUNTIME_PM_OPS(atlas_runtime_suspend, - atlas_runtime_resume, NULL) -}; - -static struct i2c_driver atlas_driver = { - .driver = { - .name = ATLAS_DRV_NAME, - .of_match_table = of_match_ptr(atlas_dt_ids), - .pm = &atlas_pm_ops, - }, - .probe = atlas_probe, - .remove = atlas_remove, - .id_table = atlas_id, -}; -module_i2c_driver(atlas_driver); - -MODULE_AUTHOR("Matt Ranostay "); -MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor"); -MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c new file mode 100644 index 000000000000..99095c80531b --- /dev/null +++ b/drivers/iio/chemical/atlas-sensor.c @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * atlas-sensor.c - Support for Atlas Scientific OEM SM sensors + * + * Copyright (C) 2015-2019 Konsulko Group + * Author: Matt Ranostay + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ATLAS_REGMAP_NAME "atlas_regmap" +#define ATLAS_DRV_NAME "atlas" + +#define ATLAS_REG_DEV_TYPE 0x00 +#define ATLAS_REG_DEV_VERSION 0x01 + +#define ATLAS_REG_INT_CONTROL 0x04 +#define ATLAS_REG_INT_CONTROL_EN BIT(3) + +#define ATLAS_REG_PWR_CONTROL 0x06 + +#define ATLAS_REG_PH_CALIB_STATUS 0x0d +#define ATLAS_REG_PH_CALIB_STATUS_MASK 0x07 +#define ATLAS_REG_PH_CALIB_STATUS_LOW BIT(0) +#define ATLAS_REG_PH_CALIB_STATUS_MID BIT(1) +#define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2) + +#define ATLAS_REG_EC_CALIB_STATUS 0x0f +#define ATLAS_REG_EC_CALIB_STATUS_MASK 0x0f +#define ATLAS_REG_EC_CALIB_STATUS_DRY BIT(0) +#define ATLAS_REG_EC_CALIB_STATUS_SINGLE BIT(1) +#define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2) +#define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3) + +#define ATLAS_REG_PH_TEMP_DATA 0x0e +#define ATLAS_REG_PH_DATA 0x16 + +#define ATLAS_REG_EC_PROBE 0x08 +#define ATLAS_REG_EC_TEMP_DATA 0x10 +#define ATLAS_REG_EC_DATA 0x18 +#define ATLAS_REG_TDS_DATA 0x1c +#define ATLAS_REG_PSS_DATA 0x20 + +#define ATLAS_REG_ORP_CALIB_STATUS 0x0d +#define ATLAS_REG_ORP_DATA 0x0e + +#define ATLAS_PH_INT_TIME_IN_MS 450 +#define ATLAS_EC_INT_TIME_IN_MS 650 +#define ATLAS_ORP_INT_TIME_IN_MS 450 + +enum { + ATLAS_PH_SM, + ATLAS_EC_SM, + ATLAS_ORP_SM, +}; + +struct atlas_data { + struct i2c_client *client; + struct iio_trigger *trig; + struct atlas_device *chip; + struct regmap *regmap; + struct irq_work work; + + __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ +}; + +static const struct regmap_config atlas_regmap_config = { + .name = ATLAS_REGMAP_NAME, + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct iio_chan_spec atlas_ph_channels[] = { + { + .type = IIO_PH, + .address = ATLAS_REG_PH_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), + { + .type = IIO_TEMP, + .address = ATLAS_REG_PH_TEMP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .output = 1, + .scan_index = -1 + }, +}; + +#define ATLAS_EC_CHANNEL(_idx, _addr) \ + {\ + .type = IIO_CONCENTRATION, \ + .indexed = 1, \ + .channel = _idx, \ + .address = _addr, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = _idx + 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec atlas_ec_channels[] = { + { + .type = IIO_ELECTRICALCONDUCTIVITY, + .address = ATLAS_REG_EC_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA), + ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA), + IIO_CHAN_SOFT_TIMESTAMP(3), + { + .type = IIO_TEMP, + .address = ATLAS_REG_EC_TEMP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .output = 1, + .scan_index = -1 + }, +}; + +static const struct iio_chan_spec atlas_orp_channels[] = { + { + .type = IIO_VOLTAGE, + .address = ATLAS_REG_ORP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static int atlas_check_ph_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_PH_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_LOW)) + dev_warn(dev, "device missing low point calibration\n"); + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_MID)) + dev_warn(dev, "device missing mid point calibration\n"); + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_HIGH)) + dev_warn(dev, "device missing high point calibration\n"); + + return 0; +} + +static int atlas_check_ec_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + __be16 rval; + + ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &rval, 2); + if (ret) + return ret; + + val = be16_to_cpu(rval); + dev_info(dev, "probe set to K = %d.%.2d", val / 100, val % 100); + + ret = regmap_read(data->regmap, ATLAS_REG_EC_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_DRY)) + dev_warn(dev, "device missing dry point calibration\n"); + + if (val & ATLAS_REG_EC_CALIB_STATUS_SINGLE) { + dev_warn(dev, "device using single point calibration\n"); + } else { + if (!(val & ATLAS_REG_EC_CALIB_STATUS_LOW)) + dev_warn(dev, "device missing low point calibration\n"); + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_HIGH)) + dev_warn(dev, "device missing high point calibration\n"); + } + + return 0; +} + +static int atlas_check_orp_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!val) + dev_warn(dev, "device has not been calibrated\n"); + + return 0; +}; + +struct atlas_device { + const struct iio_chan_spec *channels; + int num_channels; + int data_reg; + + int (*calibration)(struct atlas_data *data); + int delay; +}; + +static struct atlas_device atlas_devices[] = { + [ATLAS_PH_SM] = { + .channels = atlas_ph_channels, + .num_channels = 3, + .data_reg = ATLAS_REG_PH_DATA, + .calibration = &atlas_check_ph_calibration, + .delay = ATLAS_PH_INT_TIME_IN_MS, + }, + [ATLAS_EC_SM] = { + .channels = atlas_ec_channels, + .num_channels = 5, + .data_reg = ATLAS_REG_EC_DATA, + .calibration = &atlas_check_ec_calibration, + .delay = ATLAS_EC_INT_TIME_IN_MS, + }, + [ATLAS_ORP_SM] = { + .channels = atlas_orp_channels, + .num_channels = 2, + .data_reg = ATLAS_REG_ORP_DATA, + .calibration = &atlas_check_orp_calibration, + .delay = ATLAS_ORP_INT_TIME_IN_MS, + }, +}; + +static int atlas_set_powermode(struct atlas_data *data, int on) +{ + return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on); +} + +static int atlas_set_interrupt(struct atlas_data *data, bool state) +{ + return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL, + ATLAS_REG_INT_CONTROL_EN, + state ? ATLAS_REG_INT_CONTROL_EN : 0); +} + +static int atlas_buffer_postenable(struct iio_dev *indio_dev) +{ + struct atlas_data *data = iio_priv(indio_dev); + int ret; + + ret = iio_triggered_buffer_postenable(indio_dev); + if (ret) + return ret; + + ret = pm_runtime_get_sync(&data->client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&data->client->dev); + return ret; + } + + return atlas_set_interrupt(data, true); +} + +static int atlas_buffer_predisable(struct iio_dev *indio_dev) +{ + struct atlas_data *data = iio_priv(indio_dev); + int ret; + + ret = atlas_set_interrupt(data, false); + if (ret) + return ret; + + pm_runtime_mark_last_busy(&data->client->dev); + ret = pm_runtime_put_autosuspend(&data->client->dev); + if (ret) + return ret; + + return iio_triggered_buffer_predisable(indio_dev); +} + +static const struct iio_trigger_ops atlas_interrupt_trigger_ops = { +}; + +static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { + .postenable = atlas_buffer_postenable, + .predisable = atlas_buffer_predisable, +}; + +static void atlas_work_handler(struct irq_work *work) +{ + struct atlas_data *data = container_of(work, struct atlas_data, work); + + iio_trigger_poll(data->trig); +} + +static irqreturn_t atlas_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct atlas_data *data = iio_priv(indio_dev); + int ret; + + ret = regmap_bulk_read(data->regmap, data->chip->data_reg, + (u8 *) &data->buffer, + sizeof(__be32) * (data->chip->num_channels - 2)); + + if (!ret) + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t atlas_interrupt_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct atlas_data *data = iio_priv(indio_dev); + + irq_work_queue(&data->work); + + return IRQ_HANDLED; +} + +static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val) +{ + struct device *dev = &data->client->dev; + int suspended = pm_runtime_suspended(dev); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + if (suspended) + msleep(data->chip->delay); + + ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int atlas_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct atlas_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + int ret; + __be32 reg; + + switch (chan->type) { + case IIO_TEMP: + ret = regmap_bulk_read(data->regmap, chan->address, + (u8 *) ®, sizeof(reg)); + break; + case IIO_PH: + case IIO_CONCENTRATION: + case IIO_ELECTRICALCONDUCTIVITY: + case IIO_VOLTAGE: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = atlas_read_measurement(data, chan->address, ®); + + iio_device_release_direct_mode(indio_dev); + break; + default: + ret = -EINVAL; + } + + if (!ret) { + *val = be32_to_cpu(reg); + ret = IIO_VAL_INT; + } + return ret; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + *val = 10; + return IIO_VAL_INT; + case IIO_PH: + *val = 1; /* 0.001 */ + *val2 = 1000; + break; + case IIO_ELECTRICALCONDUCTIVITY: + *val = 1; /* 0.00001 */ + *val2 = 100000; + break; + case IIO_CONCENTRATION: + *val = 0; /* 0.000000001 */ + *val2 = 1000; + return IIO_VAL_INT_PLUS_NANO; + case IIO_VOLTAGE: + *val = 1; /* 0.1 */ + *val2 = 10; + break; + default: + return -EINVAL; + } + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static int atlas_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct atlas_data *data = iio_priv(indio_dev); + __be32 reg = cpu_to_be32(val / 10); + + if (val2 != 0 || val < 0 || val > 20000) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_TEMP) + return -EINVAL; + + return regmap_bulk_write(data->regmap, chan->address, + ®, sizeof(reg)); +} + +static const struct iio_info atlas_info = { + .read_raw = atlas_read_raw, + .write_raw = atlas_write_raw, +}; + +static const struct i2c_device_id atlas_id[] = { + { "atlas-ph-sm", ATLAS_PH_SM}, + { "atlas-ec-sm", ATLAS_EC_SM}, + { "atlas-orp-sm", ATLAS_ORP_SM}, + {} +}; +MODULE_DEVICE_TABLE(i2c, atlas_id); + +static const struct of_device_id atlas_dt_ids[] = { + { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, + { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, }, + { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, }, + { } +}; +MODULE_DEVICE_TABLE(of, atlas_dt_ids); + +static int atlas_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct atlas_data *data; + struct atlas_device *chip; + const struct of_device_id *of_id; + struct iio_trigger *trig; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + of_id = of_match_device(atlas_dt_ids, &client->dev); + if (!of_id) + chip = &atlas_devices[id->driver_data]; + else + chip = &atlas_devices[(unsigned long)of_id->data]; + + indio_dev->info = &atlas_info; + indio_dev->name = ATLAS_DRV_NAME; + indio_dev->channels = chip->channels; + indio_dev->num_channels = chip->num_channels; + indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE; + indio_dev->dev.parent = &client->dev; + + trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + + if (!trig) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + data->trig = trig; + data->chip = chip; + trig->dev.parent = indio_dev->dev.parent; + trig->ops = &atlas_interrupt_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + + i2c_set_clientdata(client, indio_dev); + + data->regmap = devm_regmap_init_i2c(client, &atlas_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "regmap initialization failed\n"); + return PTR_ERR(data->regmap); + } + + ret = pm_runtime_set_active(&client->dev); + if (ret) + return ret; + + if (client->irq <= 0) { + dev_err(&client->dev, "no valid irq defined\n"); + return -EINVAL; + } + + ret = chip->calibration(data); + if (ret) + return ret; + + ret = iio_trigger_register(trig); + if (ret) { + dev_err(&client->dev, "failed to register trigger\n"); + return ret; + } + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &atlas_trigger_handler, &atlas_buffer_setup_ops); + if (ret) { + dev_err(&client->dev, "cannot setup iio trigger\n"); + goto unregister_trigger; + } + + init_irq_work(&data->work, atlas_work_handler); + + /* interrupt pin toggles on new conversion */ + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, atlas_interrupt_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "atlas_irq", + indio_dev); + if (ret) { + dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + goto unregister_buffer; + } + + ret = atlas_set_powermode(data, 1); + if (ret) { + dev_err(&client->dev, "cannot power device on"); + goto unregister_buffer; + } + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 2500); + pm_runtime_use_autosuspend(&client->dev); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&client->dev, "unable to register device\n"); + goto unregister_pm; + } + + return 0; + +unregister_pm: + pm_runtime_disable(&client->dev); + atlas_set_powermode(data, 0); + +unregister_buffer: + iio_triggered_buffer_cleanup(indio_dev); + +unregister_trigger: + iio_trigger_unregister(data->trig); + + return ret; +} + +static int atlas_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct atlas_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + iio_trigger_unregister(data->trig); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + return atlas_set_powermode(data, 0); +} + +#ifdef CONFIG_PM +static int atlas_runtime_suspend(struct device *dev) +{ + struct atlas_data *data = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return atlas_set_powermode(data, 0); +} + +static int atlas_runtime_resume(struct device *dev) +{ + struct atlas_data *data = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return atlas_set_powermode(data, 1); +} +#endif + +static const struct dev_pm_ops atlas_pm_ops = { + SET_RUNTIME_PM_OPS(atlas_runtime_suspend, + atlas_runtime_resume, NULL) +}; + +static struct i2c_driver atlas_driver = { + .driver = { + .name = ATLAS_DRV_NAME, + .of_match_table = of_match_ptr(atlas_dt_ids), + .pm = &atlas_pm_ops, + }, + .probe = atlas_probe, + .remove = atlas_remove, + .id_table = atlas_id, +}; +module_i2c_driver(atlas_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("Atlas Scientific SM sensors"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4dae3d6147b362b1c4a35d3ef3177358d084d464 Mon Sep 17 00:00:00 2001 From: Chen Zhou Date: Mon, 16 Dec 2019 18:51:01 +0800 Subject: iio: light: remove unneeded semicolon Fixes coccicheck warning: ./drivers/iio/light/lm3533-als.c:745:2-3: Unneeded semicolon Signed-off-by: Chen Zhou Signed-off-by: Jonathan Cameron --- drivers/iio/light/lm3533-als.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 6733b52c22df..bc196c212881 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -742,7 +742,7 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) { dev_err(&als->pdev->dev, "invalid resistor value\n"); return -EINVAL; - }; + } ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val); if (ret) { -- cgit v1.2.3 From ee978bb86d055131364baa617fbdb0a9bb52612a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 14 Dec 2019 17:52:54 +0100 Subject: iio: imu: st_lsm6dsx: export max num of slave devices in st_lsm6dsx_shub_settings Export max number of slave devices supported by the i2c master controller in st_lsm6dsx_shub_settings data structure. This is a preliminary patch to support i2c master controller on lsm6dsm sensors Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 2 ++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 2 ++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 3 +-- 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index f1189eef6e37..140e4c6971f3 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -176,6 +176,7 @@ struct st_lsm6dsx_hw_ts_settings { * @pullup_en: i2c controller pull-up register info (addr + mask). * @aux_sens: aux sensor register info (addr + mask). * @wr_once: write_once register info (addr + mask). + * @num_ext_dev: max number of slave devices. * @shub_out: sensor hub first output register info. * @slv0_addr: slave0 address in secondary page. * @dw_slv0_addr: slave0 write register address in secondary page. @@ -187,6 +188,7 @@ struct st_lsm6dsx_shub_settings { struct st_lsm6dsx_reg pullup_en; struct st_lsm6dsx_reg aux_sens; struct st_lsm6dsx_reg wr_once; + u8 num_ext_dev; u8 shub_out; u8 slv0_addr; u8 dw_slv0_addr; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 95d7eb1d9cdb..91dc9796add7 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -883,6 +883,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x14, .mask = BIT(6), }, + .num_ext_dev = 3, .shub_out = 0x02, .slv0_addr = 0x15, .dw_slv0_addr = 0x21, @@ -1257,6 +1258,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x14, .mask = BIT(6), }, + .num_ext_dev = 3, .shub_out = 0x02, .slv0_addr = 0x15, .dw_slv0_addr = 0x21, diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index fa5d1001a46c..5684e663d0aa 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -30,7 +30,6 @@ #include "st_lsm6dsx.h" -#define ST_LSM6DSX_MAX_SLV_NUM 3 #define ST_LSM6DSX_SLV_ADDR(n, base) ((base) + (n) * 3) #define ST_LSM6DSX_SLV_SUB_ADDR(n, base) ((base) + 1 + (n) * 3) #define ST_LSM6DSX_SLV_CONFIG(n, base) ((base) + 2 + (n) * 3) @@ -770,7 +769,7 @@ int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name) if (err < 0) return err; - if (++num_ext_dev >= ST_LSM6DSX_MAX_SLV_NUM) + if (++num_ext_dev >= hw->settings->shub_settings.num_ext_dev) break; id++; } -- cgit v1.2.3 From 007f2ebbac2ac7f0f6d80ca5afd3f51c5a083838 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 14 Dec 2019 17:52:55 +0100 Subject: iio: imu: st_lsm6dsx: check if master_enable is located in primary page Check if the master enable register is located in the primary or in the secondary memory page. This is a preliminary patch to support i2c master controller on lsm6dsm devices Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 6 +++++- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 2 ++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 11 +++++++---- 3 files changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 140e4c6971f3..bd1c77d16147 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -184,7 +184,11 @@ struct st_lsm6dsx_hw_ts_settings { */ struct st_lsm6dsx_shub_settings { struct st_lsm6dsx_reg page_mux; - struct st_lsm6dsx_reg master_en; + struct { + bool sec_page; + u8 addr; + u8 mask; + } master_en; struct st_lsm6dsx_reg pullup_en; struct st_lsm6dsx_reg aux_sens; struct st_lsm6dsx_reg wr_once; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 91dc9796add7..b8cfea977968 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -868,6 +868,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = BIT(6), }, .master_en = { + .sec_page = true, .addr = 0x14, .mask = BIT(2), }, @@ -1243,6 +1244,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = BIT(6), }, .master_en = { + .sec_page = true, .addr = 0x14, .mask = BIT(2), }, diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index 5684e663d0aa..767482e61a1e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -185,15 +185,18 @@ static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor, mutex_lock(&hw->page_lock); hub_settings = &hw->settings->shub_settings; - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - goto out; + if (hub_settings->master_en.sec_page) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + } data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask); err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr, hub_settings->master_en.mask, data); - st_lsm6dsx_set_page(hw, false); + if (hub_settings->master_en.sec_page) + st_lsm6dsx_set_page(hw, false); out: mutex_unlock(&hw->page_lock); -- cgit v1.2.3 From 3a4319574584ef5a71e11780cc83337282818882 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 14 Dec 2019 17:52:56 +0100 Subject: iio: imu: st_lsm6dsx: check if pull_up is located in primary page Check if the pull up register is located in the primary or in the secondary memory page. This is a preliminary patch to support i2c master controller on lsm6dsm devices Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 6 +++++- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 13 +++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index bd1c77d16147..4974f6fafd53 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -189,7 +189,11 @@ struct st_lsm6dsx_shub_settings { u8 addr; u8 mask; } master_en; - struct st_lsm6dsx_reg pullup_en; + struct { + bool sec_page; + u8 addr; + u8 mask; + } pullup_en; struct st_lsm6dsx_reg aux_sens; struct st_lsm6dsx_reg wr_once; u8 num_ext_dev; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index b8cfea977968..b5e9f71be085 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -873,6 +873,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = BIT(2), }, .pullup_en = { + .sec_page = true, .addr = 0x14, .mask = BIT(3), }, @@ -1249,6 +1250,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = BIT(2), }, .pullup_en = { + .sec_page = true, .addr = 0x14, .mask = BIT(3), }, @@ -1864,16 +1866,19 @@ static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw) pdata = (struct st_sensors_platform_data *)dev->platform_data; if ((dev_fwnode(dev) && device_property_read_bool(dev, "st,pullups")) || (pdata && pdata->pullups)) { - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - return err; + if (hub_settings->pullup_en.sec_page) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + return err; + } data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask); err = regmap_update_bits(hw->regmap, hub_settings->pullup_en.addr, hub_settings->pullup_en.mask, data); - st_lsm6dsx_set_page(hw, false); + if (hub_settings->pullup_en.sec_page) + st_lsm6dsx_set_page(hw, false); if (err < 0) return err; -- cgit v1.2.3 From 5b8343e067f8c0ce6e480659956d011e876d3635 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 14 Dec 2019 17:52:57 +0100 Subject: iio: imu: st_lsm6dsx: check if shub_output reg is located in primary page Check if the sensor hub output register is located in the primary or in the secondary memory page. This is a preliminary patch to support i2c master controller on lsm6dsm devices Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 5 ++++- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 10 ++++++++-- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 28 ++++++++++++++++------------ 3 files changed, 28 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 4974f6fafd53..001179e19ba7 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -197,7 +197,10 @@ struct st_lsm6dsx_shub_settings { struct st_lsm6dsx_reg aux_sens; struct st_lsm6dsx_reg wr_once; u8 num_ext_dev; - u8 shub_out; + struct { + bool sec_page; + u8 addr; + } shub_out; u8 slv0_addr; u8 dw_slv0_addr; u8 batch_en; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index b5e9f71be085..49ad70c589bd 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -886,7 +886,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = BIT(6), }, .num_ext_dev = 3, - .shub_out = 0x02, + .shub_out = { + .sec_page = true, + .addr = 0x02, + }, .slv0_addr = 0x15, .dw_slv0_addr = 0x21, .batch_en = BIT(3), @@ -1263,7 +1266,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = BIT(6), }, .num_ext_dev = 3, - .shub_out = 0x02, + .shub_out = { + .sec_page = true, + .addr = 0x02, + }, .slv0_addr = 0x15, .dw_slv0_addr = 0x21, .batch_en = BIT(3), diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index 767482e61a1e..ccc59682cb66 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -105,20 +105,27 @@ static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw) * * Read st_lsm6dsx i2c controller register */ -static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr, - u8 *data, int len) +static int +st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 *data, + int len) { + const struct st_lsm6dsx_shub_settings *hub_settings; int err; mutex_lock(&hw->page_lock); - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - goto out; + hub_settings = &hw->settings->shub_settings; + if (hub_settings->shub_out.sec_page) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + } - err = regmap_bulk_read(hw->regmap, addr, data, len); + err = regmap_bulk_read(hw->regmap, hub_settings->shub_out.addr, + data, len); - st_lsm6dsx_set_page(hw, false); + if (hub_settings->shub_out.sec_page) + st_lsm6dsx_set_page(hw, false); out: mutex_unlock(&hw->page_lock); @@ -236,8 +243,7 @@ st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, st_lsm6dsx_shub_wait_complete(hw); - err = st_lsm6dsx_shub_read_reg(hw, hub_settings->shub_out, data, - len & ST_LS6DSX_READ_OP_MASK); + err = st_lsm6dsx_shub_read_reg(hw, data, len & ST_LS6DSX_READ_OP_MASK); st_lsm6dsx_shub_master_enable(sensor, false); @@ -719,9 +725,7 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, st_lsm6dsx_shub_wait_complete(hw); - err = st_lsm6dsx_shub_read_reg(hw, - hub_settings->shub_out, - &data, sizeof(data)); + err = st_lsm6dsx_shub_read_reg(hw, &data, sizeof(data)); st_lsm6dsx_shub_master_enable(sensor, false); -- cgit v1.2.3 From 111b0875774b9622161e2ed6dd176e21c8665da0 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 14 Dec 2019 17:52:58 +0100 Subject: iio: imu: st_lsm6dsx: rename st_lsm6dsx_shub_read_reg in st_lsm6dsx_shub_read_output Rename st_lsm6dsx_shub_read_reg routine in st_lsm6dsx_shub_read_output since it is used to read from sensorhub channel0 output register Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index ccc59682cb66..dc739dfb36f2 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -101,13 +101,13 @@ static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw) } /** - * st_lsm6dsx_shub_read_reg - read i2c controller register + * st_lsm6dsx_shub_read_output - read i2c controller register * * Read st_lsm6dsx i2c controller register */ static int -st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 *data, - int len) +st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, + int len) { const struct st_lsm6dsx_shub_settings *hub_settings; int err; @@ -243,7 +243,8 @@ st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, st_lsm6dsx_shub_wait_complete(hw); - err = st_lsm6dsx_shub_read_reg(hw, data, len & ST_LS6DSX_READ_OP_MASK); + err = st_lsm6dsx_shub_read_output(hw, data, + len & ST_LS6DSX_READ_OP_MASK); st_lsm6dsx_shub_master_enable(sensor, false); @@ -725,7 +726,7 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, st_lsm6dsx_shub_wait_complete(hw); - err = st_lsm6dsx_shub_read_reg(hw, &data, sizeof(data)); + err = st_lsm6dsx_shub_read_output(hw, &data, sizeof(data)); st_lsm6dsx_shub_master_enable(sensor, false); -- cgit v1.2.3 From e485e2a2cfd6404d21066be6378ed42f8ce81060 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 14 Dec 2019 17:52:59 +0100 Subject: iio: imu: st_lsm6dsx: enable sensor-hub support for lsm6dsm Enabled i2c master controller support for LSM6DSM sensor. Enable ext_sensor0 for lsm6dsm. This series has been tested using LIS2MDL as slave device connected to the i2c controller of the LSM6DSM Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 4 +++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 27 +++++++++++---- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 47 ++++++++++++++++++++++++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 31 ++++++++++++----- 4 files changed, 94 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 001179e19ba7..a763ff46f596 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -176,11 +176,13 @@ struct st_lsm6dsx_hw_ts_settings { * @pullup_en: i2c controller pull-up register info (addr + mask). * @aux_sens: aux sensor register info (addr + mask). * @wr_once: write_once register info (addr + mask). + * @emb_func: embedded function register info (addr + mask). * @num_ext_dev: max number of slave devices. * @shub_out: sensor hub first output register info. * @slv0_addr: slave0 address in secondary page. * @dw_slv0_addr: slave0 write register address in secondary page. * @batch_en: Enable/disable FIFO batching. + * @pause: controller pause value. */ struct st_lsm6dsx_shub_settings { struct st_lsm6dsx_reg page_mux; @@ -196,6 +198,7 @@ struct st_lsm6dsx_shub_settings { } pullup_en; struct st_lsm6dsx_reg aux_sens; struct st_lsm6dsx_reg wr_once; + struct st_lsm6dsx_reg emb_func; u8 num_ext_dev; struct { bool sec_page; @@ -204,6 +207,7 @@ struct st_lsm6dsx_shub_settings { u8 slv0_addr; u8 dw_slv0_addr; u8 batch_en; + u8 pause; }; struct st_lsm6dsx_event_settings { diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index d416990ae309..cc2b19b60d61 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -332,12 +332,13 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr, */ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) { + struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor, *ext_sensor = NULL; + int err, acc_sip, gyro_sip, ts_sip, ext_sip, read_len, offset; u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE; u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; - int err, acc_sip, gyro_sip, ts_sip, read_len, offset; - struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor; u8 gyro_buff[ST_LSM6DSX_IIO_BUFF_SIZE]; u8 acc_buff[ST_LSM6DSX_IIO_BUFF_SIZE]; + u8 ext_buff[ST_LSM6DSX_IIO_BUFF_SIZE]; bool reset_ts = false; __le16 fifo_status; s64 ts = 0; @@ -360,6 +361,8 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); + if (hw->iio_devs[ST_LSM6DSX_ID_EXT0]) + ext_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_EXT0]); for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUTL_ADDR, @@ -387,12 +390,13 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) * following pattern is repeated every 9 samples: * - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, .. */ + ext_sip = ext_sensor ? ext_sensor->sip : 0; gyro_sip = gyro_sensor->sip; acc_sip = acc_sensor->sip; ts_sip = hw->ts_sip; offset = 0; - while (acc_sip > 0 || gyro_sip > 0) { + while (acc_sip > 0 || gyro_sip > 0 || ext_sip > 0) { if (gyro_sip > 0) { memcpy(gyro_buff, &hw->buff[offset], ST_LSM6DSX_SAMPLE_SIZE); @@ -403,6 +407,11 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) ST_LSM6DSX_SAMPLE_SIZE); offset += ST_LSM6DSX_SAMPLE_SIZE; } + if (ext_sip > 0) { + memcpy(ext_buff, &hw->buff[offset], + ST_LSM6DSX_SAMPLE_SIZE); + offset += ST_LSM6DSX_SAMPLE_SIZE; + } if (ts_sip-- > 0) { u8 data[ST_LSM6DSX_SAMPLE_SIZE]; @@ -436,6 +445,10 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) iio_push_to_buffers_with_timestamp( hw->iio_devs[ST_LSM6DSX_ID_ACC], acc_buff, acc_sensor->ts_ref + ts); + if (ext_sip-- > 0) + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_EXT0], + ext_buff, ext_sensor->ts_ref + ts); } } @@ -628,12 +641,12 @@ int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) err = st_lsm6dsx_sensor_set_enable(sensor, enable); if (err < 0) goto out; - - err = st_lsm6dsx_set_fifo_odr(sensor, enable); - if (err < 0) - goto out; } + err = st_lsm6dsx_set_fifo_odr(sensor, enable); + if (err < 0) + goto out; + err = st_lsm6dsx_update_decimators(hw); if (err < 0) goto out; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 49ad70c589bd..0c64e35c7599 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -656,6 +656,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x08, .mask = GENMASK(5, 3), }, + [ST_LSM6DSX_ID_EXT0] = { + .addr = 0x09, + .mask = GENMASK(2, 0), + }, }, .fifo_ops = { .update_fifo = st_lsm6dsx_update_fifo, @@ -688,6 +692,39 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(5, 3), }, }, + .shub_settings = { + .page_mux = { + .addr = 0x01, + .mask = BIT(7), + }, + .master_en = { + .addr = 0x1a, + .mask = BIT(0), + }, + .pullup_en = { + .addr = 0x1a, + .mask = BIT(3), + }, + .aux_sens = { + .addr = 0x04, + .mask = GENMASK(5, 4), + }, + .wr_once = { + .addr = 0x07, + .mask = BIT(5), + }, + .emb_func = { + .addr = 0x19, + .mask = BIT(2), + }, + .num_ext_dev = 1, + .shub_out = { + .addr = 0x2e, + }, + .slv0_addr = 0x02, + .dw_slv0_addr = 0x0e, + .pause = 0x7, + }, .event_settings = { .enable_reg = { .addr = 0x58, @@ -1902,6 +1939,16 @@ static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw) hub_settings->aux_sens.mask, data); st_lsm6dsx_set_page(hw, false); + + if (err < 0) + return err; + } + + if (hub_settings->emb_func.addr) { + data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->emb_func.mask); + err = regmap_update_bits(hw->regmap, + hub_settings->emb_func.addr, + hub_settings->emb_func.mask, data); } return err; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index dc739dfb36f2..eea555617d4a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -221,16 +221,21 @@ st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, u8 *data, int len) { const struct st_lsm6dsx_shub_settings *hub_settings; + u8 config[3], slv_addr, slv_config = 0; struct st_lsm6dsx_hw *hw = sensor->hw; - u8 config[3], slv_addr; + const struct st_lsm6dsx_reg *aux_sens; int err; hub_settings = &hw->settings->shub_settings; slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); + aux_sens = &hw->settings->shub_settings.aux_sens; + /* do not overwrite aux_sens */ + if (slv_addr + 2 == aux_sens->addr) + slv_config = ST_LSM6DSX_SHIFT_VAL(3, aux_sens->mask); config[0] = (sensor->ext_info.addr << 1) | 1; config[1] = addr; - config[2] = len & ST_LS6DSX_READ_OP_MASK; + config[2] = (len & ST_LS6DSX_READ_OP_MASK) | slv_config; err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); @@ -248,7 +253,9 @@ st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, st_lsm6dsx_shub_master_enable(sensor, false); - memset(config, 0, sizeof(config)); + config[0] = hub_settings->pause; + config[1] = 0; + config[2] = slv_config; return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); } @@ -305,7 +312,8 @@ st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr, st_lsm6dsx_shub_master_enable(sensor, false); } - memset(config, 0, sizeof(config)); + config[0] = hub_settings->pause; + config[1] = 0; return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); } @@ -697,14 +705,19 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, const struct st_lsm6dsx_ext_dev_settings *settings) { const struct st_lsm6dsx_shub_settings *hub_settings; + u8 config[3], data, slv_addr, slv_config = 0; + const struct st_lsm6dsx_reg *aux_sens; struct st_lsm6dsx_sensor *sensor; - u8 config[3], data, slv_addr; bool found = false; int i, err; + sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); hub_settings = &hw->settings->shub_settings; + aux_sens = &hw->settings->shub_settings.aux_sens; slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); - sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + /* do not overwrite aux_sens */ + if (slv_addr + 2 == aux_sens->addr) + slv_config = ST_LSM6DSX_SHIFT_VAL(3, aux_sens->mask); for (i = 0; i < ARRAY_SIZE(settings->i2c_addr); i++) { if (!settings->i2c_addr[i]) @@ -713,7 +726,7 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, /* read wai slave register */ config[0] = (settings->i2c_addr[i] << 1) | 0x1; config[1] = settings->wai.addr; - config[2] = 0x1; + config[2] = 0x1 | slv_config; err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); @@ -742,7 +755,9 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, } /* reset SLV0 channel */ - memset(config, 0, sizeof(config)); + config[0] = hub_settings->pause; + config[1] = 0; + config[2] = slv_config; err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); if (err < 0) -- cgit v1.2.3 From 964172561ea65f92394ef823405c31f17c57cc90 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Dec 2019 22:38:16 +0100 Subject: iio: accel: bma180: Add dev helper variable Having a shorthand "dev" instead of &client->dev everywhere makes the code easier to read (more compact). Cc: Peter Meerwald Cc: Oleksandr Kravchenko Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma180.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 1574e4604a4f..518efbe4eaf6 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -712,12 +712,13 @@ static const struct iio_trigger_ops bma180_trigger_ops = { static int bma180_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct bma180_data *data; struct iio_dev *indio_dev; enum chip_ids chip; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -725,12 +726,12 @@ static int bma180_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); data->client = client; if (client->dev.of_node) - chip = (enum chip_ids)of_device_get_match_data(&client->dev); + chip = (enum chip_ids)of_device_get_match_data(dev); else chip = id->driver_data; data->part_info = &bma180_part_info[chip]; - ret = iio_read_mount_matrix(&client->dev, "mount-matrix", + ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation); if (ret) return ret; @@ -740,7 +741,7 @@ static int bma180_probe(struct i2c_client *client, goto err_chip_disable; mutex_init(&data->mutex); - indio_dev->dev.parent = &client->dev; + indio_dev->dev.parent = dev; indio_dev->channels = data->part_info->channels; indio_dev->num_channels = data->part_info->num_channels; indio_dev->name = id->name; @@ -755,15 +756,15 @@ static int bma180_probe(struct i2c_client *client, goto err_chip_disable; } - ret = devm_request_irq(&client->dev, client->irq, + ret = devm_request_irq(dev, client->irq, iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING, "bma180_event", data->trig); if (ret) { - dev_err(&client->dev, "unable to request IRQ\n"); + dev_err(dev, "unable to request IRQ\n"); goto err_trigger_free; } - data->trig->dev.parent = &client->dev; + data->trig->dev.parent = dev; data->trig->ops = &bma180_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); indio_dev->trig = iio_trigger_get(data->trig); @@ -776,13 +777,13 @@ static int bma180_probe(struct i2c_client *client, ret = iio_triggered_buffer_setup(indio_dev, NULL, bma180_trigger_handler, NULL); if (ret < 0) { - dev_err(&client->dev, "unable to setup iio triggered buffer\n"); + dev_err(dev, "unable to setup iio triggered buffer\n"); goto err_trigger_unregister; } ret = iio_device_register(indio_dev); if (ret < 0) { - dev_err(&client->dev, "unable to register iio device\n"); + dev_err(dev, "unable to register iio device\n"); goto err_buffer_cleanup; } -- cgit v1.2.3 From c35aae7443023c08d406c9077c9e842b48cdb22f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Dec 2019 22:38:17 +0100 Subject: iio: accel: bma180: Basic regulator support This brings up the VDD and VDDIO regulators using the regulator framework. Platforms that do not use regulators will provide stubs or dummy regulators. Cc: Peter Meerwald Cc: Oleksandr Kravchenko Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma180.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 518efbe4eaf6..c2c15a4f9ee1 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,8 @@ struct bma180_part_info { #define BMA250_INT_RESET_MASK BIT(7) /* Reset pending interrupts */ struct bma180_data { + struct regulator *vdd_supply; + struct regulator *vddio_supply; struct i2c_client *client; struct iio_trigger *trig; const struct bma180_part_info *part_info; @@ -736,6 +739,40 @@ static int bma180_probe(struct i2c_client *client, if (ret) return ret; + data->vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->vdd_supply)) { + if (PTR_ERR(data->vdd_supply) != -EPROBE_DEFER) + dev_err(dev, "Failed to get vdd regulator %d\n", + (int)PTR_ERR(data->vdd_supply)); + return PTR_ERR(data->vdd_supply); + } + data->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(data->vddio_supply)) { + if (PTR_ERR(data->vddio_supply) != -EPROBE_DEFER) + dev_err(dev, "Failed to get vddio regulator %d\n", + (int)PTR_ERR(data->vddio_supply)); + return PTR_ERR(data->vddio_supply); + } + /* Typical voltage 2.4V these are min and max */ + ret = regulator_set_voltage(data->vdd_supply, 1620000, 3600000); + if (ret) + return ret; + ret = regulator_set_voltage(data->vddio_supply, 1200000, 3600000); + if (ret) + return ret; + ret = regulator_enable(data->vdd_supply); + if (ret) { + dev_err(dev, "Failed to enable vdd regulator: %d\n", ret); + return ret; + } + ret = regulator_enable(data->vddio_supply); + if (ret) { + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret); + goto err_disable_vdd; + } + /* Wait to make sure we started up properly (3 ms at least) */ + usleep_range(3000, 5000); + ret = data->part_info->chip_config(data); if (ret < 0) goto err_chip_disable; @@ -798,6 +835,9 @@ err_trigger_free: iio_trigger_free(data->trig); err_chip_disable: data->part_info->chip_disable(data); + regulator_disable(data->vddio_supply); +err_disable_vdd: + regulator_disable(data->vdd_supply); return ret; } @@ -817,6 +857,8 @@ static int bma180_remove(struct i2c_client *client) mutex_lock(&data->mutex); data->part_info->chip_disable(data); mutex_unlock(&data->mutex); + regulator_disable(data->vddio_supply); + regulator_disable(data->vdd_supply); return 0; } -- cgit v1.2.3 From 9436abc40139503a7cea22a96437697d048f31c0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Dec 2019 22:38:18 +0100 Subject: iio: accel: bma180: Use explicit member assignment This uses the C99 explicit .member assignment for the variant data in struct bma180_part_info. This makes it easier to understand and add new variants. Cc: Peter Meerwald Cc: Oleksandr Kravchenko Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma180.c | 68 ++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index c2c15a4f9ee1..45dd096c0220 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -632,32 +632,52 @@ static const struct iio_chan_spec bma250_channels[] = { static const struct bma180_part_info bma180_part_info[] = { [BMA180] = { - bma180_channels, ARRAY_SIZE(bma180_channels), - bma180_scale_table, ARRAY_SIZE(bma180_scale_table), - bma180_bw_table, ARRAY_SIZE(bma180_bw_table), - BMA180_CTRL_REG0, BMA180_RESET_INT, - BMA180_CTRL_REG0, BMA180_SLEEP, - BMA180_BW_TCS, BMA180_BW, - BMA180_OFFSET_LSB1, BMA180_RANGE, - BMA180_TCO_Z, BMA180_MODE_CONFIG, BMA180_LOW_POWER, - BMA180_CTRL_REG3, BMA180_NEW_DATA_INT, - BMA180_RESET, - bma180_chip_config, - bma180_chip_disable, + .channels = bma180_channels, + .num_channels = ARRAY_SIZE(bma180_channels), + .scale_table = bma180_scale_table, + .num_scales = ARRAY_SIZE(bma180_scale_table), + .bw_table = bma180_bw_table, + .num_bw = ARRAY_SIZE(bma180_bw_table), + .int_reset_reg = BMA180_CTRL_REG0, + .int_reset_mask = BMA180_RESET_INT, + .sleep_reg = BMA180_CTRL_REG0, + .sleep_mask = BMA180_SLEEP, + .bw_reg = BMA180_BW_TCS, + .bw_mask = BMA180_BW, + .scale_reg = BMA180_OFFSET_LSB1, + .scale_mask = BMA180_RANGE, + .power_reg = BMA180_TCO_Z, + .power_mask = BMA180_MODE_CONFIG, + .lowpower_val = BMA180_LOW_POWER, + .int_enable_reg = BMA180_CTRL_REG3, + .int_enable_mask = BMA180_NEW_DATA_INT, + .softreset_reg = BMA180_RESET, + .chip_config = bma180_chip_config, + .chip_disable = bma180_chip_disable, }, [BMA250] = { - bma250_channels, ARRAY_SIZE(bma250_channels), - bma250_scale_table, ARRAY_SIZE(bma250_scale_table), - bma250_bw_table, ARRAY_SIZE(bma250_bw_table), - BMA250_INT_RESET_REG, BMA250_INT_RESET_MASK, - BMA250_POWER_REG, BMA250_SUSPEND_MASK, - BMA250_BW_REG, BMA250_BW_MASK, - BMA250_RANGE_REG, BMA250_RANGE_MASK, - BMA250_POWER_REG, BMA250_LOWPOWER_MASK, 1, - BMA250_INT_ENABLE_REG, BMA250_DATA_INTEN_MASK, - BMA250_RESET_REG, - bma250_chip_config, - bma250_chip_disable, + .channels = bma250_channels, + .num_channels = ARRAY_SIZE(bma250_channels), + .scale_table = bma250_scale_table, + .num_scales = ARRAY_SIZE(bma250_scale_table), + .bw_table = bma250_bw_table, + .num_bw = ARRAY_SIZE(bma250_bw_table), + .int_reset_reg = BMA250_INT_RESET_REG, + .int_reset_mask = BMA250_INT_RESET_MASK, + .sleep_reg = BMA250_POWER_REG, + .sleep_mask = BMA250_SUSPEND_MASK, + .bw_reg = BMA250_BW_REG, + .bw_mask = BMA250_BW_MASK, + .scale_reg = BMA250_RANGE_REG, + .scale_mask = BMA250_RANGE_MASK, + .power_reg = BMA250_POWER_REG, + .power_mask = BMA250_LOWPOWER_MASK, + .lowpower_val = 1, + .int_enable_reg = BMA250_INT_ENABLE_REG, + .int_enable_mask = BMA250_DATA_INTEN_MASK, + .softreset_reg = BMA250_RESET_REG, + .chip_config = bma250_chip_config, + .chip_disable = bma250_chip_disable, }, }; -- cgit v1.2.3 From 0c040d1ddce1d5a4d8de215117e8f0792cc62364 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 11 Dec 2019 12:41:47 +0200 Subject: iio: buffer-dmaengine: Add module information Make sure that the industrialio-buffer-dmaengine has proper license information so it can be build as a module and loaded without tainting the kernel. Signed-off-by: Lars-Peter Clausen Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index b7b5a934e9b2..e0b92f3dec0e 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -206,3 +207,7 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer) iio_buffer_put(buffer); } EXPORT_SYMBOL_GPL(iio_dmaengine_buffer_free); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("DMA buffer for the IIO framework"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ac78c6aa4a5dcb1a28eb212fd39d362371b36b06 Mon Sep 17 00:00:00 2001 From: Tomislav Denis Date: Wed, 11 Dec 2019 10:59:44 +0100 Subject: iio: pressure: Add driver for DLH pressure sensors All Sensors DLH is series of low voltage digital pressure sensors. Additionally to pressure value sensors deliver a temperature value. Sensors can be accessed over I2C and SPI, this driver supports only I2C access. Signed-off-by: Tomislav Denis Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/Kconfig | 12 ++ drivers/iio/pressure/Makefile | 1 + drivers/iio/pressure/dlhl60d.c | 375 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 388 insertions(+) create mode 100644 drivers/iio/pressure/dlhl60d.c (limited to 'drivers') diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index ba420e484787..9c2d9bf8f100 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -53,6 +53,18 @@ config IIO_CROS_EC_BARO To compile this driver as a module, choose M here: the module will be called cros_ec_baro. +config DLHL60D + tristate "All Sensors DLHL60D and DLHL60G low voltage digital pressure sensors" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the All Sensors DLH series + pressure sensors driver. + + To compile this driver as a module, choose M here: the module + will be called dlhl60d. + config DPS310 tristate "Infineon DPS310 pressure and temperature sensor" depends on I2C diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index d8f5ace1f25d..5a79192d8cb5 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_BMP280) += bmp280.o bmp280-objs := bmp280-core.o bmp280-regmap.o obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o +obj-$(CONFIG_DLHL60D) += dlhl60d.o obj-$(CONFIG_DPS310) += dps310.o obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c new file mode 100644 index 000000000000..b8c99e7bd6cf --- /dev/null +++ b/drivers/iio/pressure/dlhl60d.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * All Sensors DLH series low voltage digital pressure sensors + * + * Copyright (c) 2019 AVL DiTEST GmbH + * Tomislav Denis + * + * Datasheet: http://www.allsensors.com/cad/DS-0355_Rev_B.PDF + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Commands */ +#define DLH_START_SINGLE 0xAA + +/* Status bits */ +#define DLH_STATUS_OK 0x40 + +/* DLH data format */ +#define DLH_NUM_READ_BYTES 7 +#define DLH_NUM_DATA_BYTES 3 +#define DLH_NUM_PR_BITS 24 +#define DLH_NUM_TEMP_BITS 24 + +/* DLH timings */ +#define DLH_SINGLE_DUT_MS 5 + +enum dhl_ids { + dlhl60d, + dlhl60g, +}; + +struct dlh_info { + u8 osdig; /* digital offset factor */ + unsigned int fss; /* full scale span (inch H2O) */ +}; + +struct dlh_state { + struct i2c_client *client; + struct dlh_info info; + bool use_interrupt; + struct completion completion; + u8 rx_buf[DLH_NUM_READ_BYTES] ____cacheline_aligned; +}; + +static struct dlh_info dlh_info_tbl[] = { + [dlhl60d] = { + .osdig = 2, + .fss = 120, + }, + [dlhl60g] = { + .osdig = 10, + .fss = 60, + }, +}; + + +static int dlh_cmd_start_single(struct dlh_state *st) +{ + int ret; + + ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE); + if (ret) + dev_err(&st->client->dev, + "%s: I2C write byte failed\n", __func__); + + return ret; +} + +static int dlh_cmd_read_data(struct dlh_state *st) +{ + int ret; + + ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES); + if (ret < 0) { + dev_err(&st->client->dev, + "%s: I2C read block failed\n", __func__); + return ret; + } + + if (st->rx_buf[0] != DLH_STATUS_OK) { + dev_err(&st->client->dev, + "%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]); + return -EBUSY; + } + + return 0; +} + +static int dlh_start_capture_and_read(struct dlh_state *st) +{ + int ret; + + if (st->use_interrupt) + reinit_completion(&st->completion); + + ret = dlh_cmd_start_single(st); + if (ret) + return ret; + + if (st->use_interrupt) { + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(DLH_SINGLE_DUT_MS)); + if (!ret) { + dev_err(&st->client->dev, + "%s: conversion timed out\n", __func__); + return -ETIMEDOUT; + } + } else { + mdelay(DLH_SINGLE_DUT_MS); + } + + return dlh_cmd_read_data(st); +} + +static int dlh_read_direct(struct dlh_state *st, + unsigned int *pressure, unsigned int *temperature) +{ + int ret; + + ret = dlh_start_capture_and_read(st); + if (ret) + return ret; + + *pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8; + *temperature = get_unaligned_be32(&st->rx_buf[3]) & + GENMASK(DLH_NUM_TEMP_BITS - 1, 0); + + return 0; +} + +static int dlh_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *value, + int *value2, long mask) +{ + struct dlh_state *st = iio_priv(indio_dev); + unsigned int pressure, temperature; + int ret; + s64 tmp; + s32 rem; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = dlh_read_direct(st, &pressure, &temperature); + iio_device_release_direct_mode(indio_dev); + if (ret) + return ret; + + switch (channel->type) { + case IIO_PRESSURE: + *value = pressure; + return IIO_VAL_INT; + + case IIO_TEMP: + *value = temperature; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (channel->type) { + case IIO_PRESSURE: + tmp = div_s64(125LL * st->info.fss * 24909 * 100, + 1 << DLH_NUM_PR_BITS); + tmp = div_s64_rem(tmp, 1000000000LL, &rem); + *value = tmp; + *value2 = rem; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_TEMP: + *value = 125 * 1000; + *value2 = DLH_NUM_TEMP_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (channel->type) { + case IIO_PRESSURE: + *value = -125 * st->info.fss * 24909; + *value2 = 100 * st->info.osdig * 100000; + return IIO_VAL_FRACTIONAL; + + case IIO_TEMP: + *value = -40 * 1000; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static const struct iio_info dlh_info = { + .read_raw = dlh_read_raw, +}; + +static const struct iio_chan_spec dlh_channels[] = { + { + .type = IIO_PRESSURE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = DLH_NUM_PR_BITS, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, + }, { + .type = IIO_TEMP, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = DLH_NUM_TEMP_BITS, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, + } +}; + +static irqreturn_t dlh_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct dlh_state *st = iio_priv(indio_dev); + int ret; + unsigned int chn, i = 0; + __be32 tmp_buf[2]; + + ret = dlh_start_capture_and_read(st); + if (ret) + goto out; + + for_each_set_bit(chn, indio_dev->active_scan_mask, + indio_dev->masklength) { + memcpy(tmp_buf + i, + &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES, + DLH_NUM_DATA_BYTES); + i++; + } + + iio_push_to_buffers(indio_dev, tmp_buf); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t dlh_interrupt(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct dlh_state *st = iio_priv(indio_dev); + + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int dlh_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct dlh_state *st; + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) { + dev_err(&client->dev, + "adapter doesn't support required i2c functionality\n"); + return -EOPNOTSUPP; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (!indio_dev) { + dev_err(&client->dev, "failed to allocate iio device\n"); + return -ENOMEM; + } + + i2c_set_clientdata(client, indio_dev); + + st = iio_priv(indio_dev); + st->info = dlh_info_tbl[id->driver_data]; + st->client = client; + st->use_interrupt = false; + + indio_dev->name = id->name; + indio_dev->dev.parent = &client->dev; + indio_dev->dev.of_node = client->dev.of_node; + indio_dev->info = &dlh_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dlh_channels; + indio_dev->num_channels = ARRAY_SIZE(dlh_channels); + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + dlh_interrupt, NULL, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + id->name, indio_dev); + if (ret) { + dev_err(&client->dev, "failed to allocate threaded irq"); + return ret; + } + + st->use_interrupt = true; + init_completion(&st->completion); + } + + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, + NULL, &dlh_trigger_handler, NULL); + if (ret) { + dev_err(&client->dev, "failed to setup iio buffer\n"); + return ret; + } + + ret = devm_iio_device_register(&client->dev, indio_dev); + if (ret) + dev_err(&client->dev, "failed to register iio device\n"); + + return ret; +} + +static const struct of_device_id dlh_of_match[] = { + { .compatible = "asc,dlhl60d" }, + { .compatible = "asc,dlhl60g" }, + {} +}; +MODULE_DEVICE_TABLE(of, dlh_of_match); + +static const struct i2c_device_id dlh_id[] = { + { "dlhl60d", dlhl60d }, + { "dlhl60g", dlhl60g }, + {} +}; +MODULE_DEVICE_TABLE(i2c, dlh_id); + +static struct i2c_driver dlh_driver = { + .driver = { + .name = "dlhl60d", + .of_match_table = dlh_of_match, + }, + .probe = dlh_probe, + .id_table = dlh_id, +}; +module_i2c_driver(dlh_driver); + +MODULE_AUTHOR("Tomislav Denis "); +MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 4538c185680996d7328beac629dbdb7dd3f8f34e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 11 Dec 2019 13:56:15 +0200 Subject: iio: buffer-dmaengine: Report buffer length requirements The dmaengine buffer has some length alignment requirements that can differ from platform to platform. If the length alignment requirements are not met unexpected behavior like dropping of samples can occur. Currently these requirements are not reported and applications need to know the requirements of the platform by some out-of-band means. Add a new buffer attribute that reports the length alignment requirements called `length_align_bytes`. The reported length alignment is in bytes that means the buffer length alignment in sample sets depends on the number of enabled channels and the bytes per channel. Applications using this attribute to determine the buffer size requirements need to consider this. Signed-off-by: Lars-Peter Clausen Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index e0b92f3dec0e..744abd7e1269 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -126,6 +127,24 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = { .abort = iio_dmaengine_buffer_abort, }; +static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct dmaengine_buffer *dmaengine_buffer = + iio_buffer_to_dmaengine_buffer(indio_dev->buffer); + + return sprintf(buf, "%u\n", dmaengine_buffer->align); +} + +static IIO_DEVICE_ATTR(length_align_bytes, 0444, + iio_dmaengine_buffer_get_length_align, NULL, 0); + +static const struct attribute *iio_dmaengine_buffer_attrs[] = { + &iio_dev_attr_length_align_bytes.dev_attr.attr, + NULL, +}; + /** * iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine * @dev: Parent device for the buffer @@ -179,6 +198,8 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev, &iio_dmaengine_default_ops); + iio_buffer_set_attrs(&dmaengine_buffer->queue.buffer, + iio_dmaengine_buffer_attrs); dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops; -- cgit v1.2.3 From f6d4033d2a14b454680585d4ab974d163fcd7a47 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 11 Dec 2019 12:43:00 +0200 Subject: iio: buffer: rename 'read_first_n' callback to 'read' It is implied that 'read' will read the first n bytes and not e.g. bytes only from offsets within the buffer that are a prime number. This change is non-functional, mostly just a rename. A secondary intent with this patch is to make room later to add a write callback. Signed-off-by: Lars-Peter Clausen Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dma.c | 2 +- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 2 +- drivers/iio/buffer/kfifo_buf.c | 5 ++--- drivers/iio/iio_core.h | 8 ++++---- drivers/iio/industrialio-buffer.c | 10 +++++----- drivers/iio/industrialio-core.c | 2 +- 6 files changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 90cf6e586b10..a74bd9c0587c 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -476,7 +476,7 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue( * @n: Number of bytes to read * @user_buffer: Userspace buffer to copy the data to * - * Should be used as the read_first_n callback for iio_buffer_access_ops + * Should be used as the read callback for iio_buffer_access_ops * struct for DMA buffers. */ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 744abd7e1269..b129693af0fd 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -109,7 +109,7 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf) } static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = { - .read_first_n = iio_dma_buffer_read, + .read = iio_dma_buffer_read, .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum, .set_length = iio_dma_buffer_set_length, .request_update = iio_dma_buffer_request_update, diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index e78fc0834e6b..3150f8ab984b 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -98,8 +98,7 @@ static int iio_store_to_kfifo(struct iio_buffer *r, return 0; } -static int iio_read_first_n_kfifo(struct iio_buffer *r, - size_t n, char __user *buf) +static int iio_read_kfifo(struct iio_buffer *r, size_t n, char __user *buf) { int ret, copied; struct iio_kfifo *kf = iio_to_kfifo(r); @@ -141,7 +140,7 @@ static void iio_kfifo_buffer_release(struct iio_buffer *buffer) static const struct iio_buffer_access_funcs kfifo_access_funcs = { .store_to = &iio_store_to_kfifo, - .read_first_n = &iio_read_first_n_kfifo, + .read = &iio_read_kfifo, .data_available = iio_kfifo_buf_data_available, .request_update = &iio_request_update_kfifo, .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 159ea3f8c02b..fd9a5f1d5e51 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -42,14 +42,14 @@ struct poll_table_struct; __poll_t iio_buffer_poll(struct file *filp, struct poll_table_struct *wait); -ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, - size_t n, loff_t *f_ps); +ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, + size_t n, loff_t *f_ps); int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev); void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev); #define iio_buffer_poll_addr (&iio_buffer_poll) -#define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer) +#define iio_buffer_read_outer_addr (&iio_buffer_read_outer) void iio_disable_all_buffers(struct iio_dev *indio_dev); void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); @@ -57,7 +57,7 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); #else #define iio_buffer_poll_addr NULL -#define iio_buffer_read_first_n_outer_addr NULL +#define iio_buffer_read_outer_addr NULL static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index c193d64e5217..dbbf0cf4cac9 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -87,7 +87,7 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, } /** - * iio_buffer_read_first_n_outer() - chrdev read for buffer access + * iio_buffer_read_outer() - chrdev read for buffer access * @filp: File structure pointer for the char device * @buf: Destination buffer for iio buffer read * @n: First n bytes to read @@ -99,8 +99,8 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, * Return: negative values corresponding to error codes or ret != 0 * for ending the reading activity **/ -ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, - size_t n, loff_t *f_ps) +ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, + size_t n, loff_t *f_ps) { struct iio_dev *indio_dev = filp->private_data; struct iio_buffer *rb = indio_dev->buffer; @@ -112,7 +112,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, if (!indio_dev->info) return -ENODEV; - if (!rb || !rb->access->read_first_n) + if (!rb || !rb->access->read) return -EINVAL; datum_size = rb->bytes_per_datum; @@ -147,7 +147,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, continue; } - ret = rb->access->read_first_n(rb, n, buf); + ret = rb->access->read(rb, n, buf); if (ret == 0 && (filp->f_flags & O_NONBLOCK)) ret = -EAGAIN; } while (ret == 0); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index fa2c3b321bfd..2c107ac61de3 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1632,7 +1632,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } static const struct file_operations iio_buffer_fileops = { - .read = iio_buffer_read_first_n_outer_addr, + .read = iio_buffer_read_outer_addr, .release = iio_chrdev_release, .open = iio_chrdev_open, .poll = iio_buffer_poll_addr, -- cgit v1.2.3 From ab2ecec63320f8de5e0b15be6b427312e2e59f24 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Dec 2019 20:28:04 +0200 Subject: iio: pressure: bmp280: Drop ACPI support There is no evidence of officially registered ACPI IDs for these devices. Thus, revert ACPI support from the driver. All authors of the respective changes are being informed here: d5c94568cc1d ("iio: add bmp280 pressure and temperature driver") 6dba72eca7fb ("iio: pressure: bmp280: add support for BMP180") 14beaa8f5ab1 ("iio: pressure: bmp280: add humidity support") Above seems a cargo cult without paying attention to how ACPI IDs are being allocated. Cc: Vlad Dogaru Cc: Akinobu Mita Cc: Matt Ranostay Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-i2c.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 3109c8e2cc11..06f90853c141 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include -#include #include #include @@ -38,15 +37,6 @@ static int bmp280_i2c_probe(struct i2c_client *client, client->irq); } -static const struct acpi_device_id bmp280_acpi_i2c_match[] = { - {"BMP0280", BMP280_CHIP_ID }, - {"BMP0180", BMP180_CHIP_ID }, - {"BMP0085", BMP180_CHIP_ID }, - {"BME0280", BME280_CHIP_ID }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, bmp280_acpi_i2c_match); - #ifdef CONFIG_OF static const struct of_device_id bmp280_of_i2c_match[] = { { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, @@ -72,7 +62,6 @@ MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); static struct i2c_driver bmp280_i2c_driver = { .driver = { .name = "bmp280", - .acpi_match_table = ACPI_PTR(bmp280_acpi_i2c_match), .of_match_table = of_match_ptr(bmp280_of_i2c_match), .pm = &bmp280_dev_pm_ops, }, -- cgit v1.2.3 From ae9685992cd542b7881ff1d64a4bfd7ba509c3bb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Dec 2019 20:28:05 +0200 Subject: iio: pressure: bmp280: Allow device to be enumerated from ACPI There is no need to limit the driver use by OF/platform code. In this case we simple remove redundant OF parts from the code. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-i2c.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 06f90853c141..8b03ea15c0d0 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include -#include #include #include "bmp280.h" @@ -37,7 +36,6 @@ static int bmp280_i2c_probe(struct i2c_client *client, client->irq); } -#ifdef CONFIG_OF static const struct of_device_id bmp280_of_i2c_match[] = { { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, @@ -46,9 +44,6 @@ static const struct of_device_id bmp280_of_i2c_match[] = { { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); -#else -#define bmp280_of_i2c_match NULL -#endif static const struct i2c_device_id bmp280_i2c_id[] = { {"bmp280", BMP280_CHIP_ID }, @@ -62,7 +57,7 @@ MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); static struct i2c_driver bmp280_i2c_driver = { .driver = { .name = "bmp280", - .of_match_table = of_match_ptr(bmp280_of_i2c_match), + .of_match_table = bmp280_of_i2c_match, .pm = &bmp280_dev_pm_ops, }, .probe = bmp280_i2c_probe, -- cgit v1.2.3 From c1d1c4a62db5c4745b9e15cfee0daf0f774f73fc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Dec 2019 22:38:19 +0100 Subject: iio: accel: bma180: BMA254 support This adds support for the BMA254 variant of this accelerometer. The only difference for the simple IIO driver is that values are 12 bit and the temperature offset differs by 1 degree. Whilst wildcards in naming are normally frowned upon: The cases where I have labeled variables "25x" is where the models are identical, so as to make things easier for people that want to add support for BMA253 and BMA255. Cc: Peter Meerwald Cc: Oleksandr Kravchenko Cc: devicetree@vger.kernel.org Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 4 +- drivers/iio/accel/bma180.c | 108 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 95 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 670e60568033..5d91a6dda894 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -89,13 +89,13 @@ config ADXL372_I2C module will be called adxl372_i2c. config BMA180 - tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver" + tristate "Bosch BMA180/BMA25x 3-Axis Accelerometer Driver" depends on I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER help Say Y here if you want to build a driver for the Bosch BMA180 or - BMA250 triaxial acceleration sensor. + BMA25x triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called bma180. diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 45dd096c0220..fcd91d5f05fd 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -9,6 +9,7 @@ * SPI is not supported by driver * BMA180: 7-bit I2C slave address 0x40 or 0x41 * BMA250: 7-bit I2C slave address 0x18 or 0x19 + * BMA254: 7-bit I2C slave address 0x18 or 0x19 */ #include @@ -34,17 +35,20 @@ enum chip_ids { BMA180, BMA250, + BMA254, }; struct bma180_data; struct bma180_part_info { + u8 chip_id; const struct iio_chan_spec *channels; unsigned int num_channels; const int *scale_table; unsigned int num_scales; const int *bw_table; unsigned int num_bw; + int center_temp; u8 int_reset_reg, int_reset_mask; u8 sleep_reg, sleep_mask; @@ -52,6 +56,7 @@ struct bma180_part_info { u8 scale_reg, scale_mask; u8 power_reg, power_mask, lowpower_val; u8 int_enable_reg, int_enable_mask; + u8 int_map_reg, int_enable_dataready_int1_mask; u8 softreset_reg; int (*chip_config)(struct bma180_data *data); @@ -90,6 +95,8 @@ struct bma180_part_info { #define BMA180_RESET_VAL 0xb6 #define BMA180_ID_REG_VAL 0x03 +#define BMA250_ID_REG_VAL 0x03 +#define BMA254_ID_REG_VAL 0xfa /* 250 decimal */ /* Chip power modes */ #define BMA180_LOW_POWER 0x03 @@ -110,6 +117,23 @@ struct bma180_part_info { #define BMA250_INT1_DATA_MASK BIT(0) #define BMA250_INT_RESET_MASK BIT(7) /* Reset pending interrupts */ +#define BMA254_RANGE_REG 0x0f +#define BMA254_BW_REG 0x10 +#define BMA254_POWER_REG 0x11 +#define BMA254_RESET_REG 0x14 +#define BMA254_INT_ENABLE_REG 0x17 +#define BMA254_INT_MAP_REG 0x1a +#define BMA254_INT_RESET_REG 0x21 + +#define BMA254_RANGE_MASK GENMASK(3, 0) /* Range of accel values */ +#define BMA254_BW_MASK GENMASK(4, 0) /* Accel bandwidth */ +#define BMA254_SUSPEND_MASK BIT(7) /* chip will sleep */ +#define BMA254_LOWPOWER_MASK BIT(6) +#define BMA254_DATA_INTEN_MASK BIT(4) +#define BMA254_INT2_DATA_MASK BIT(7) +#define BMA254_INT1_DATA_MASK BIT(0) +#define BMA254_INT_RESET_MASK BIT(7) /* Reset pending interrupts */ + struct bma180_data { struct regulator *vdd_supply; struct regulator *vddio_supply; @@ -135,8 +159,8 @@ enum bma180_chan { static int bma180_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */ static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 }; -static int bma250_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */ -static int bma250_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0, +static int bma25x_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */ +static int bma25x_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0, 0, 0, 306458 }; static int bma180_get_data_reg(struct bma180_data *data, enum bma180_chan chan) @@ -310,8 +334,11 @@ static int bma180_chip_init(struct bma180_data *data) if (ret < 0) return ret; - if (ret != BMA180_ID_REG_VAL) + if (ret != data->part_info->chip_id) { + dev_err(&data->client->dev, "wrong chip ID %d expected %d\n", + ret, data->part_info->chip_id); return -ENODEV; + } ret = bma180_soft_reset(data); if (ret) @@ -358,7 +385,7 @@ err: return ret; } -static int bma250_chip_config(struct bma180_data *data) +static int bma25x_chip_config(struct bma180_data *data) { int ret = bma180_chip_init(data); @@ -370,8 +397,12 @@ static int bma250_chip_config(struct bma180_data *data) ret = bma180_set_scale(data, 38344); /* 2 G */ if (ret) goto err; - ret = bma180_set_bits(data, BMA250_INT_MAP_REG, - BMA250_INT1_DATA_MASK, 1); + /* + * This enables dataready interrupt on the INT1 pin + * FIXME: support using the INT2 pin + */ + ret = bma180_set_bits(data, data->part_info->int_map_reg, + data->part_info->int_enable_dataready_int1_mask, 1); if (ret) goto err; @@ -397,7 +428,7 @@ err: dev_err(&data->client->dev, "failed to disable the chip\n"); } -static void bma250_chip_disable(struct bma180_data *data) +static void bma25x_chip_disable(struct bma180_data *data) { if (bma180_set_new_data_intr_state(data, false)) goto err; @@ -500,7 +531,7 @@ static int bma180_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_OFFSET: - *val = 48; /* 0 LSB @ 24 degree C */ + *val = data->part_info->center_temp; return IIO_VAL_INT; default: return -EINVAL; @@ -630,14 +661,24 @@ static const struct iio_chan_spec bma250_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; +static const struct iio_chan_spec bma254_channels[] = { + BMA180_ACC_CHANNEL(X, 12), + BMA180_ACC_CHANNEL(Y, 12), + BMA180_ACC_CHANNEL(Z, 12), + BMA180_TEMP_CHANNEL, + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + static const struct bma180_part_info bma180_part_info[] = { [BMA180] = { + .chip_id = BMA180_ID_REG_VAL, .channels = bma180_channels, .num_channels = ARRAY_SIZE(bma180_channels), .scale_table = bma180_scale_table, .num_scales = ARRAY_SIZE(bma180_scale_table), .bw_table = bma180_bw_table, .num_bw = ARRAY_SIZE(bma180_bw_table), + .center_temp = 48, /* 0 LSB @ 24 degree C */ .int_reset_reg = BMA180_CTRL_REG0, .int_reset_mask = BMA180_RESET_INT, .sleep_reg = BMA180_CTRL_REG0, @@ -656,12 +697,14 @@ static const struct bma180_part_info bma180_part_info[] = { .chip_disable = bma180_chip_disable, }, [BMA250] = { + .chip_id = BMA250_ID_REG_VAL, .channels = bma250_channels, .num_channels = ARRAY_SIZE(bma250_channels), - .scale_table = bma250_scale_table, - .num_scales = ARRAY_SIZE(bma250_scale_table), - .bw_table = bma250_bw_table, - .num_bw = ARRAY_SIZE(bma250_bw_table), + .scale_table = bma25x_scale_table, + .num_scales = ARRAY_SIZE(bma25x_scale_table), + .bw_table = bma25x_bw_table, + .num_bw = ARRAY_SIZE(bma25x_bw_table), + .center_temp = 48, /* 0 LSB @ 24 degree C */ .int_reset_reg = BMA250_INT_RESET_REG, .int_reset_mask = BMA250_INT_RESET_MASK, .sleep_reg = BMA250_POWER_REG, @@ -675,9 +718,39 @@ static const struct bma180_part_info bma180_part_info[] = { .lowpower_val = 1, .int_enable_reg = BMA250_INT_ENABLE_REG, .int_enable_mask = BMA250_DATA_INTEN_MASK, + .int_map_reg = BMA250_INT_MAP_REG, + .int_enable_dataready_int1_mask = BMA250_INT1_DATA_MASK, .softreset_reg = BMA250_RESET_REG, - .chip_config = bma250_chip_config, - .chip_disable = bma250_chip_disable, + .chip_config = bma25x_chip_config, + .chip_disable = bma25x_chip_disable, + }, + [BMA254] = { + .chip_id = BMA254_ID_REG_VAL, + .channels = bma254_channels, + .num_channels = ARRAY_SIZE(bma254_channels), + .scale_table = bma25x_scale_table, + .num_scales = ARRAY_SIZE(bma25x_scale_table), + .bw_table = bma25x_bw_table, + .num_bw = ARRAY_SIZE(bma25x_bw_table), + .center_temp = 46, /* 0 LSB @ 23 degree C */ + .int_reset_reg = BMA254_INT_RESET_REG, + .int_reset_mask = BMA254_INT_RESET_MASK, + .sleep_reg = BMA254_POWER_REG, + .sleep_mask = BMA254_SUSPEND_MASK, + .bw_reg = BMA254_BW_REG, + .bw_mask = BMA254_BW_MASK, + .scale_reg = BMA254_RANGE_REG, + .scale_mask = BMA254_RANGE_MASK, + .power_reg = BMA254_POWER_REG, + .power_mask = BMA254_LOWPOWER_MASK, + .lowpower_val = 1, + .int_enable_reg = BMA254_INT_ENABLE_REG, + .int_enable_mask = BMA254_DATA_INTEN_MASK, + .int_map_reg = BMA254_INT_MAP_REG, + .int_enable_dataready_int1_mask = BMA254_INT1_DATA_MASK, + .softreset_reg = BMA254_RESET_REG, + .chip_config = bma25x_chip_config, + .chip_disable = bma25x_chip_disable, }, }; @@ -919,6 +992,7 @@ static SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); static const struct i2c_device_id bma180_ids[] = { { "bma180", BMA180 }, { "bma250", BMA250 }, + { "bma254", BMA254 }, { } }; @@ -933,6 +1007,10 @@ static const struct of_device_id bma180_of_match[] = { .compatible = "bosch,bma250", .data = (void *)BMA250 }, + { + .compatible = "bosch,bma254", + .data = (void *)BMA254 + }, { } }; MODULE_DEVICE_TABLE(of, bma180_of_match); @@ -952,5 +1030,5 @@ module_i2c_driver(bma180_driver); MODULE_AUTHOR("Kravchenko Oleksandr "); MODULE_AUTHOR("Texas Instruments, Inc."); -MODULE_DESCRIPTION("Bosch BMA180/BMA250 triaxial acceleration sensor"); +MODULE_DESCRIPTION("Bosch BMA180/BMA25x triaxial acceleration sensor"); MODULE_LICENSE("GPL"); -- cgit v1.2.3