diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-27 21:07:50 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-27 21:07:50 +0200 |
commit | cec24b8b6bb841a19b5c5555b600a511a8988100 (patch) | |
tree | b12115ba8e6e6929cea0658ee3c9dae9aad8a82d /drivers/iio | |
parent | Merge tag 'driver-core-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/... (diff) | |
parent | mcb-lpc: Reallocate memory region to avoid memory overlapping (diff) | |
download | linux-cec24b8b6bb841a19b5c5555b600a511a8988100.tar.xz linux-cec24b8b6bb841a19b5c5555b600a511a8988100.zip |
Merge tag 'char-misc-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc drivers updates from Greg KH:
"Here is the "big" set of char/misc and other driver subsystems for
6.4-rc1.
It's pretty big, but due to the removal of pcmcia drivers, almost
breaks even for number of lines added vs. removed, a nice change.
Included in here are:
- removal of unused PCMCIA drivers (finally!)
- Interconnect driver updates and additions
- Lots of IIO driver updates and additions
- MHI driver updates
- Coresight driver updates
- NVMEM driver updates, which required some OF updates
- W1 driver updates and a new maintainer to manage the subsystem
- FPGA driver updates
- New driver subsystem, CDX, for AMD systems
- lots of other small driver updates and additions
All of these have been in linux-next for a while with no reported
issues"
* tag 'char-misc-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (196 commits)
mcb-lpc: Reallocate memory region to avoid memory overlapping
mcb-pci: Reallocate memory region to avoid memory overlapping
mcb: Return actual parsed size when reading chameleon table
kernel/configs: Drop Android config fragments
virt: acrn: Replace obsolete memalign() with posix_memalign()
spmi: Add a check for remove callback when removing a SPMI driver
spmi: fix W=1 kernel-doc warnings
spmi: mtk-pmif: Drop of_match_ptr for ID table
spmi: pmic-arb: Convert to platform remove callback returning void
spmi: mtk-pmif: Convert to platform remove callback returning void
spmi: hisi-spmi-controller: Convert to platform remove callback returning void
w1: gpio: remove unnecessary ENOMEM messages
w1: omap-hdq: remove unnecessary ENOMEM messages
w1: omap-hdq: add SPDX tag
w1: omap-hdq: allow compile testing
w1: matrox: remove unnecessary ENOMEM messages
w1: matrox: use inline over __inline__
w1: matrox: switch from asm to linux header
w1: ds2482: do not use assignment in if condition
w1: ds2482: drop unnecessary header
...
Diffstat (limited to 'drivers/iio')
69 files changed, 5302 insertions, 816 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index b190846c3dc2..52eb46ef84c1 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -30,6 +30,9 @@ config IIO_CONFIGFS (e.g. software triggers). For more info see Documentation/iio/iio_configfs.rst. +config IIO_GTS_HELPER + tristate + config IIO_TRIGGER bool "Enable triggered sampling support" help diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 3be08cdadd7e..9622347a1c1b 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o +obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 623f37cbaf50..a68b845f5b4f 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -1688,7 +1688,7 @@ static irqreturn_t bma400_interrupt(int irq, void *private) if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) { mutex_unlock(&data->mutex); - iio_trigger_poll_chained(data->trig); + iio_trigger_poll_nested(data->trig); return IRQ_HANDLED; } diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 1c3a72380fb8..f98393d74666 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -162,7 +162,6 @@ struct kx022a_data { int inc_reg; int ien_reg; - unsigned int g_range; unsigned int state; unsigned int odr_ns; @@ -900,7 +899,7 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) mutex_lock(&data->mutex); if (data->trigger_enabled) { - iio_trigger_poll_chained(data->trig); + iio_trigger_poll_nested(data->trig); ret = IRQ_HANDLED; } diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index f97fb68e3a71..ea14e3aaa30a 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1067,7 +1067,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) return IRQ_NONE; if (src & MMA8452_INT_DRDY) { - iio_trigger_poll_chained(indio_dev->trig); + iio_trigger_poll_nested(indio_dev->trig); ret = IRQ_HANDLED; } diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index af94d3adf6d8..6690fa37da8f 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -951,7 +951,7 @@ static irqreturn_t msa311_irq_thread(int irq, void *p) } if (new_data_int_enabled) - iio_trigger_poll_chained(msa311->new_data_trig); + iio_trigger_poll_nested(msa311->new_data_trig); return IRQ_HANDLED; } diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 56ed0c776d4a..e7525615712b 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -39,6 +39,7 @@ #define LIS302DL_ACCEL_DEV_NAME "lis302dl" #define LSM303C_ACCEL_DEV_NAME "lsm303c_accel" #define SC7A20_ACCEL_DEV_NAME "sc7a20" +#define IIS328DQ_ACCEL_DEV_NAME "iis328dq" #ifdef CONFIG_IIO_BUFFER diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 6b8562f684d5..5f7d81b44b1d 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -517,6 +517,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = H3LIS331DL_ACCEL_DEV_NAME, + [1] = IIS328DQ_ACCEL_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_accel_12bit_channels, .odr = { diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 3f02fd5d5946..fb9e2d6f4210 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -119,6 +119,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "silan,sc7a20", .data = SC7A20_ACCEL_DEV_NAME, }, + { + .compatible = "st,iis328dq", + .data = IIS328DQ_ACCEL_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -157,6 +161,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS302DL_ACCEL_DEV_NAME }, { LSM303C_ACCEL_DEV_NAME }, { SC7A20_ACCEL_DEV_NAME }, + { IIS328DQ_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 5740dc1820bd..f72a24f45322 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -100,6 +100,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lsm303c-accel", .data = LSM303C_ACCEL_DEV_NAME, }, + { + .compatible = "st,iis328dq", + .data = IIS328DQ_ACCEL_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -157,6 +161,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS3DE_ACCEL_DEV_NAME }, { LIS302DL_ACCEL_DEV_NAME }, { LSM303C_ACCEL_DEV_NAME }, + { IIS328DQ_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 45af2302be53..eb2b09ef5d5b 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1229,6 +1229,16 @@ config TI_ADS7924 This driver can also be built as a module. If so, the module will be called ti-ads7924. +config TI_ADS1100 + tristate "Texas Instruments ADS1100 and ADS1000 ADC" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADS1100 and + ADS1000 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads1100. + config TI_ADS7950 tristate "Texas Instruments ADS7950 ADC driver" depends on SPI && GPIOLIB diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 36c18177322a..e07e4a3e6237 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -108,6 +108,7 @@ obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o +obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index dd6b603f65ea..1928d9ae5bcf 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -477,7 +477,7 @@ static irqreturn_t ad7606_interrupt(int irq, void *dev_id) if (iio_buffer_enabled(indio_dev)) { gpiod_set_value(st->gpio_convst, 0); - iio_trigger_poll_chained(st->trig); + iio_trigger_poll_nested(st->trig); } else { complete(&st->completion); } diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 7258912fe17b..df67b63ccf69 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1194,7 +1194,7 @@ static void at91_dma_buffer_done(void *data) { struct iio_dev *indio_dev = data; - iio_trigger_poll_chained(indio_dev->trig); + iio_trigger_poll_nested(indio_dev->trig); } static int at91_adc_dma_start(struct iio_dev *indio_dev) @@ -2400,12 +2400,8 @@ static int at91_adc_probe(struct platform_device *pdev) st->dma_st.phys_addr = res->start; st->irq = platform_get_irq(pdev, 0); - if (st->irq <= 0) { - if (!st->irq) - st->irq = -ENXIO; - + if (st->irq < 0) return st->irq; - } st->per_clk = devm_clk_get(&pdev->dev, "adc_clk"); if (IS_ERR(st->per_clk)) diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 53bf7d4899d2..75bda94dbce1 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -5,6 +5,7 @@ * Quentin Schulz <quentin.schulz@free-electrons.com> */ +#include <linux/bitfield.h> #include <linux/completion.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -22,20 +23,20 @@ #include <linux/mfd/axp20x.h> #define AXP20X_ADC_EN1_MASK GENMASK(7, 0) - #define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7)) + #define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0)) #define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0) #define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1) -#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0)) -#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1) #define AXP20X_ADC_RATE_MASK GENMASK(7, 6) -#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4) -#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK) #define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK) + #define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK) + +#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4) +#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK) #define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x) #define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK) #define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x)) @@ -234,7 +235,7 @@ static int axp20x_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct axp20x_adc_iio *info = iio_priv(indio_dev); - int size = 12; + int ret, size; /* * N.B.: Unlike the Chinese datasheets tell, the charging current is @@ -246,10 +247,11 @@ static int axp20x_adc_raw(struct iio_dev *indio_dev, else size = 12; - *val = axp20x_read_variable_width(info->regmap, chan->address, size); - if (*val < 0) - return *val; + ret = axp20x_read_variable_width(info->regmap, chan->address, size); + if (ret < 0) + return ret; + *val = ret; return IIO_VAL_INT; } @@ -257,11 +259,13 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret; - *val = axp20x_read_variable_width(info->regmap, chan->address, 12); - if (*val < 0) - return *val; + ret = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (ret < 0) + return ret; + *val = ret; return IIO_VAL_INT; } @@ -269,11 +273,13 @@ static int axp813_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret; - *val = axp20x_read_variable_width(info->regmap, chan->address, 12); - if (*val < 0) - return *val; + ret = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (ret < 0) + return ret; + *val = ret; return IIO_VAL_INT; } @@ -443,27 +449,27 @@ static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel, int *val) { struct axp20x_adc_iio *info = iio_priv(indio_dev); + unsigned int regval; int ret; - ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, val); + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, ®val); if (ret < 0) return ret; switch (channel) { case AXP20X_GPIO0_V: - *val &= AXP20X_GPIO10_IN_RANGE_GPIO0; + regval = FIELD_GET(AXP20X_GPIO10_IN_RANGE_GPIO0, regval); break; case AXP20X_GPIO1_V: - *val &= AXP20X_GPIO10_IN_RANGE_GPIO1; + regval = FIELD_GET(AXP20X_GPIO10_IN_RANGE_GPIO1, regval); break; default: return -EINVAL; } - *val = *val ? 700000 : 0; - + *val = regval ? 700000 : 0; return IIO_VAL_INT; } @@ -548,7 +554,7 @@ static int axp20x_write_raw(struct iio_dev *indio_dev, long mask) { struct axp20x_adc_iio *info = iio_priv(indio_dev); - unsigned int reg, regval; + unsigned int regmask, regval; /* * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets @@ -560,25 +566,22 @@ static int axp20x_write_raw(struct iio_dev *indio_dev, if (val != 0 && val != 700000) return -EINVAL; - val = val ? 1 : 0; - switch (chan->channel) { case AXP20X_GPIO0_V: - reg = AXP20X_GPIO10_IN_RANGE_GPIO0; - regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(val); + regmask = AXP20X_GPIO10_IN_RANGE_GPIO0; + regval = FIELD_PREP(AXP20X_GPIO10_IN_RANGE_GPIO0, !!val); break; case AXP20X_GPIO1_V: - reg = AXP20X_GPIO10_IN_RANGE_GPIO1; - regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(val); + regmask = AXP20X_GPIO10_IN_RANGE_GPIO1; + regval = FIELD_PREP(AXP20X_GPIO10_IN_RANGE_GPIO1, !!val); break; default: return -EINVAL; } - return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, reg, - regval); + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, regmask, regval); } static const struct iio_info axp20x_adc_iio_info = { @@ -620,9 +623,9 @@ struct axp_data { int num_channels; struct iio_chan_spec const *channels; unsigned long adc_en1_mask; + unsigned long adc_en2_mask; int (*adc_rate)(struct axp20x_adc_iio *info, int rate); - bool adc_en2; struct iio_map *maps; }; @@ -631,8 +634,8 @@ static const struct axp_data axp20x_data = { .num_channels = ARRAY_SIZE(axp20x_adc_channels), .channels = axp20x_adc_channels, .adc_en1_mask = AXP20X_ADC_EN1_MASK, + .adc_en2_mask = AXP20X_ADC_EN2_MASK, .adc_rate = axp20x_adc_rate, - .adc_en2 = true, .maps = axp20x_maps, }; @@ -642,7 +645,6 @@ static const struct axp_data axp22x_data = { .channels = axp22x_adc_channels, .adc_en1_mask = AXP22X_ADC_EN1_MASK, .adc_rate = axp22x_adc_rate, - .adc_en2 = false, .maps = axp22x_maps, }; @@ -652,7 +654,6 @@ static const struct axp_data axp813_data = { .channels = axp813_adc_channels, .adc_en1_mask = AXP22X_ADC_EN1_MASK, .adc_rate = axp813_adc_rate, - .adc_en2 = false, .maps = axp22x_maps, }; @@ -710,10 +711,10 @@ static int axp20x_probe(struct platform_device *pdev) /* Enable the ADCs on IP */ regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask); - if (info->data->adc_en2) - /* Enable GPIO0/1 and internal temperature ADCs */ + if (info->data->adc_en2_mask) regmap_update_bits(info->regmap, AXP20X_ADC_EN2, - AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK); + info->data->adc_en2_mask, + info->data->adc_en2_mask); /* Configure ADCs rate */ info->data->adc_rate(info, 100); @@ -738,7 +739,7 @@ fail_register: fail_map: regmap_write(info->regmap, AXP20X_ADC_EN1, 0); - if (info->data->adc_en2) + if (info->data->adc_en2_mask) regmap_write(info->regmap, AXP20X_ADC_EN2, 0); return ret; @@ -754,7 +755,7 @@ static int axp20x_remove(struct platform_device *pdev) regmap_write(info->regmap, AXP20X_ADC_EN1, 0); - if (info->data->adc_en2) + if (info->data->adc_en2_mask) regmap_write(info->regmap, AXP20X_ADC_EN2, 0); return 0; diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index f6895bc9fc4b..6af829349b4e 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -682,7 +682,7 @@ static irqreturn_t max11410_interrupt(int irq, void *dev_id) struct max11410_state *st = iio_priv(indio_dev); if (iio_buffer_enabled(indio_dev)) - iio_trigger_poll_chained(st->trig); + iio_trigger_poll_nested(st->trig); else complete(&st->completion); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 85b6826cc10c..18937a262af6 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -957,14 +957,18 @@ err_lock: return ret; } -static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) +static void meson_sar_adc_hw_disable(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); int ret; + /* + * If taking the lock fails we have to assume that BL30 is broken. The + * best we can do then is to release the resources anyhow. + */ ret = meson_sar_adc_lock(indio_dev); if (ret) - return ret; + dev_err(indio_dev->dev.parent, "Failed to lock ADC (%pE)\n", ERR_PTR(ret)); clk_disable_unprepare(priv->adc_clk); @@ -977,9 +981,8 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) regulator_disable(priv->vref); - meson_sar_adc_unlock(indio_dev); - - return 0; + if (!ret) + meson_sar_adc_unlock(indio_dev); } static irqreturn_t meson_sar_adc_irq(int irq, void *data) @@ -1283,14 +1286,18 @@ static int meson_sar_adc_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); - return meson_sar_adc_hw_disable(indio_dev); + meson_sar_adc_hw_disable(indio_dev); + + return 0; } static int meson_sar_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); - return meson_sar_adc_hw_disable(indio_dev); + meson_sar_adc_hw_disable(indio_dev); + + return 0; } static int meson_sar_adc_resume(struct device *dev) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 849a697a467e..c1c439215aeb 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -20,6 +20,7 @@ #include <linux/completion.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/machine.h> #include <linux/iio/driver.h> @@ -76,6 +77,17 @@ static struct palmas_gpadc_info palmas_gpadc_info[] = { PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true), }; +struct palmas_adc_event { + bool enabled; + int channel; + enum iio_event_direction direction; +}; + +struct palmas_gpadc_thresholds { + int high; + int low; +}; + /* * struct palmas_gpadc - the palmas_gpadc structure * @ch0_current: channel 0 current source setting @@ -111,14 +123,33 @@ struct palmas_gpadc { int irq_auto_1; struct palmas_gpadc_info *adc_info; struct completion conv_completion; - struct palmas_adc_wakeup_property wakeup1_data; - struct palmas_adc_wakeup_property wakeup2_data; - bool wakeup1_enable; - bool wakeup2_enable; + struct palmas_adc_event event0; + struct palmas_adc_event event1; + struct palmas_gpadc_thresholds thresholds[PALMAS_ADC_CH_MAX]; int auto_conversion_period; struct mutex lock; }; +static struct palmas_adc_event *palmas_gpadc_get_event(struct palmas_gpadc *adc, + int adc_chan, + enum iio_event_direction dir) +{ + if (adc_chan == adc->event0.channel && dir == adc->event0.direction) + return &adc->event0; + + if (adc_chan == adc->event1.channel && dir == adc->event1.direction) + return &adc->event1; + + return NULL; +} + +static bool palmas_gpadc_channel_is_freerunning(struct palmas_gpadc *adc, + int adc_chan) +{ + return palmas_gpadc_get_event(adc, adc_chan, IIO_EV_DIR_RISING) || + palmas_gpadc_get_event(adc, adc_chan, IIO_EV_DIR_FALLING); +} + /* * GPADC lock issue in AUTO mode. * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO @@ -188,11 +219,24 @@ static irqreturn_t palmas_gpadc_irq(int irq, void *data) static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data) { - struct palmas_gpadc *adc = data; + struct iio_dev *indio_dev = data; + struct palmas_gpadc *adc = iio_priv(indio_dev); + struct palmas_adc_event *ev; dev_dbg(adc->dev, "Threshold interrupt %d occurs\n", irq); palmas_disable_auto_conversion(adc); + ev = (irq == adc->irq_auto_0) ? &adc->event0 : &adc->event1; + if (ev->channel != -1) { + enum iio_event_direction dir; + u64 code; + + dir = ev->direction; + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, ev->channel, + IIO_EV_TYPE_THRESH, dir); + iio_push_event(indio_dev, code, iio_get_time_ns(indio_dev)); + } + return IRQ_HANDLED; } @@ -280,6 +324,9 @@ static int palmas_gpadc_read_prepare(struct palmas_gpadc *adc, int adc_chan) { int ret; + if (palmas_gpadc_channel_is_freerunning(adc, adc_chan)) + return 0; /* ADC already running */ + ret = palmas_gpadc_enable(adc, adc_chan, true); if (ret < 0) return ret; @@ -339,28 +386,43 @@ static int palmas_gpadc_start_conversion(struct palmas_gpadc *adc, int adc_chan) unsigned int val; int ret; - init_completion(&adc->conv_completion); - ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, - PALMAS_GPADC_SW_SELECT, - PALMAS_GPADC_SW_SELECT_SW_START_CONV0, - PALMAS_GPADC_SW_SELECT_SW_START_CONV0); - if (ret < 0) { - dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret); - return ret; - } + if (palmas_gpadc_channel_is_freerunning(adc, adc_chan)) { + int event = (adc_chan == adc->event0.channel) ? 0 : 1; + unsigned int reg = (event == 0) ? + PALMAS_GPADC_AUTO_CONV0_LSB : + PALMAS_GPADC_AUTO_CONV1_LSB; - ret = wait_for_completion_timeout(&adc->conv_completion, - PALMAS_ADC_CONVERSION_TIMEOUT); - if (ret == 0) { - dev_err(adc->dev, "conversion not completed\n"); - return -ETIMEDOUT; - } + ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, + reg, &val, 2); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CONV%x_LSB read failed: %d\n", + event, ret); + return ret; + } + } else { + init_completion(&adc->conv_completion); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0); + if (ret < 0) { + dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret); + return ret; + } - ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, - PALMAS_GPADC_SW_CONV0_LSB, &val, 2); - if (ret < 0) { - dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret); - return ret; + ret = wait_for_completion_timeout(&adc->conv_completion, + PALMAS_ADC_CONVERSION_TIMEOUT); + if (ret == 0) { + dev_err(adc->dev, "conversion not completed\n"); + return -ETIMEDOUT; + } + + ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_CONV0_LSB, &val, 2); + if (ret < 0) { + dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret); + return ret; + } } ret = val & 0xFFF; @@ -386,6 +448,98 @@ static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc, return val; } +/* + * The high and low threshold values are calculated based on the advice given + * in TI Application Report SLIA087A, "Guide to Using the GPADC in PS65903x, + * TPS65917-Q1, TPS65919-Q1, and TPS65916 Devices". This document recommend + * taking ADC tolerances into account and is based on the device integral non- + * linearity (INL), offset error and gain error: + * + * raw high threshold = (ideal threshold + INL) * gain error + offset error + * + * The gain error include both gain error, as specified in the datasheet, and + * the gain error drift. These paramenters vary depending on device and whether + * the the channel is calibrated (trimmed) or not. + */ +static int palmas_gpadc_threshold_with_tolerance(int val, const int INL, + const int gain_error, + const int offset_error) +{ + val = ((val + INL) * (1000 + gain_error)) / 1000 + offset_error; + + return clamp(val, 0, 0xFFF); +} + +/* + * The values below are taken from the datasheet of TWL6035, TWL6037. + * todo: get max INL, gain error, and offset error from OF. + */ +static int palmas_gpadc_get_high_threshold_raw(struct palmas_gpadc *adc, + struct palmas_adc_event *ev) +{ + const int adc_chan = ev->channel; + int val = adc->thresholds[adc_chan].high; + /* integral nonlinearity, measured in LSB */ + const int max_INL = 2; + /* measured in LSB */ + int max_offset_error; + /* 0.2% when calibrated */ + int max_gain_error = 2; + + val = (val * 1000) / adc->adc_info[adc_chan].gain; + + if (adc->adc_info[adc_chan].is_uncalibrated) { + /* 2% worse */ + max_gain_error += 20; + max_offset_error = 36; + } else { + val = (val * adc->adc_info[adc_chan].gain_error + + adc->adc_info[adc_chan].offset) / + 1000; + max_offset_error = 2; + } + + return palmas_gpadc_threshold_with_tolerance(val, + max_INL, + max_gain_error, + max_offset_error); +} + +/* + * The values below are taken from the datasheet of TWL6035, TWL6037. + * todo: get min INL, gain error, and offset error from OF. + */ +static int palmas_gpadc_get_low_threshold_raw(struct palmas_gpadc *adc, + struct palmas_adc_event *ev) +{ + const int adc_chan = ev->channel; + int val = adc->thresholds[adc_chan].low; + /* integral nonlinearity, measured in LSB */ + const int min_INL = -2; + /* measured in LSB */ + int min_offset_error; + /* -0.6% when calibrated */ + int min_gain_error = -6; + + val = (val * 1000) / adc->adc_info[adc_chan].gain; + + if (adc->adc_info[adc_chan].is_uncalibrated) { + /* 2% worse */ + min_gain_error -= 20; + min_offset_error = -36; + } else { + val = (val * adc->adc_info[adc_chan].gain_error - + adc->adc_info[adc_chan].offset) / + 1000; + min_offset_error = -2; + } + + return palmas_gpadc_threshold_with_tolerance(val, + min_INL, + min_gain_error, + min_offset_error); +} + static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { @@ -432,8 +586,217 @@ out: return ret; } +static int palmas_gpadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret = 0; + + if (adc_chan > PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + if (palmas_gpadc_get_event(adc, adc_chan, dir)) + ret = 1; + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_adc_configure_events(struct palmas_gpadc *adc); +static int palmas_adc_reset_events(struct palmas_gpadc *adc); + +static int palmas_gpadc_reconfigure_event_channels(struct palmas_gpadc *adc) +{ + return (adc->event0.enabled || adc->event1.enabled) ? + palmas_adc_configure_events(adc) : + palmas_adc_reset_events(adc); +} + +static int palmas_gpadc_enable_event_config(struct palmas_gpadc *adc, + const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + struct palmas_adc_event *ev; + int adc_chan = chan->channel; + + if (palmas_gpadc_get_event(adc, adc_chan, dir)) + /* already enabled */ + return 0; + + if (adc->event0.channel == -1) { + ev = &adc->event0; + } else if (adc->event1.channel == -1) { + /* event0 has to be the lowest channel */ + if (adc_chan < adc->event0.channel) { + adc->event1 = adc->event0; + ev = &adc->event0; + } else { + ev = &adc->event1; + } + } else { /* both AUTO channels already in use */ + dev_warn(adc->dev, "event0 - %d, event1 - %d\n", + adc->event0.channel, adc->event1.channel); + return -EBUSY; + } + + ev->enabled = true; + ev->channel = adc_chan; + ev->direction = dir; + + return palmas_gpadc_reconfigure_event_channels(adc); +} + +static int palmas_gpadc_disable_event_config(struct palmas_gpadc *adc, + const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + int adc_chan = chan->channel; + struct palmas_adc_event *ev = palmas_gpadc_get_event(adc, adc_chan, dir); + + if (!ev) + return 0; + + if (ev == &adc->event0) { + adc->event0 = adc->event1; + ev = &adc->event1; + } + + ev->enabled = false; + ev->channel = -1; + ev->direction = IIO_EV_DIR_NONE; + + return palmas_gpadc_reconfigure_event_channels(adc); +} + +static int palmas_gpadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret; + + if (adc_chan > PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + if (state) + ret = palmas_gpadc_enable_event_config(adc, chan, dir); + else + ret = palmas_gpadc_disable_event_config(adc, chan, dir); + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_gpadc_read_event_value(struct iio_dev *indio_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) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret; + + if (adc_chan > PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = (dir == IIO_EV_DIR_RISING) ? + adc->thresholds[adc_chan].high : + adc->thresholds[adc_chan].low; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_gpadc_write_event_value(struct iio_dev *indio_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) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int old; + int ret; + + if (adc_chan > PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 0 || val > 0xFFF) { + ret = -EINVAL; + goto out_unlock; + } + if (dir == IIO_EV_DIR_RISING) { + old = adc->thresholds[adc_chan].high; + adc->thresholds[adc_chan].high = val; + } else { + old = adc->thresholds[adc_chan].low; + adc->thresholds[adc_chan].low = val; + } + ret = 0; + break; + default: + ret = -EINVAL; + goto out_unlock; + } + + if (val != old && palmas_gpadc_get_event(adc, adc_chan, dir)) + ret = palmas_gpadc_reconfigure_event_channels(adc); + +out_unlock: + mutex_unlock(&adc->lock); + + return ret; +} + static const struct iio_info palmas_gpadc_iio_info = { .read_raw = palmas_gpadc_read_raw, + .read_event_config = palmas_gpadc_read_event_config, + .write_event_config = palmas_gpadc_write_event_config, + .read_event_value = palmas_gpadc_read_event_value, + .write_event_value = palmas_gpadc_write_event_value, +}; + +static const struct iio_event_spec palmas_gpadc_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), + }, }; #define PALMAS_ADC_CHAN_IIO(chan, _type, chan_info) \ @@ -444,6 +807,8 @@ static const struct iio_info palmas_gpadc_iio_info = { BIT(chan_info), \ .indexed = 1, \ .channel = PALMAS_ADC_CH_##chan, \ + .event_spec = palmas_gpadc_events, \ + .num_event_specs = ARRAY_SIZE(palmas_gpadc_events) \ } static const struct iio_chan_spec palmas_gpadc_iio_channel[] = { @@ -493,6 +858,13 @@ static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev, return 0; } +static void palmas_gpadc_reset(void *data) +{ + struct palmas_gpadc *adc = data; + if (adc->event0.enabled || adc->event1.enabled) + palmas_adc_reset_events(adc); +} + static int palmas_gpadc_probe(struct platform_device *pdev) { struct palmas_gpadc *adc; @@ -532,53 +904,49 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms; adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ); - if (adc->irq < 0) { - dev_err(adc->dev, - "get virq failed: %d\n", adc->irq); - ret = adc->irq; - goto out; - } - ret = request_threaded_irq(adc->irq, NULL, - palmas_gpadc_irq, - IRQF_ONESHOT, dev_name(adc->dev), - adc); - if (ret < 0) { - dev_err(adc->dev, - "request irq %d failed: %d\n", adc->irq, ret); - goto out; - } + if (adc->irq < 0) + return dev_err_probe(adc->dev, adc->irq, "get virq failed\n"); - if (gpadc_pdata->adc_wakeup1_data) { - memcpy(&adc->wakeup1_data, gpadc_pdata->adc_wakeup1_data, - sizeof(adc->wakeup1_data)); - adc->wakeup1_enable = true; - adc->irq_auto_0 = platform_get_irq(pdev, 1); - ret = request_threaded_irq(adc->irq_auto_0, NULL, - palmas_gpadc_irq_auto, - IRQF_ONESHOT, - "palmas-adc-auto-0", adc); - if (ret < 0) { - dev_err(adc->dev, "request auto0 irq %d failed: %d\n", - adc->irq_auto_0, ret); - goto out_irq_free; - } - } + ret = devm_request_threaded_irq(&pdev->dev, adc->irq, NULL, + palmas_gpadc_irq, + IRQF_ONESHOT, dev_name(adc->dev), + adc); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request irq %d failed\n", adc->irq); - if (gpadc_pdata->adc_wakeup2_data) { - memcpy(&adc->wakeup2_data, gpadc_pdata->adc_wakeup2_data, - sizeof(adc->wakeup2_data)); - adc->wakeup2_enable = true; - adc->irq_auto_1 = platform_get_irq(pdev, 2); - ret = request_threaded_irq(adc->irq_auto_1, NULL, - palmas_gpadc_irq_auto, - IRQF_ONESHOT, - "palmas-adc-auto-1", adc); - if (ret < 0) { - dev_err(adc->dev, "request auto1 irq %d failed: %d\n", - adc->irq_auto_1, ret); - goto out_irq_auto0_free; - } - } + adc->irq_auto_0 = platform_get_irq(pdev, 1); + if (adc->irq_auto_0 < 0) + return dev_err_probe(adc->dev, adc->irq_auto_0, + "get auto0 irq failed\n"); + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, NULL, + palmas_gpadc_irq_auto, IRQF_ONESHOT, + "palmas-adc-auto-0", indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request auto0 irq %d failed\n", + adc->irq_auto_0); + + adc->irq_auto_1 = platform_get_irq(pdev, 2); + if (adc->irq_auto_1 < 0) + return dev_err_probe(adc->dev, adc->irq_auto_1, + "get auto1 irq failed\n"); + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, NULL, + palmas_gpadc_irq_auto, IRQF_ONESHOT, + "palmas-adc-auto-1", indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request auto1 irq %d failed\n", + adc->irq_auto_1); + + adc->event0.enabled = false; + adc->event0.channel = -1; + adc->event0.direction = IIO_EV_DIR_NONE; + adc->event1.enabled = false; + adc->event1.channel = -1; + adc->event1.direction = IIO_EV_DIR_NONE; /* set the current source 0 (value 0/5/15/20 uA => 0..3) */ if (gpadc_pdata->ch0_current <= 1) @@ -608,11 +976,10 @@ static int palmas_gpadc_probe(struct platform_device *pdev) indio_dev->channels = palmas_gpadc_iio_channel; indio_dev->num_channels = ARRAY_SIZE(palmas_gpadc_iio_channel); - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(adc->dev, "iio_device_register() failed: %d\n", ret); - goto out_irq_auto1_free; - } + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "iio_device_register() failed\n"); device_set_wakeup_capable(&pdev->dev, 1); for (i = 0; i < PALMAS_ADC_CH_MAX; i++) { @@ -620,41 +987,14 @@ static int palmas_gpadc_probe(struct platform_device *pdev) palmas_gpadc_calibrate(adc, i); } - if (adc->wakeup1_enable || adc->wakeup2_enable) - device_wakeup_enable(&pdev->dev); - - return 0; - -out_irq_auto1_free: - if (gpadc_pdata->adc_wakeup2_data) - free_irq(adc->irq_auto_1, adc); -out_irq_auto0_free: - if (gpadc_pdata->adc_wakeup1_data) - free_irq(adc->irq_auto_0, adc); -out_irq_free: - free_irq(adc->irq, adc); -out: - return ret; -} - -static int palmas_gpadc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev); - struct palmas_gpadc *adc = iio_priv(indio_dev); - - if (adc->wakeup1_enable || adc->wakeup2_enable) - device_wakeup_disable(&pdev->dev); - iio_device_unregister(indio_dev); - free_irq(adc->irq, adc); - if (adc->wakeup1_enable) - free_irq(adc->irq_auto_0, adc); - if (adc->wakeup2_enable) - free_irq(adc->irq_auto_1, adc); + ret = devm_add_action(&pdev->dev, palmas_gpadc_reset, adc); + if (ret) + return ret; return 0; } -static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) +static int palmas_adc_configure_events(struct palmas_gpadc *adc) { int adc_period, conv; int i; @@ -680,17 +1020,23 @@ static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) } conv = 0; - if (adc->wakeup1_enable) { + if (adc->event0.enabled) { + struct palmas_adc_event *ev = &adc->event0; int polarity; - ch0 = adc->wakeup1_data.adc_channel_number; + ch0 = ev->channel; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN; - if (adc->wakeup1_data.adc_high_threshold > 0) { - thres = adc->wakeup1_data.adc_high_threshold; + switch (ev->direction) { + case IIO_EV_DIR_RISING: + thres = palmas_gpadc_get_high_threshold_raw(adc, ev); polarity = 0; - } else { - thres = adc->wakeup1_data.adc_low_threshold; + break; + case IIO_EV_DIR_FALLING: + thres = palmas_gpadc_get_low_threshold_raw(adc, ev); polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL; + break; + default: + return -EINVAL; } ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, @@ -711,17 +1057,23 @@ static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) } } - if (adc->wakeup2_enable) { + if (adc->event1.enabled) { + struct palmas_adc_event *ev = &adc->event1; int polarity; - ch1 = adc->wakeup2_data.adc_channel_number; + ch1 = ev->channel; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN; - if (adc->wakeup2_data.adc_high_threshold > 0) { - thres = adc->wakeup2_data.adc_high_threshold; + switch (ev->direction) { + case IIO_EV_DIR_RISING: + thres = palmas_gpadc_get_high_threshold_raw(adc, ev); polarity = 0; - } else { - thres = adc->wakeup2_data.adc_low_threshold; + break; + case IIO_EV_DIR_FALLING: + thres = palmas_gpadc_get_low_threshold_raw(adc, ev); polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL; + break; + default: + return -EINVAL; } ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, @@ -759,7 +1111,7 @@ static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) return ret; } -static int palmas_adc_wakeup_reset(struct palmas_gpadc *adc) +static int palmas_adc_reset_events(struct palmas_gpadc *adc) { int ret; @@ -781,20 +1133,14 @@ static int palmas_gpadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct palmas_gpadc *adc = iio_priv(indio_dev); - int wakeup = adc->wakeup1_enable || adc->wakeup2_enable; - int ret; - if (!device_may_wakeup(dev) || !wakeup) + if (!device_may_wakeup(dev)) return 0; - ret = palmas_adc_wakeup_configure(adc); - if (ret < 0) - return ret; - - if (adc->wakeup1_enable) + if (adc->event0.enabled) enable_irq_wake(adc->irq_auto_0); - if (adc->wakeup2_enable) + if (adc->event1.enabled) enable_irq_wake(adc->irq_auto_1); return 0; @@ -804,20 +1150,14 @@ static int palmas_gpadc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct palmas_gpadc *adc = iio_priv(indio_dev); - int wakeup = adc->wakeup1_enable || adc->wakeup2_enable; - int ret; - if (!device_may_wakeup(dev) || !wakeup) + if (!device_may_wakeup(dev)) return 0; - ret = palmas_adc_wakeup_reset(adc); - if (ret < 0) - return ret; - - if (adc->wakeup1_enable) + if (adc->event0.enabled) disable_irq_wake(adc->irq_auto_0); - if (adc->wakeup2_enable) + if (adc->event1.enabled) disable_irq_wake(adc->irq_auto_1); return 0; @@ -834,7 +1174,6 @@ MODULE_DEVICE_TABLE(of, of_palmas_gpadc_match_tbl); static struct platform_driver palmas_gpadc_driver = { .probe = palmas_gpadc_probe, - .remove = palmas_gpadc_remove, .driver = { .name = MOD_NAME, .pm = pm_sleep_ptr(&palmas_pm_ops), diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index eb424496ee1d..64a3aeb6261c 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -758,7 +758,7 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, /* Find the right channel setting */ chid = 0; hwchan = &hw_channels[0]; - while (hwchan && hwchan->datasheet_name) { + while (hwchan->datasheet_name) { if (hwchan->pre_scale_mux == pre_scale_mux && hwchan->amux_channel == amux_channel) break; diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 27d9e147b4b7..b8972f673c9d 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -283,7 +283,7 @@ static const struct of_device_id rcar_gyroadc_match[] = { MODULE_DEVICE_TABLE(of, rcar_gyroadc_match); -static const struct of_device_id rcar_gyroadc_child_match[] = { +static const struct of_device_id rcar_gyroadc_child_match[] __maybe_unused = { /* Mode 1 ADCs */ { .compatible = "fujitsu,mb88101a", diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 45d4e79f8e55..1aadb2ad2cab 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2588,7 +2588,7 @@ static const struct stm32_adc_cfg stm32f4_adc_cfg = { .irq_clear = stm32f4_adc_irq_clear, }; -const unsigned int stm32_adc_min_ts_h7[] = { 0, 0, 0, 4300, 9000 }; +static const unsigned int stm32_adc_min_ts_h7[] = { 0, 0, 0, 4300, 9000 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_h7) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32h7_adc_cfg = { @@ -2607,7 +2607,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .ts_int_ch = stm32_adc_min_ts_h7, }; -const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; +static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp1) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32mp1_adc_cfg = { @@ -2627,7 +2627,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .ts_int_ch = stm32_adc_min_ts_mp1, }; -const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; +static const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp13) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32mp13_adc_cfg = { diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c new file mode 100644 index 000000000000..6b5aebb82455 --- /dev/null +++ b/drivers/iio/adc/ti-ads1100.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADS1100 - Texas Instruments Analog-to-Digital Converter + * + * Copyright (c) 2023, Topic Embedded Products + * + * Datasheet: https://www.ti.com/lit/gpn/ads1100 + * IIO driver for ADS1100 and ADS1000 ADC 16-bit I2C + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/units.h> + +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +/* The ADS1100 has a single byte config register */ + +/* Conversion in progress bit */ +#define ADS1100_CFG_ST_BSY BIT(7) +/* Single conversion bit */ +#define ADS1100_CFG_SC BIT(4) +/* Data rate */ +#define ADS1100_DR_MASK GENMASK(3, 2) +/* Gain */ +#define ADS1100_PGA_MASK GENMASK(1, 0) + +#define ADS1100_CONTINUOUS 0 +#define ADS1100_SINGLESHOT ADS1100_CFG_SC + +#define ADS1100_SLEEP_DELAY_MS 2000 + +static const int ads1100_data_rate[] = { 128, 32, 16, 8 }; +static const int ads1100_data_rate_bits[] = { 12, 14, 15, 16 }; + +struct ads1100_data { + struct i2c_client *client; + struct regulator *reg_vdd; + struct mutex lock; + int scale_avail[2 * 4]; /* 4 gain settings */ + u8 config; + bool supports_data_rate; /* Only the ADS1100 can select the rate */ +}; + +static const struct iio_chan_spec ads1100_channel = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + .datasheet_name = "AIN", +}; + +static int ads1100_set_config_bits(struct ads1100_data *data, u8 mask, u8 value) +{ + int ret; + u8 config = (data->config & ~mask) | (value & mask); + + if (data->config == config) + return 0; /* Already done */ + + ret = i2c_master_send(data->client, &config, 1); + if (ret < 0) + return ret; + + data->config = config; + + return 0; +}; + +static int ads1100_data_bits(struct ads1100_data *data) +{ + return ads1100_data_rate_bits[FIELD_GET(ADS1100_DR_MASK, data->config)]; +} + +static int ads1100_get_adc_result(struct ads1100_data *data, int chan, int *val) +{ + int ret; + __be16 buffer; + s16 value; + + if (chan != 0) + return -EINVAL; + + ret = pm_runtime_resume_and_get(&data->client->dev); + if (ret < 0) + return ret; + + ret = i2c_master_recv(data->client, (char *)&buffer, sizeof(buffer)); + + pm_runtime_mark_last_busy(&data->client->dev); + pm_runtime_put_autosuspend(&data->client->dev); + + if (ret < 0) { + dev_err(&data->client->dev, "I2C read fail: %d\n", ret); + return ret; + } + + /* Value is always 16-bit 2's complement */ + value = be16_to_cpu(buffer); + + /* Shift result to compensate for bit resolution vs. sample rate */ + value <<= 16 - ads1100_data_bits(data); + + *val = sign_extend32(value, 15); + + return 0; +} + +static int ads1100_set_scale(struct ads1100_data *data, int val, int val2) +{ + int microvolts; + int gain; + + /* With Vdd between 2.7 and 5V, the scale is always below 1 */ + if (val) + return -EINVAL; + + if (!val2) + return -EINVAL; + + microvolts = regulator_get_voltage(data->reg_vdd); + /* + * val2 is in 'micro' units, n = val2 / 1000000 + * result must be millivolts, d = microvolts / 1000 + * the full-scale value is d/n, corresponds to 2^15, + * hence the gain = (d / n) >> 15, factoring out the 1000 and moving the + * bitshift so everything fits in 32-bits yields this formula. + */ + gain = DIV_ROUND_CLOSEST(microvolts, BIT(15)) * MILLI / val2; + if (gain < BIT(0) || gain > BIT(3)) + return -EINVAL; + + ads1100_set_config_bits(data, ADS1100_PGA_MASK, ffs(gain) - 1); + + return 0; +} + +static int ads1100_set_data_rate(struct ads1100_data *data, int chan, int rate) +{ + unsigned int i; + unsigned int size; + + size = data->supports_data_rate ? ARRAY_SIZE(ads1100_data_rate) : 1; + for (i = 0; i < size; i++) { + if (ads1100_data_rate[i] == rate) + return ads1100_set_config_bits(data, ADS1100_DR_MASK, + FIELD_PREP(ADS1100_DR_MASK, i)); + } + + return -EINVAL; +} + +static int ads1100_get_vdd_millivolts(struct ads1100_data *data) +{ + return regulator_get_voltage(data->reg_vdd) / (MICRO / MILLI); +} + +static void ads1100_calc_scale_avail(struct ads1100_data *data) +{ + int millivolts = ads1100_get_vdd_millivolts(data); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(data->scale_avail) / 2; i++) { + data->scale_avail[i * 2 + 0] = millivolts; + data->scale_avail[i * 2 + 1] = 15 + i; + } +} + +static int ads1100_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ads1100_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT; + *vals = ads1100_data_rate; + if (data->supports_data_rate) + *length = ARRAY_SIZE(ads1100_data_rate); + else + *length = 1; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_FRACTIONAL_LOG2; + *vals = data->scale_avail; + *length = ARRAY_SIZE(data->scale_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ads1100_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + struct ads1100_data *data = iio_priv(indio_dev); + + mutex_lock(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + break; + + ret = ads1100_get_adc_result(data, chan->address, val); + if (ret >= 0) + ret = IIO_VAL_INT; + iio_device_release_direct_mode(indio_dev); + break; + case IIO_CHAN_INFO_SCALE: + /* full-scale is the supply voltage in millivolts */ + *val = ads1100_get_vdd_millivolts(data); + *val2 = 15 + FIELD_GET(ADS1100_PGA_MASK, data->config); + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = ads1100_data_rate[FIELD_GET(ADS1100_DR_MASK, + data->config)]; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + + return ret; +} + +static int ads1100_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads1100_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = ads1100_set_scale(data, val, val2); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = ads1100_set_data_rate(data, chan->address, val); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + + return ret; +} + +static const struct iio_info ads1100_info = { + .read_avail = ads1100_read_avail, + .read_raw = ads1100_read_raw, + .write_raw = ads1100_write_raw, +}; + +static int ads1100_setup(struct ads1100_data *data) +{ + int ret; + u8 buffer[3]; + + /* Setup continuous sampling mode at 8sps */ + buffer[0] = ADS1100_DR_MASK | ADS1100_CONTINUOUS; + ret = i2c_master_send(data->client, buffer, 1); + if (ret < 0) + return ret; + + ret = i2c_master_recv(data->client, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + /* Config register returned in third byte, strip away the busy status */ + data->config = buffer[2] & ~ADS1100_CFG_ST_BSY; + + /* Detect the sample rate capability by checking the DR bits */ + data->supports_data_rate = FIELD_GET(ADS1100_DR_MASK, buffer[2]) != 0; + + return 0; +} + +static void ads1100_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static void ads1100_disable_continuous(void *data) +{ + ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_SINGLESHOT); +} + +static int ads1100_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ads1100_data *data; + struct device *dev = &client->dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, data); + data->client = client; + mutex_init(&data->lock); + + indio_dev->name = "ads1100"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &ads1100_channel; + indio_dev->num_channels = 1; + indio_dev->info = &ads1100_info; + + data->reg_vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->reg_vdd)) + return dev_err_probe(dev, PTR_ERR(data->reg_vdd), + "Failed to get vdd regulator\n"); + + ret = regulator_enable(data->reg_vdd); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to enable vdd regulator\n"); + + ret = devm_add_action_or_reset(dev, ads1100_reg_disable, data->reg_vdd); + if (ret) + return ret; + + ret = ads1100_setup(data); + if (ret) + return dev_err_probe(dev, ret, + "Failed to communicate with device\n"); + + ret = devm_add_action_or_reset(dev, ads1100_disable_continuous, data); + if (ret) + return ret; + + ads1100_calc_scale_avail(data); + + pm_runtime_set_autosuspend_delay(dev, ADS1100_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register IIO device\n"); + + return 0; +} + +static int ads1100_runtime_suspend(struct device *dev) +{ + struct ads1100_data *data = dev_get_drvdata(dev); + + ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_SINGLESHOT); + regulator_disable(data->reg_vdd); + + return 0; +} + +static int ads1100_runtime_resume(struct device *dev) +{ + struct ads1100_data *data = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(data->reg_vdd); + if (ret) { + dev_err(&data->client->dev, "Failed to enable Vdd\n"); + return ret; + } + + /* + * We'll always change the mode bit in the config register, so there is + * no need here to "force" a write to the config register. If the device + * has been power-cycled, we'll re-write its config register now. + */ + return ads1100_set_config_bits(data, ADS1100_CFG_SC, + ADS1100_CONTINUOUS); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ads1100_pm_ops, + ads1100_runtime_suspend, + ads1100_runtime_resume, + NULL); + +static const struct i2c_device_id ads1100_id[] = { + { "ads1100" }, + { "ads1000" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ads1100_id); + +static const struct of_device_id ads1100_of_match[] = { + {.compatible = "ti,ads1100" }, + {.compatible = "ti,ads1000" }, + { } +}; + +MODULE_DEVICE_TABLE(of, ads1100_of_match); + +static struct i2c_driver ads1100_driver = { + .driver = { + .name = "ads1100", + .of_match_table = ads1100_of_match, + .pm = pm_ptr(&ads1100_pm_ops), + }, + .probe_new = ads1100_probe, + .id_table = ads1100_id, +}; + +module_i2c_driver(ads1100_driver); + +MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); +MODULE_DESCRIPTION("Texas Instruments ADS1100 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index 2843fcb70e24..877f9124803c 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -35,7 +35,9 @@ config STX104 tristate "Apex Embedded Systems STX104 driver" depends on PC104 && X86 select ISA_BUS_API + select REGMAP_MMIO select GPIOLIB + select GPIO_REGMAP help Say yes here to build support for the Apex Embedded Systems STX104 integrated analog PC/104 card. diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index f32c8c2fb26d..07e9f6ae16a8 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -39,6 +39,7 @@ struct ad74413r_chip_info { struct ad74413r_channel_config { u32 func; + u32 drive_strength; bool gpo_comparator; bool initialized; }; @@ -99,6 +100,7 @@ struct ad74413r_state { #define AD74413R_REG_ADC_CONFIG_X(x) (0x05 + (x)) #define AD74413R_ADC_CONFIG_RANGE_MASK GENMASK(7, 5) #define AD74413R_ADC_CONFIG_REJECTION_MASK GENMASK(4, 3) +#define AD74413R_ADC_CONFIG_CH_200K_TO_GND BIT(2) #define AD74413R_ADC_RANGE_10V 0b000 #define AD74413R_ADC_RANGE_2P5V_EXT_POW 0b001 #define AD74413R_ADC_RANGE_2P5V_INT_POW 0b010 @@ -111,6 +113,7 @@ struct ad74413r_state { #define AD74413R_REG_DIN_CONFIG_X(x) (0x09 + (x)) #define AD74413R_DIN_DEBOUNCE_MASK GENMASK(4, 0) #define AD74413R_DIN_DEBOUNCE_LEN BIT(5) +#define AD74413R_DIN_SINK_MASK GENMASK(9, 6) #define AD74413R_REG_DAC_CODE_X(x) (0x16 + (x)) #define AD74413R_DAC_CODE_MAX GENMASK(12, 0) @@ -261,6 +264,18 @@ static int ad74413r_set_comp_debounce(struct ad74413r_state *st, val); } +static int ad74413r_set_comp_drive_strength(struct ad74413r_state *st, + unsigned int offset, + unsigned int strength) +{ + strength = min(strength, 1800U); + + return regmap_update_bits(st->regmap, AD74413R_REG_DIN_CONFIG_X(offset), + AD74413R_DIN_SINK_MASK, + FIELD_PREP(AD74413R_DIN_SINK_MASK, strength / 120)); +} + + static void ad74413r_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { @@ -424,9 +439,20 @@ static int ad74413r_set_channel_dac_code(struct ad74413r_state *st, static int ad74413r_set_channel_function(struct ad74413r_state *st, unsigned int channel, u8 func) { - return regmap_update_bits(st->regmap, + int ret; + + ret = regmap_update_bits(st->regmap, AD74413R_REG_CH_FUNC_SETUP_X(channel), AD74413R_CH_FUNC_SETUP_MASK, func); + if (ret) + return ret; + + if (func == CH_FUNC_CURRENT_INPUT_LOOP_POWER) + ret = regmap_set_bits(st->regmap, + AD74413R_REG_ADC_CONFIG_X(channel), + AD74413R_ADC_CONFIG_CH_200K_TO_GND); + + return ret; } static int ad74413r_set_adc_conv_seq(struct ad74413r_state *st, @@ -1112,6 +1138,11 @@ static struct iio_chan_spec ad74413r_current_input_channels[] = { AD74413R_ADC_CURRENT_CHANNEL, }; +static struct iio_chan_spec ad74413r_current_input_loop_channels[] = { + AD74413R_DAC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_CURRENT_CHANNEL, +}; + static struct iio_chan_spec ad74413r_resistance_input_channels[] = { AD74413R_ADC_CHANNEL(IIO_RESISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)), }; @@ -1135,7 +1166,7 @@ static const struct ad74413r_channels ad74413r_channels_map[] = { [CH_FUNC_CURRENT_OUTPUT] = AD74413R_CHANNELS(current_output), [CH_FUNC_VOLTAGE_INPUT] = AD74413R_CHANNELS(voltage_input), [CH_FUNC_CURRENT_INPUT_EXT_POWER] = AD74413R_CHANNELS(current_input), - [CH_FUNC_CURRENT_INPUT_LOOP_POWER] = AD74413R_CHANNELS(current_input), + [CH_FUNC_CURRENT_INPUT_LOOP_POWER] = AD74413R_CHANNELS(current_input_loop), [CH_FUNC_RESISTANCE_INPUT] = AD74413R_CHANNELS(resistance_input), [CH_FUNC_DIGITAL_INPUT_LOGIC] = AD74413R_CHANNELS(digital_input), [CH_FUNC_DIGITAL_INPUT_LOOP_POWER] = AD74413R_CHANNELS(digital_input), @@ -1190,6 +1221,9 @@ static int ad74413r_parse_channel_config(struct iio_dev *indio_dev, config->gpo_comparator = fwnode_property_read_bool(channel_node, "adi,gpo-comparator"); + fwnode_property_read_u32(channel_node, "drive-strength-microamp", + &config->drive_strength); + if (!config->gpo_comparator) st->num_gpo_gpios++; @@ -1269,6 +1303,7 @@ static int ad74413r_setup_gpios(struct ad74413r_state *st) unsigned int gpo_gpio_i = 0; unsigned int i; u8 gpo_config; + u32 strength; int ret; for (i = 0; i < AD74413R_CHANNEL_MAX; i++) { @@ -1285,6 +1320,11 @@ static int ad74413r_setup_gpios(struct ad74413r_state *st) config->func == CH_FUNC_DIGITAL_INPUT_LOOP_POWER) st->comp_gpio_offsets[comp_gpio_i++] = i; + strength = config->drive_strength; + ret = ad74413r_set_comp_drive_strength(st, i, strength); + if (ret) + return ret; + ret = ad74413r_set_gpo_config(st, i, gpo_config); if (ret) return ret; diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index 48a91a95e597..d1f7ce033b46 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -3,19 +3,20 @@ * IIO driver for the Apex Embedded Systems STX104 * Copyright (C) 2016 William Breathitt Gray */ -#include <linux/bitops.h> +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/device.h> -#include <linux/errno.h> -#include <linux/gpio/driver.h> +#include <linux/err.h> +#include <linux/gpio/regmap.h> #include <linux/iio/iio.h> #include <linux/iio/types.h> -#include <linux/io.h> -#include <linux/ioport.h> #include <linux/isa.h> #include <linux/kernel.h> +#include <linux/limits.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/regmap.h> #include <linux/types.h> #define STX104_OUT_CHAN(chan) { \ @@ -45,101 +46,211 @@ static unsigned int num_stx104; module_param_hw_array(base, uint, ioport, &num_stx104, 0); MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); -/** - * struct stx104_reg - device register structure - * @ssr_ad: Software Strobe Register and ADC Data - * @achan: ADC Channel - * @dio: Digital I/O - * @dac: DAC Channels - * @cir_asr: Clear Interrupts and ADC Status - * @acr: ADC Control - * @pccr_fsh: Pacer Clock Control and FIFO Status MSB - * @acfg: ADC Configuration - */ -struct stx104_reg { - u16 ssr_ad; - u8 achan; - u8 dio; - u16 dac[2]; - u8 cir_asr; - u8 acr; - u8 pccr_fsh; - u8 acfg; -}; +#define STX104_AIO_BASE 0x0 +#define STX104_SOFTWARE_STROBE STX104_AIO_BASE +#define STX104_ADC_DATA STX104_AIO_BASE +#define STX104_ADC_CHANNEL (STX104_AIO_BASE + 0x2) +#define STX104_DIO_REG (STX104_AIO_BASE + 0x3) +#define STX104_DAC_BASE (STX104_AIO_BASE + 0x4) +#define STX104_ADC_STATUS (STX104_AIO_BASE + 0x8) +#define STX104_ADC_CONTROL (STX104_AIO_BASE + 0x9) +#define STX104_ADC_CONFIGURATION (STX104_AIO_BASE + 0x11) + +#define STX104_AIO_DATA_STRIDE 2 +#define STX104_DAC_OFFSET(_channel) (STX104_DAC_BASE + STX104_AIO_DATA_STRIDE * (_channel)) + +/* ADC Channel */ +#define STX104_FC GENMASK(3, 0) +#define STX104_LC GENMASK(7, 4) +#define STX104_SINGLE_CHANNEL(_channel) \ + (u8_encode_bits(_channel, STX104_FC) | u8_encode_bits(_channel, STX104_LC)) + +/* ADC Status */ +#define STX104_SD BIT(5) +#define STX104_CNV BIT(7) +#define STX104_DIFFERENTIAL 1 + +/* ADC Control */ +#define STX104_ALSS GENMASK(1, 0) +#define STX104_SOFTWARE_TRIGGER u8_encode_bits(0x0, STX104_ALSS) + +/* ADC Configuration */ +#define STX104_GAIN GENMASK(1, 0) +#define STX104_ADBU BIT(2) +#define STX104_BIPOLAR 0 +#define STX104_GAIN_X1 0 +#define STX104_GAIN_X2 1 +#define STX104_GAIN_X4 2 +#define STX104_GAIN_X8 3 /** * struct stx104_iio - IIO device private data structure - * @chan_out_states: channels' output states - * @reg: I/O address offset for the device registers + * @lock: synchronization lock to prevent I/O race conditions + * @aio_data_map: Regmap for analog I/O data + * @aio_ctl_map: Regmap for analog I/O control */ struct stx104_iio { - unsigned int chan_out_states[STX104_NUM_OUT_CHAN]; - struct stx104_reg __iomem *reg; + struct mutex lock; + struct regmap *aio_data_map; + struct regmap *aio_ctl_map; }; -/** - * struct stx104_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device - * @out_state: output bits state - */ -struct stx104_gpio { - struct gpio_chip chip; - spinlock_t lock; - u8 __iomem *base; - unsigned int out_state; +static const struct regmap_range aio_ctl_wr_ranges[] = { + regmap_reg_range(0x0, 0x0), regmap_reg_range(0x2, 0x2), regmap_reg_range(0x9, 0x9), + regmap_reg_range(0x11, 0x11), +}; +static const struct regmap_range aio_ctl_rd_ranges[] = { + regmap_reg_range(0x2, 0x2), regmap_reg_range(0x8, 0x9), regmap_reg_range(0x11, 0x11), +}; +static const struct regmap_range aio_ctl_volatile_ranges[] = { + regmap_reg_range(0x8, 0x8), +}; +static const struct regmap_access_table aio_ctl_wr_table = { + .yes_ranges = aio_ctl_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_ctl_wr_ranges), +}; +static const struct regmap_access_table aio_ctl_rd_table = { + .yes_ranges = aio_ctl_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_ctl_rd_ranges), +}; +static const struct regmap_access_table aio_ctl_volatile_table = { + .yes_ranges = aio_ctl_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_ctl_volatile_ranges), +}; + +static const struct regmap_config aio_ctl_regmap_config = { + .name = "aio_ctl", + .reg_bits = 8, + .reg_stride = 1, + .reg_base = STX104_AIO_BASE, + .val_bits = 8, + .io_port = true, + .wr_table = &aio_ctl_wr_table, + .rd_table = &aio_ctl_rd_table, + .volatile_table = &aio_ctl_volatile_table, + .cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_range aio_data_wr_ranges[] = { + regmap_reg_range(0x4, 0x6), +}; +static const struct regmap_range aio_data_rd_ranges[] = { + regmap_reg_range(0x0, 0x0), +}; +static const struct regmap_access_table aio_data_wr_table = { + .yes_ranges = aio_data_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_data_wr_ranges), +}; +static const struct regmap_access_table aio_data_rd_table = { + .yes_ranges = aio_data_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_data_rd_ranges), +}; + +static const struct regmap_config aio_data_regmap_config = { + .name = "aio_data", + .reg_bits = 16, + .reg_stride = STX104_AIO_DATA_STRIDE, + .reg_base = STX104_AIO_BASE, + .val_bits = 16, + .io_port = true, + .wr_table = &aio_data_wr_table, + .rd_table = &aio_data_rd_table, + .volatile_table = &aio_data_rd_table, + .cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_config dio_regmap_config = { + .name = "dio", + .reg_bits = 8, + .reg_stride = 1, + .reg_base = STX104_DIO_REG, + .val_bits = 8, + .io_port = true, }; static int stx104_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct stx104_iio *const priv = iio_priv(indio_dev); - struct stx104_reg __iomem *const reg = priv->reg; + int err; unsigned int adc_config; - int adbu; - int gain; + unsigned int value; + unsigned int adc_status; switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: - /* get gain configuration */ - adc_config = ioread8(®->acfg); - gain = adc_config & 0x3; + err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config); + if (err) + return err; - *val = 1 << gain; + *val = BIT(u8_get_bits(adc_config, STX104_GAIN)); return IIO_VAL_INT; case IIO_CHAN_INFO_RAW: if (chan->output) { - *val = priv->chan_out_states[chan->channel]; + err = regmap_read(priv->aio_data_map, STX104_DAC_OFFSET(chan->channel), + &value); + if (err) + return err; + *val = value; return IIO_VAL_INT; } + mutex_lock(&priv->lock); + /* select ADC channel */ - iowrite8(chan->channel | (chan->channel << 4), ®->achan); + err = regmap_write(priv->aio_ctl_map, STX104_ADC_CHANNEL, + STX104_SINGLE_CHANNEL(chan->channel)); + if (err) { + mutex_unlock(&priv->lock); + return err; + } - /* trigger ADC sample capture by writing to the 8-bit - * Software Strobe Register and wait for completion + /* + * Trigger ADC sample capture by writing to the 8-bit Software Strobe Register and + * wait for completion; the conversion time range is 5 microseconds to 53.68 seconds + * in steps of 25 nanoseconds. The actual Analog Input Frame Timer time interval is + * calculated as: + * ai_time_frame_ns = ( AIFT + 1 ) * ( 25 nanoseconds ). + * Where 0 <= AIFT <= 2147483648. */ - iowrite8(0, ®->ssr_ad); - while (ioread8(®->cir_asr) & BIT(7)); + err = regmap_write(priv->aio_ctl_map, STX104_SOFTWARE_STROBE, 0); + if (err) { + mutex_unlock(&priv->lock); + return err; + } + err = regmap_read_poll_timeout(priv->aio_ctl_map, STX104_ADC_STATUS, adc_status, + !u8_get_bits(adc_status, STX104_CNV), 0, 53687092); + if (err) { + mutex_unlock(&priv->lock); + return err; + } + + err = regmap_read(priv->aio_data_map, STX104_ADC_DATA, &value); + if (err) { + mutex_unlock(&priv->lock); + return err; + } + *val = value; - *val = ioread16(®->ssr_ad); + mutex_unlock(&priv->lock); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: /* get ADC bipolar/unipolar configuration */ - adc_config = ioread8(®->acfg); - adbu = !(adc_config & BIT(2)); + err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config); + if (err) + return err; - *val = -32768 * adbu; + *val = (u8_get_bits(adc_config, STX104_ADBU) == STX104_BIPOLAR) ? -32768 : 0; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* get ADC bipolar/unipolar and gain configuration */ - adc_config = ioread8(®->acfg); - adbu = !(adc_config & BIT(2)); - gain = adc_config & 0x3; + err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config); + if (err) + return err; *val = 5; - *val2 = 15 - adbu + gain; + *val2 = (u8_get_bits(adc_config, STX104_ADBU) == STX104_BIPOLAR) ? 14 : 15; + *val2 += u8_get_bits(adc_config, STX104_GAIN); return IIO_VAL_FRACTIONAL_LOG2; } @@ -150,40 +261,37 @@ static int stx104_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct stx104_iio *const priv = iio_priv(indio_dev); + u8 gain; switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: /* Only four gain states (x1, x2, x4, x8) */ switch (val) { case 1: - iowrite8(0, &priv->reg->acfg); + gain = STX104_GAIN_X1; break; case 2: - iowrite8(1, &priv->reg->acfg); + gain = STX104_GAIN_X2; break; case 4: - iowrite8(2, &priv->reg->acfg); + gain = STX104_GAIN_X4; break; case 8: - iowrite8(3, &priv->reg->acfg); + gain = STX104_GAIN_X8; break; default: return -EINVAL; } - return 0; + return regmap_write(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, gain); case IIO_CHAN_INFO_RAW: - if (chan->output) { - /* DAC can only accept up to a 16-bit value */ - if ((unsigned int)val > 65535) - return -EINVAL; + if (!chan->output) + return -EINVAL; - priv->chan_out_states[chan->channel] = val; - iowrite16(val, &priv->reg->dac[chan->channel]); + if (val < 0 || val > U16_MAX) + return -EINVAL; - return 0; - } - return -EINVAL; + return regmap_write(priv->aio_data_map, STX104_DAC_OFFSET(chan->channel), val); } return -EINVAL; @@ -212,119 +320,66 @@ static const struct iio_chan_spec stx104_channels_diff[] = { STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1) }; -static int stx104_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) -{ - /* GPIO 0-3 are input only, while the rest are output only */ - if (offset < 4) - return 1; - - return 0; -} - -static int stx104_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) +static int stx104_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base, + unsigned int offset, unsigned int *const reg, + unsigned int *const mask) { + /* Output lines are located at same register bit offsets as input lines */ if (offset >= 4) - return -EINVAL; - - return 0; -} + offset -= 4; -static int stx104_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - if (offset < 4) - return -EINVAL; + *reg = base; + *mask = BIT(offset); - chip->set(chip, offset, value); return 0; } -static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - - if (offset >= 4) - return -EINVAL; - - return !!(ioread8(stx104gpio->base) & BIT(offset)); -} - -static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - - *bits = ioread8(stx104gpio->base); - - return 0; -} - -static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - const unsigned int mask = BIT(offset) >> 4; - unsigned long flags; - - if (offset < 4) - return; - - spin_lock_irqsave(&stx104gpio->lock, flags); - - if (value) - stx104gpio->out_state |= mask; - else - stx104gpio->out_state &= ~mask; - - iowrite8(stx104gpio->out_state, stx104gpio->base); - - spin_unlock_irqrestore(&stx104gpio->lock, flags); -} - #define STX104_NGPIO 8 static const char *stx104_names[STX104_NGPIO] = { "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3" }; -static void stx104_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) +static int stx104_init_hw(struct stx104_iio *const priv) { - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - unsigned long flags; - - /* verify masked GPIO are output */ - if (!(*mask & 0xF0)) - return; + int err; - *mask >>= 4; - *bits >>= 4; + /* configure device for software trigger operation */ + err = regmap_write(priv->aio_ctl_map, STX104_ADC_CONTROL, STX104_SOFTWARE_TRIGGER); + if (err) + return err; - spin_lock_irqsave(&stx104gpio->lock, flags); + /* initialize gain setting to x1 */ + err = regmap_write(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, STX104_GAIN_X1); + if (err) + return err; - stx104gpio->out_state &= ~*mask; - stx104gpio->out_state |= *mask & *bits; - iowrite8(stx104gpio->out_state, stx104gpio->base); + /* initialize DAC outputs to 0V */ + err = regmap_write(priv->aio_data_map, STX104_DAC_BASE, 0); + if (err) + return err; + err = regmap_write(priv->aio_data_map, STX104_DAC_BASE + STX104_AIO_DATA_STRIDE, 0); + if (err) + return err; - spin_unlock_irqrestore(&stx104gpio->lock, flags); + return 0; } static int stx104_probe(struct device *dev, unsigned int id) { struct iio_dev *indio_dev; struct stx104_iio *priv; - struct stx104_gpio *stx104gpio; + struct gpio_regmap_config gpio_config; + void __iomem *stx104_base; + struct regmap *aio_ctl_map; + struct regmap *aio_data_map; + struct regmap *dio_map; int err; + unsigned int adc_status; indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); if (!indio_dev) return -ENOMEM; - stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL); - if (!stx104gpio) - return -ENOMEM; - if (!devm_request_region(dev, base[id], STX104_EXTENT, dev_name(dev))) { dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", @@ -332,16 +387,37 @@ static int stx104_probe(struct device *dev, unsigned int id) return -EBUSY; } - priv = iio_priv(indio_dev); - priv->reg = devm_ioport_map(dev, base[id], STX104_EXTENT); - if (!priv->reg) + stx104_base = devm_ioport_map(dev, base[id], STX104_EXTENT); + if (!stx104_base) return -ENOMEM; + aio_ctl_map = devm_regmap_init_mmio(dev, stx104_base, &aio_ctl_regmap_config); + if (IS_ERR(aio_ctl_map)) + return dev_err_probe(dev, PTR_ERR(aio_ctl_map), + "Unable to initialize aio_ctl register map\n"); + + aio_data_map = devm_regmap_init_mmio(dev, stx104_base, &aio_data_regmap_config); + if (IS_ERR(aio_data_map)) + return dev_err_probe(dev, PTR_ERR(aio_data_map), + "Unable to initialize aio_data register map\n"); + + dio_map = devm_regmap_init_mmio(dev, stx104_base, &dio_regmap_config); + if (IS_ERR(dio_map)) + return dev_err_probe(dev, PTR_ERR(dio_map), + "Unable to initialize dio register map\n"); + + priv = iio_priv(indio_dev); + priv->aio_ctl_map = aio_ctl_map; + priv->aio_data_map = aio_data_map; + indio_dev->info = &stx104_info; indio_dev->modes = INDIO_DIRECT_MODE; - /* determine if differential inputs */ - if (ioread8(&priv->reg->cir_asr) & BIT(5)) { + err = regmap_read(aio_ctl_map, STX104_ADC_STATUS, &adc_status); + if (err) + return err; + + if (u8_get_bits(adc_status, STX104_SD) == STX104_DIFFERENTIAL) { indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff); indio_dev->channels = stx104_channels_diff; } else { @@ -351,41 +427,29 @@ static int stx104_probe(struct device *dev, unsigned int id) indio_dev->name = dev_name(dev); - /* configure device for software trigger operation */ - iowrite8(0, &priv->reg->acr); + mutex_init(&priv->lock); - /* initialize gain setting to x1 */ - iowrite8(0, &priv->reg->acfg); - - /* initialize DAC output to 0V */ - iowrite16(0, &priv->reg->dac[0]); - iowrite16(0, &priv->reg->dac[1]); - - stx104gpio->chip.label = dev_name(dev); - stx104gpio->chip.parent = dev; - stx104gpio->chip.owner = THIS_MODULE; - stx104gpio->chip.base = -1; - stx104gpio->chip.ngpio = STX104_NGPIO; - stx104gpio->chip.names = stx104_names; - stx104gpio->chip.get_direction = stx104_gpio_get_direction; - stx104gpio->chip.direction_input = stx104_gpio_direction_input; - stx104gpio->chip.direction_output = stx104_gpio_direction_output; - stx104gpio->chip.get = stx104_gpio_get; - stx104gpio->chip.get_multiple = stx104_gpio_get_multiple; - stx104gpio->chip.set = stx104_gpio_set; - stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; - stx104gpio->base = &priv->reg->dio; - stx104gpio->out_state = 0x0; - - spin_lock_init(&stx104gpio->lock); - - err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); + err = stx104_init_hw(priv); + if (err) + return err; + + err = devm_iio_device_register(dev, indio_dev); + if (err) return err; - } - return devm_iio_device_register(dev, indio_dev); + gpio_config = (struct gpio_regmap_config) { + .parent = dev, + .regmap = dio_map, + .ngpio = STX104_NGPIO, + .names = stx104_names, + .reg_dat_base = GPIO_REGMAP_ADDR(STX104_DIO_REG), + .reg_set_base = GPIO_REGMAP_ADDR(STX104_DIO_REG), + .ngpio_per_reg = STX104_NGPIO, + .reg_mask_xlate = stx104_reg_mask_xlate, + .drvdata = dio_map, + }; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); } static struct isa_driver stx104_driver = { diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c index 2aed483a2fde..0cb5d9b65d62 100644 --- a/drivers/iio/chemical/sps30_i2c.c +++ b/drivers/iio/chemical/sps30_i2c.c @@ -68,10 +68,10 @@ static int sps30_i2c_command(struct sps30_state *state, u16 cmd, void *arg, size /* * Internally sensor stores measurements in a following manner: * - * PM1: upper two bytes, crc8, lower two bytes, crc8 + * PM1: upper two bytes, crc8, lower two bytes, crc8 * PM2P5: upper two bytes, crc8, lower two bytes, crc8 - * PM4: upper two bytes, crc8, lower two bytes, crc8 - * PM10: upper two bytes, crc8, lower two bytes, crc8 + * PM4: upper two bytes, crc8, lower two bytes, crc8 + * PM10: upper two bytes, crc8, lower two bytes, crc8 * * What follows next are number concentration measurements and * typical particle size measurement which we omit. diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 899b640c0a70..a0df9250a69f 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -85,7 +85,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) */ if (sdata->hw_irq_trigger && st_sensors_new_samples_available(indio_dev, sdata)) { - iio_trigger_poll_chained(p); + iio_trigger_poll_nested(p); } else { dev_dbg(indio_dev->dev.parent, "spurious IRQ\n"); return IRQ_NONE; @@ -110,7 +110,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) dev_dbg(indio_dev->dev.parent, "more samples came in during polling\n"); sdata->hw_timestamp = iio_get_time_ns(indio_dev); - iio_trigger_poll_chained(p); + iio_trigger_poll_nested(p); } return IRQ_HANDLED; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index d3f90cf86143..3acd9c3f388e 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -277,6 +277,7 @@ config CIO_DAC tristate "Measurement Computing CIO-DAC IIO driver" depends on X86 && (ISA_BUS || PC104) select ISA_BUS_API + select REGMAP_MMIO help Say yes here to build support for the Measurement Computing CIO-DAC analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 7a9b5fc1e579..076bc9ecfb49 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -124,6 +124,10 @@ static int ad5592r_gpio_request(struct gpio_chip *chip, unsigned offset) return 0; } +static const char * const ad5592r_gpio_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", +}; + static int ad5592r_gpio_init(struct ad5592r_state *st) { if (!st->gpio_map) @@ -140,6 +144,7 @@ static int ad5592r_gpio_init(struct ad5592r_state *st) st->gpiochip.set = ad5592r_gpio_set; st->gpiochip.request = ad5592r_gpio_request; st->gpiochip.owner = THIS_MODULE; + st->gpiochip.names = ad5592r_gpio_names; mutex_init(&st->gpio_lock); diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c index 18a64f72fc18..069904d00c2e 100644 --- a/drivers/iio/dac/cio-dac.c +++ b/drivers/iio/dac/cio-dac.c @@ -4,18 +4,17 @@ * Copyright (C) 2016 William Breathitt Gray * * This driver supports the following Measurement Computing devices: CIO-DAC16, - * CIO-DAC06, and PC104-DAC06. + * CIO-DAC08, and PC104-DAC06. */ -#include <linux/bitops.h> +#include <linux/bits.h> #include <linux/device.h> -#include <linux/errno.h> +#include <linux/err.h> #include <linux/iio/iio.h> #include <linux/iio/types.h> -#include <linux/io.h> -#include <linux/ioport.h> #include <linux/isa.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/regmap.h> #include <linux/types.h> #define CIO_DAC_NUM_CHAN 16 @@ -35,25 +34,51 @@ static unsigned int num_cio_dac; module_param_hw_array(base, uint, ioport, &num_cio_dac, 0); MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses"); +#define CIO_DAC_BASE 0x00 +#define CIO_DAC_CHANNEL_STRIDE 2 + +static bool cio_dac_precious_reg(struct device *dev, unsigned int reg) +{ + /* + * All registers are considered precious; if the XFER jumper is set on + * the device, then no update occurs until a DAC register is read. + */ + return true; +} + +static const struct regmap_config cio_dac_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .io_port = true, + .max_register = 0x1F, + .precious_reg = cio_dac_precious_reg, +}; + /** * struct cio_dac_iio - IIO device private data structure - * @chan_out_states: channels' output states - * @base: base memory address of the DAC device + * @map: Regmap for the device */ struct cio_dac_iio { - int chan_out_states[CIO_DAC_NUM_CHAN]; - u16 __iomem *base; + struct regmap *map; }; static int cio_dac_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct cio_dac_iio *const priv = iio_priv(indio_dev); + const unsigned int offset = chan->channel * CIO_DAC_CHANNEL_STRIDE; + int err; + unsigned int dac_val; if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; - *val = priv->chan_out_states[chan->channel]; + err = regmap_read(priv->map, CIO_DAC_BASE + offset, &dac_val); + if (err) + return err; + + *val = dac_val; return IIO_VAL_INT; } @@ -62,6 +87,7 @@ static int cio_dac_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct cio_dac_iio *const priv = iio_priv(indio_dev); + const unsigned int offset = chan->channel * CIO_DAC_CHANNEL_STRIDE; if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; @@ -70,10 +96,7 @@ static int cio_dac_write_raw(struct iio_dev *indio_dev, if ((unsigned int)val > 4095) return -EINVAL; - priv->chan_out_states[chan->channel] = val; - iowrite16(val, priv->base + chan->channel); - - return 0; + return regmap_write(priv->map, CIO_DAC_BASE + offset, val); } static const struct iio_info cio_dac_info = { @@ -92,7 +115,7 @@ static int cio_dac_probe(struct device *dev, unsigned int id) { struct iio_dev *indio_dev; struct cio_dac_iio *priv; - unsigned int i; + void __iomem *regs; indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); if (!indio_dev) @@ -105,21 +128,22 @@ static int cio_dac_probe(struct device *dev, unsigned int id) return -EBUSY; } - priv = iio_priv(indio_dev); - priv->base = devm_ioport_map(dev, base[id], CIO_DAC_EXTENT); - if (!priv->base) + regs = devm_ioport_map(dev, base[id], CIO_DAC_EXTENT); + if (!regs) return -ENOMEM; + priv = iio_priv(indio_dev); + priv->map = devm_regmap_init_mmio(dev, regs, &cio_dac_regmap_config); + if (IS_ERR(priv->map)) + return dev_err_probe(dev, PTR_ERR(priv->map), + "Unable to initialize register map\n"); + indio_dev->info = &cio_dac_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = cio_dac_channels; indio_dev->num_channels = CIO_DAC_NUM_CHAN; indio_dev->name = dev_name(dev); - /* initialize DAC outputs to 0V */ - for (i = 0; i < CIO_DAC_NUM_CHAN; i++) - iowrite16(0, priv->base + i); - return devm_iio_device_register(dev, indio_dev); } diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c index 00ba4e98fb9c..05034a306597 100644 --- a/drivers/iio/dac/max5522.c +++ b/drivers/iio/dac/max5522.c @@ -52,7 +52,7 @@ struct max5522_state { } \ } -const struct iio_chan_spec max5522_channels[] = { +static const struct iio_chan_spec max5522_channels[] = { MAX5522_CHANNEL(0), MAX5522_CHANNEL(1), }; diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index ed8167271358..9bf8337806fc 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -490,11 +490,6 @@ static int admv1013_init(struct admv1013_state *st) st->input_mode); } -static void admv1013_clk_disable(void *data) -{ - clk_disable_unprepare(data); -} - static void admv1013_reg_disable(void *data) { regulator_disable(data); @@ -559,11 +554,6 @@ static int admv1013_properties_parse(struct admv1013_state *st) return dev_err_probe(&spi->dev, PTR_ERR(st->reg), "failed to get the common-mode voltage\n"); - st->clkin = devm_clk_get(&spi->dev, "lo_in"); - if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), - "failed to get the LO input clock\n"); - return 0; } @@ -601,13 +591,10 @@ static int admv1013_probe(struct spi_device *spi) if (ret) return ret; - ret = clk_prepare_enable(st->clkin); - if (ret) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, admv1013_clk_disable, st->clkin); - if (ret) - return ret; + st->clkin = devm_clk_get_enabled(&spi->dev, "lo_in"); + if (IS_ERR(st->clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + "failed to get the LO input clock\n"); st->nb.notifier_call = admv1013_freq_change; ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); diff --git a/drivers/iio/gyro/fxas21002c_core.c b/drivers/iio/gyro/fxas21002c_core.c index 3ea1d4613080..c28d17ca6f5e 100644 --- a/drivers/iio/gyro/fxas21002c_core.c +++ b/drivers/iio/gyro/fxas21002c_core.c @@ -813,7 +813,7 @@ static irqreturn_t fxas21002c_data_rdy_thread(int irq, void *private) if (!data_ready) return IRQ_NONE; - iio_trigger_poll_chained(data->dready_trig); + iio_trigger_poll_nested(data->dready_trig); return IRQ_HANDLED; } diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 6a6d84a3deda..a791ba3a693a 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -939,7 +939,7 @@ static irqreturn_t mpu3050_irq_thread(int irq, void *p) if (!(val & MPU3050_INT_STATUS_RAW_RDY)) return IRQ_NONE; - iio_trigger_poll_chained(p); + iio_trigger_poll_nested(p); return IRQ_HANDLED; } diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 2a4107a79662..11ef38994a95 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -68,7 +68,7 @@ static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) if (!(status & HTS221_RH_DRDY_MASK)) return IRQ_NONE; - iio_trigger_poll_chained(hw->trig); + iio_trigger_poll_nested(hw->trig); return IRQ_HANDLED; } diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index aec55f7e1f26..3abffb01ba31 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -326,11 +326,11 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) /* * This is not an hard requirement but it's not advised to run the IMU - * with a sample rate lower than 4000Hz due to possible undersampling + * with a sample rate lower than 1900Hz due to possible undersampling * issues. However, there are users that might really want to take the risk. * Hence, we provide a module parameter for them. If set, we allow sample - * rates lower than 4KHz. By default, we won't allow this and we just roundup - * the rate to the next multiple of the input clock bigger than 4KHz. This + * rates lower than 1.9KHz. By default, we won't allow this and we just roundup + * the rate to the next multiple of the input clock bigger than 1.9KHz. This * is done like this as in some cases (when DEC_RATE is 0) might give * us the closest value to the one desired by the user... */ diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index 8c16cdacf2f2..5865a295a4df 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -14,8 +14,8 @@ config IIO_ST_LSM6DSX sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, ism330dlc, lsm6dso, lsm6dsox, asm330lhh, asm330lhhx, lsm6dsr, lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, lsm6dstx, - lsm6dsv, lsm6dsv16x, lsm6dso16is, ism330is, lsm6dst and the - accelerometer/gyroscope of lsm9ds1. + lsm6dsv, lsm6dsv16x, lsm6dso16is, ism330is, asm330lhb, lsm6dst + and the accelerometer/gyroscope of lsm9ds1. To compile this driver as a module, choose M here: the module will be called st_lsm6dsx. diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 499fcf8875b4..c19237717e81 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -37,9 +37,10 @@ #define ST_LSM6DSV16X_DEV_NAME "lsm6dsv16x" #define ST_LSM6DSO16IS_DEV_NAME "lsm6dso16is" #define ST_ISM330IS_DEV_NAME "ism330is" +#define ST_ASM330LHB_DEV_NAME "asm330lhb" enum st_lsm6dsx_hw_id { - ST_LSM6DS3_ID, + ST_LSM6DS3_ID = 1, ST_LSM6DS3H_ID, ST_LSM6DSL_ID, ST_LSM6DSM_ID, @@ -61,6 +62,7 @@ enum st_lsm6dsx_hw_id { ST_LSM6DSV16X_ID, ST_LSM6DSO16IS_ID, ST_ISM330IS_ID, + ST_ASM330LHB_ID, ST_LSM6DSX_MAX_ID, }; @@ -137,6 +139,13 @@ struct st_lsm6dsx_odr_table_entry { int odr_len; }; +struct st_lsm6dsx_samples_to_discard { + struct { + u32 milli_hz; + u16 samples; + } val[ST_LSM6DSX_ODR_LIST_SIZE]; +}; + struct st_lsm6dsx_fs { u32 gain; u8 val; @@ -291,6 +300,7 @@ struct st_lsm6dsx_ext_dev_settings { * @irq_config: interrupts related registers. * @drdy_mask: register info for data-ready mask (addr + mask). * @odr_table: Hw sensors odr table (Hz + val). + * @samples_to_discard: Number of samples to discard for filters settling time. * @fs_table: Hw sensors gain table (gain + val). * @decimator: List of decimator register info (addr + mask). * @batch: List of FIFO batching register info (addr + mask). @@ -323,6 +333,7 @@ struct st_lsm6dsx_settings { } irq_config; struct st_lsm6dsx_reg drdy_mask; struct st_lsm6dsx_odr_table_entry odr_table[2]; + struct st_lsm6dsx_samples_to_discard samples_to_discard[2]; struct st_lsm6dsx_fs_table_entry fs_table[2]; struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID]; @@ -353,6 +364,7 @@ enum st_lsm6dsx_fifo_mode { * @hw: Pointer to instance of struct st_lsm6dsx_hw. * @gain: Configured sensor sensitivity. * @odr: Output data rate of the sensor [Hz]. + * @samples_to_discard: Number of samples to discard for filters settling time. * @watermark: Sensor watermark level. * @decimator: Sensor decimation factor. * @sip: Number of samples in a given pattern. @@ -367,6 +379,7 @@ struct st_lsm6dsx_sensor { u32 gain; u32 odr; + u16 samples_to_discard; u16 watermark; u8 decimator; u8 sip; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 7dd5205aea5b..066fe561c5e8 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -15,7 +15,7 @@ * value of the decimation factor and ODR set for each FIFO data set. * * LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/LSM6DSRX/ISM330DHCX/ - * LSM6DST/LSM6DSOP/LSM6DSTX/LSM6DSV: + * LSM6DST/LSM6DSOP/LSM6DSTX/LSM6DSV/ASM330LHB: * The FIFO buffer can be configured to store data from gyroscope and * accelerometer. Each sample is queued with a tag (1B) indicating data * source (gyroscope, accelerometer, hw timer). @@ -457,17 +457,31 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) } if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) { - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_GYRO], - &hw->scan[ST_LSM6DSX_ID_GYRO], - gyro_sensor->ts_ref + ts); + /* + * We need to discards gyro samples during + * filters settling time + */ + if (gyro_sensor->samples_to_discard > 0) + gyro_sensor->samples_to_discard--; + else + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_GYRO], + &hw->scan[ST_LSM6DSX_ID_GYRO], + gyro_sensor->ts_ref + ts); gyro_sip--; } if (acc_sip > 0 && !(sip % acc_sensor->decimator)) { - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_ACC], - &hw->scan[ST_LSM6DSX_ID_ACC], - acc_sensor->ts_ref + ts); + /* + * We need to discards accel samples during + * filters settling time + */ + if (acc_sensor->samples_to_discard > 0) + acc_sensor->samples_to_discard--; + else + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_ACC], + &hw->scan[ST_LSM6DSX_ID_ACC], + acc_sensor->ts_ref + ts); acc_sip--; } if (ext_sip > 0 && !(sip % ext_sensor->decimator)) { @@ -654,6 +668,30 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) return err; } +static void +st_lsm6dsx_update_samples_to_discard(struct st_lsm6dsx_sensor *sensor) +{ + const struct st_lsm6dsx_samples_to_discard *data; + struct st_lsm6dsx_hw *hw = sensor->hw; + int i; + + if (sensor->id != ST_LSM6DSX_ID_GYRO && + sensor->id != ST_LSM6DSX_ID_ACC) + return; + + /* check if drdy mask is supported in hw */ + if (hw->settings->drdy_mask.addr) + return; + + data = &hw->settings->samples_to_discard[sensor->id]; + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) { + if (data->val[i].milli_hz == sensor->odr) { + sensor->samples_to_discard = data->val[i].samples; + return; + } + } +} + int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) { struct st_lsm6dsx_hw *hw = sensor->hw; @@ -673,6 +711,9 @@ int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) goto out; } + if (enable) + st_lsm6dsx_update_samples_to_discard(sensor); + err = st_lsm6dsx_device_set_enable(sensor, enable); 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 3f6060c64f32..6a18b363cf73 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -56,6 +56,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/acpi.h> #include <linux/delay.h> #include <linux/iio/events.h> #include <linux/iio/iio.h> @@ -634,6 +635,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_len = 4, }, }, + .samples_to_discard = { + [ST_LSM6DSX_ID_ACC] = { + .val[0] = { 12500, 1 }, + .val[1] = { 26000, 1 }, + .val[2] = { 52000, 1 }, + .val[3] = { 104000, 2 }, + .val[4] = { 208000, 2 }, + .val[5] = { 416000, 2 }, + }, + [ST_LSM6DSX_ID_GYRO] = { + .val[0] = { 12500, 2 }, + .val[1] = { 26000, 5 }, + .val[2] = { 52000, 7 }, + .val[3] = { 104000, 12 }, + .val[4] = { 208000, 20 }, + .val[5] = { 416000, 36 }, + }, + }, .irq_config = { .irq1 = { .addr = 0x0d, @@ -1014,6 +1033,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .hw_id = ST_LSM6DSOP_ID, .name = ST_LSM6DSOP_DEV_NAME, .wai = 0x6c, + }, { + .hw_id = ST_ASM330LHB_ID, + .name = ST_ASM330LHB_DEV_NAME, + .wai = 0x6b, }, }, .channels = { @@ -2602,6 +2625,73 @@ static int st_lsm6dsx_init_regulators(struct device *dev) return 0; } +#ifdef CONFIG_ACPI + +static int lsm6dsx_get_acpi_mount_matrix(struct device *dev, + struct iio_mount_matrix *orientation) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_device *adev = ACPI_COMPANION(dev); + union acpi_object *obj, *elements; + acpi_status status; + int i, j, val[3]; + char *str; + + if (!has_acpi_companion(dev)) + return -EINVAL; + + if (!acpi_has_method(adev->handle, "ROTM")) + return -EINVAL; + + status = acpi_evaluate_object(adev->handle, "ROTM", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_warn(dev, "Failed to get ACPI mount matrix: %d\n", status); + return -EINVAL; + } + + obj = buffer.pointer; + if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) + goto unknown_format; + + elements = obj->package.elements; + for (i = 0; i < 3; i++) { + if (elements[i].type != ACPI_TYPE_STRING) + goto unknown_format; + + str = elements[i].string.pointer; + if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) + goto unknown_format; + + for (j = 0; j < 3; j++) { + switch (val[j]) { + case -1: str = "-1"; break; + case 0: str = "0"; break; + case 1: str = "1"; break; + default: goto unknown_format; + } + orientation->rotation[i * 3 + j] = str; + } + } + + kfree(buffer.pointer); + return 0; + +unknown_format: + dev_warn(dev, "Unknown ACPI mount matrix format, ignoring\n"); + kfree(buffer.pointer); + return -EINVAL; +} + +#else + +static int lsm6dsx_get_acpi_mount_matrix(struct device *dev, + struct iio_mount_matrix *orientation) +{ + return false; +} + +#endif + int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, struct regmap *regmap) { @@ -2676,9 +2766,12 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } - err = iio_read_mount_matrix(hw->dev, &hw->orientation); - if (err) - return err; + err = lsm6dsx_get_acpi_mount_matrix(hw->dev, &hw->orientation); + if (err) { + err = iio_read_mount_matrix(hw->dev, &hw->orientation); + if (err) + return err; + } for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { if (!hw->iio_devs[i]) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index df5f60925260..020717f92363 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -23,10 +23,15 @@ static const struct regmap_config st_lsm6dsx_i2c_regmap_config = { static int st_lsm6dsx_i2c_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); - int hw_id = id->driver_data; + int hw_id; struct regmap *regmap; + hw_id = (kernel_ulong_t)device_get_match_data(&client->dev); + if (!hw_id) + hw_id = i2c_client_get_device_id(client)->driver_data; + if (!hw_id) + return -EINVAL; + regmap = devm_regmap_init_i2c(client, &st_lsm6dsx_i2c_regmap_config); if (IS_ERR(regmap)) { dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); @@ -125,10 +130,20 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,ism330is", .data = (void *)ST_ISM330IS_ID, }, + { + .compatible = "st,asm330lhb", + .data = (void *)ST_ASM330LHB_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); +static const struct acpi_device_id st_lsm6dsx_i2c_acpi_match[] = { + { "SMO8B30", ST_LSM6DS3TRC_ID, }, + {} +}; +MODULE_DEVICE_TABLE(acpi, st_lsm6dsx_i2c_acpi_match); + static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, @@ -152,6 +167,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, + { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); @@ -161,6 +177,7 @@ static struct i2c_driver st_lsm6dsx_driver = { .name = "st_lsm6dsx_i2c", .pm = pm_sleep_ptr(&st_lsm6dsx_pm_ops), .of_match_table = st_lsm6dsx_i2c_of_match, + .acpi_match_table = st_lsm6dsx_i2c_acpi_match, }, .probe_new = 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 974584bda875..f56c170c41a9 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -125,6 +125,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,ism330is", .data = (void *)ST_ISM330IS_ID, }, + { + .compatible = "st,asm330lhb", + .data = (void *)ST_ASM330LHB_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -152,6 +156,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, + { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c new file mode 100644 index 000000000000..8bb68975b259 --- /dev/null +++ b/drivers/iio/industrialio-gts-helper.c @@ -0,0 +1,1077 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* gain-time-scale conversion helpers for IIO light sensors + * + * Copyright (c) 2023 Matti Vaittinen <mazziesaccount@gmail.com> + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/iio-gts-helper.h> +#include <linux/iio/types.h> + +/** + * iio_gts_get_gain - Convert scale to total gain + * + * Internal helper for converting scale to total gain. + * + * @max: Maximum linearized scale. As an example, when scale is created + * in magnitude of NANOs and max scale is 64.1 - The linearized + * scale is 64 100 000 000. + * @scale: Linearized scale to compute the gain for. + * + * Return: (floored) gain corresponding to the scale. -EINVAL if scale + * is invalid. + */ +static int iio_gts_get_gain(const u64 max, const u64 scale) +{ + u64 full = max; + int tmp = 1; + + if (scale > full || !scale) + return -EINVAL; + + if (U64_MAX - full < scale) { + /* Risk of overflow */ + if (full - scale < scale) + return 1; + + full -= scale; + tmp++; + } + + while (full > scale * (u64)tmp) + tmp++; + + return tmp; +} + +/** + * gain_get_scale_fraction - get the gain or time based on scale and known one + * + * @max: Maximum linearized scale. As an example, when scale is created + * in magnitude of NANOs and max scale is 64.1 - The linearized + * scale is 64 100 000 000. + * @scale: Linearized scale to compute the gain/time for. + * @known: Either integration time or gain depending on which one is known + * @unknown: Pointer to variable where the computed gain/time is stored + * + * Internal helper for computing unknown fraction of total gain. + * Compute either gain or time based on scale and either the gain or time + * depending on which one is known. + * + * Return: 0 on success. + */ +static int gain_get_scale_fraction(const u64 max, u64 scale, int known, + int *unknown) +{ + int tot_gain; + + tot_gain = iio_gts_get_gain(max, scale); + if (tot_gain < 0) + return tot_gain; + + *unknown = tot_gain / known; + + /* We require total gain to be exact multiple of known * unknown */ + if (!*unknown || *unknown * known != tot_gain) + return -EINVAL; + + return 0; +} + +static int iio_gts_delinearize(u64 lin_scale, unsigned long scaler, + int *scale_whole, int *scale_nano) +{ + int frac; + + if (scaler > NANO) + return -EOVERFLOW; + + if (!scaler) + return -EINVAL; + + frac = do_div(lin_scale, scaler); + + *scale_whole = lin_scale; + *scale_nano = frac * (NANO / scaler); + + return 0; +} + +static int iio_gts_linearize(int scale_whole, int scale_nano, + unsigned long scaler, u64 *lin_scale) +{ + /* + * Expect scale to be (mostly) NANO or MICRO. Divide divider instead of + * multiplication followed by division to avoid overflow. + */ + if (scaler > NANO || !scaler) + return -EINVAL; + + *lin_scale = (u64)scale_whole * (u64)scaler + + (u64)(scale_nano / (NANO / scaler)); + + return 0; +} + +/** + * iio_gts_total_gain_to_scale - convert gain to scale + * @gts: Gain time scale descriptor + * @total_gain: the gain to be converted + * @scale_int: Pointer to integral part of the scale (typically val1) + * @scale_nano: Pointer to fractional part of the scale (nano or ppb) + * + * Convert the total gain value to scale. NOTE: This does not separate gain + * generated by HW-gain or integration time. It is up to caller to decide what + * part of the total gain is due to integration time and what due to HW-gain. + * + * Return: 0 on success. Negative errno on failure. + */ +int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain, + int *scale_int, int *scale_nano) +{ + u64 tmp; + + tmp = gts->max_scale; + + do_div(tmp, total_gain); + + return iio_gts_delinearize(tmp, NANO, scale_int, scale_nano); +} +EXPORT_SYMBOL_NS_GPL(iio_gts_total_gain_to_scale, IIO_GTS_HELPER); + +/** + * iio_gts_purge_avail_scale_table - free-up the available scale tables + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_scale_table(). + */ +static void iio_gts_purge_avail_scale_table(struct iio_gts *gts) +{ + int i; + + if (gts->per_time_avail_scale_tables) { + for (i = 0; i < gts->num_itime; i++) + kfree(gts->per_time_avail_scale_tables[i]); + + kfree(gts->per_time_avail_scale_tables); + gts->per_time_avail_scale_tables = NULL; + } + + kfree(gts->avail_all_scales_table); + gts->avail_all_scales_table = NULL; + + gts->num_avail_all_scales = 0; +} + +static int iio_gts_gain_cmp(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales) +{ + int ret, i, j, new_idx, time_idx; + int *all_gains; + size_t gain_bytes; + + for (i = 0; i < gts->num_itime; i++) { + /* + * Sort the tables for nice output and for easier finding of + * unique values. + */ + sort(gains[i], gts->num_hwgain, sizeof(int), iio_gts_gain_cmp, + NULL); + + /* Convert gains to scales */ + for (j = 0; j < gts->num_hwgain; j++) { + ret = iio_gts_total_gain_to_scale(gts, gains[i][j], + &scales[i][2 * j], + &scales[i][2 * j + 1]); + if (ret) + return ret; + } + } + + gain_bytes = array_size(gts->num_hwgain, sizeof(int)); + all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL); + if (!all_gains) + return -ENOMEM; + + /* + * We assume all the gains for same integration time were unique. + * It is likely the first time table had greatest time multiplier as + * the times are in the order of preference and greater times are + * usually preferred. Hence we start from the last table which is likely + * to have the smallest total gains. + */ + time_idx = gts->num_itime - 1; + memcpy(all_gains, gains[time_idx], gain_bytes); + new_idx = gts->num_hwgain; + + while (time_idx--) { + for (j = 0; j < gts->num_hwgain; j++) { + int candidate = gains[time_idx][j]; + int chk; + + if (candidate > all_gains[new_idx - 1]) { + all_gains[new_idx] = candidate; + new_idx++; + + continue; + } + for (chk = 0; chk < new_idx; chk++) + if (candidate <= all_gains[chk]) + break; + + if (candidate == all_gains[chk]) + continue; + + memmove(&all_gains[chk + 1], &all_gains[chk], + (new_idx - chk) * sizeof(int)); + all_gains[chk] = candidate; + new_idx++; + } + } + + gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int), + GFP_KERNEL); + if (!gts->avail_all_scales_table) { + ret = -ENOMEM; + goto free_out; + } + gts->num_avail_all_scales = new_idx; + + for (i = 0; i < gts->num_avail_all_scales; i++) { + ret = iio_gts_total_gain_to_scale(gts, all_gains[i], + >s->avail_all_scales_table[i * 2], + >s->avail_all_scales_table[i * 2 + 1]); + + if (ret) { + kfree(gts->avail_all_scales_table); + gts->num_avail_all_scales = 0; + goto free_out; + } + } + +free_out: + kfree(all_gains); + + return ret; +} + +/** + * iio_gts_build_avail_scale_table - create tables of available scales + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales based on the + * originally given gain and time tables. When both time and gain tables are + * given this results: + * 1. A set of tables representing available scales for each supported + * integration time. + * 2. A single table listing all the unique scales that any combination of + * supported gains and times can provide. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_scale_table() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_scale_table(struct iio_gts *gts) +{ + int **per_time_gains, **per_time_scales, i, j, ret = -ENOMEM; + + per_time_gains = kcalloc(gts->num_itime, sizeof(*per_time_gains), GFP_KERNEL); + if (!per_time_gains) + return ret; + + per_time_scales = kcalloc(gts->num_itime, sizeof(*per_time_scales), GFP_KERNEL); + if (!per_time_scales) + goto free_gains; + + for (i = 0; i < gts->num_itime; i++) { + per_time_scales[i] = kcalloc(gts->num_hwgain, 2 * sizeof(int), + GFP_KERNEL); + if (!per_time_scales[i]) + goto err_free_out; + + per_time_gains[i] = kcalloc(gts->num_hwgain, sizeof(int), + GFP_KERNEL); + if (!per_time_gains[i]) { + kfree(per_time_scales[i]); + goto err_free_out; + } + + for (j = 0; j < gts->num_hwgain; j++) + per_time_gains[i][j] = gts->hwgain_table[j].gain * + gts->itime_table[i].mul; + } + + ret = gain_to_scaletables(gts, per_time_gains, per_time_scales); + if (ret) + goto err_free_out; + + kfree(per_time_gains); + gts->per_time_avail_scale_tables = per_time_scales; + + return 0; + +err_free_out: + for (i--; i; i--) { + kfree(per_time_scales[i]); + kfree(per_time_gains[i]); + } + kfree(per_time_scales); +free_gains: + kfree(per_time_gains); + + return ret; +} + +/** + * iio_gts_build_avail_time_table - build table of available integration times + * @gts: Gain time scale descriptor + * + * Build the table which can represent the available times to be returned + * to users using the read_avail-callback. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_time_table() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_time_table(struct iio_gts *gts) +{ + int *times, i, j, idx = 0; + + if (!gts->num_itime) + return 0; + + times = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL); + if (!times) + return -ENOMEM; + + /* Sort times from all tables to one and remove duplicates */ + for (i = gts->num_itime - 1; i >= 0; i--) { + int new = gts->itime_table[i].time_us; + + if (times[idx] < new) { + times[idx++] = new; + continue; + } + + for (j = 0; j <= idx; j++) { + if (times[j] > new) { + memmove(×[j + 1], ×[j], + (idx - j) * sizeof(int)); + times[j] = new; + idx++; + } + } + } + gts->avail_time_tables = times; + /* + * This is just to survive a unlikely corner-case where times in the + * given time table were not unique. Else we could just trust the + * gts->num_itime. + */ + gts->num_avail_time_tables = idx; + + return 0; +} + +/** + * iio_gts_purge_avail_time_table - free-up the available integration time table + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_time_table(). + */ +static void iio_gts_purge_avail_time_table(struct iio_gts *gts) +{ + if (gts->num_avail_time_tables) { + kfree(gts->avail_time_tables); + gts->avail_time_tables = NULL; + gts->num_avail_time_tables = 0; + } +} + +/** + * iio_gts_build_avail_tables - create tables of available scales and int times + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales and available + * integration times. Availability tables are built based on the originally + * given gain and given time tables. + * + * When both time and gain tables are + * given this results: + * 1. A set of sorted tables representing available scales for each supported + * integration time. + * 2. A single sorted table listing all the unique scales that any combination + * of supported gains and times can provide. + * 3. A sorted table of supported integration times + * + * After these tables are built one can use the iio_gts_all_avail_scales(), + * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to + * implement the read_avail operations. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_tables() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_tables(struct iio_gts *gts) +{ + int ret; + + ret = iio_gts_build_avail_scale_table(gts); + if (ret) + return ret; + + ret = iio_gts_build_avail_time_table(gts); + if (ret) + iio_gts_purge_avail_scale_table(gts); + + return ret; +} + +/** + * iio_gts_purge_avail_tables - free-up the availability tables + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_tables(). Frees both the + * integration time and scale tables. + */ +static void iio_gts_purge_avail_tables(struct iio_gts *gts) +{ + iio_gts_purge_avail_time_table(gts); + iio_gts_purge_avail_scale_table(gts); +} + +static void devm_iio_gts_avail_all_drop(void *res) +{ + iio_gts_purge_avail_tables(res); +} + +/** + * devm_iio_gts_build_avail_tables - manged add availability tables + * @dev: Pointer to the device whose lifetime tables are bound + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales and available + * integration times. Availability tables are built based on the originally + * given gain and given time tables. + * + * When both time and gain tables are given this results: + * 1. A set of sorted tables representing available scales for each supported + * integration time. + * 2. A single sorted table listing all the unique scales that any combination + * of supported gains and times can provide. + * 3. A sorted table of supported integration times + * + * After these tables are built one can use the iio_gts_all_avail_scales(), + * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to + * implement the read_avail operations. + * + * The tables are automatically released upon device detach. + * + * Return: 0 on success. + */ +static int devm_iio_gts_build_avail_tables(struct device *dev, + struct iio_gts *gts) +{ + int ret; + + ret = iio_gts_build_avail_tables(gts); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, devm_iio_gts_avail_all_drop, gts); +} + +static int sanity_check_time(const struct iio_itime_sel_mul *t) +{ + if (t->sel < 0 || t->time_us < 0 || t->mul <= 0) + return -EINVAL; + + return 0; +} + +static int sanity_check_gain(const struct iio_gain_sel_pair *g) +{ + if (g->sel < 0 || g->gain <= 0) + return -EINVAL; + + return 0; +} + +static int iio_gts_sanity_check(struct iio_gts *gts) +{ + int g, t, ret; + + if (!gts->num_hwgain && !gts->num_itime) + return -EINVAL; + + for (t = 0; t < gts->num_itime; t++) { + ret = sanity_check_time(>s->itime_table[t]); + if (ret) + return ret; + } + + for (g = 0; g < gts->num_hwgain; g++) { + ret = sanity_check_gain(>s->hwgain_table[g]); + if (ret) + return ret; + } + + for (g = 0; g < gts->num_hwgain; g++) { + for (t = 0; t < gts->num_itime; t++) { + int gain, mul, res; + + gain = gts->hwgain_table[g].gain; + mul = gts->itime_table[t].mul; + + if (check_mul_overflow(gain, mul, &res)) + return -EOVERFLOW; + } + } + + return 0; +} + +static int iio_init_iio_gts(int max_scale_int, int max_scale_nano, + const struct iio_gain_sel_pair *gain_tbl, int num_gain, + const struct iio_itime_sel_mul *tim_tbl, int num_times, + struct iio_gts *gts) +{ + int ret; + + memset(gts, 0, sizeof(*gts)); + + ret = iio_gts_linearize(max_scale_int, max_scale_nano, NANO, + >s->max_scale); + if (ret) + return ret; + + gts->hwgain_table = gain_tbl; + gts->num_hwgain = num_gain; + gts->itime_table = tim_tbl; + gts->num_itime = num_times; + + return iio_gts_sanity_check(gts); +} + +/** + * devm_iio_init_iio_gts - Initialize the gain-time-scale helper + * @dev: Pointer to the device whose lifetime gts resources are + * bound + * @max_scale_int: integer part of the maximum scale value + * @max_scale_nano: fraction part of the maximum scale value + * @gain_tbl: table describing supported gains + * @num_gain: number of gains in the gain table + * @tim_tbl: table describing supported integration times. Provide + * the integration time table sorted so that the preferred + * integration time is in the first array index. The search + * functions like the + * iio_gts_find_time_and_gain_sel_for_scale() start search + * from first provided time. + * @num_times: number of times in the time table + * @gts: pointer to the helper struct + * + * Initialize the gain-time-scale helper for use. Note, gains, times, selectors + * and multipliers must be positive. Negative values are reserved for error + * checking. The total gain (maximum gain * maximum time multiplier) must not + * overflow int. The allocated resources will be released upon device detach. + * + * Return: 0 on success. + */ +int devm_iio_init_iio_gts(struct device *dev, int max_scale_int, int max_scale_nano, + const struct iio_gain_sel_pair *gain_tbl, int num_gain, + const struct iio_itime_sel_mul *tim_tbl, int num_times, + struct iio_gts *gts) +{ + int ret; + + ret = iio_init_iio_gts(max_scale_int, max_scale_nano, gain_tbl, + num_gain, tim_tbl, num_times, gts); + if (ret) + return ret; + + return devm_iio_gts_build_avail_tables(dev, gts); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_init_iio_gts, IIO_GTS_HELPER); + +/** + * iio_gts_all_avail_scales - helper for listing all available scales + * @gts: Gain time scale descriptor + * @vals: Returned array of supported scales + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type, + int *length) +{ + if (!gts->num_avail_all_scales) + return -EINVAL; + + *vals = gts->avail_all_scales_table; + *type = IIO_VAL_INT_PLUS_NANO; + *length = gts->num_avail_all_scales * 2; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_all_avail_scales, IIO_GTS_HELPER); + +/** + * iio_gts_avail_scales_for_time - list scales for integration time + * @gts: Gain time scale descriptor + * @time: Integration time for which the scales are listed + * @vals: Returned array of supported scales + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Drivers which do not allow scale setting to change integration time can + * use this helper to list only the scales which are valid for given integration + * time. + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time, + const int **vals, int *type, int *length) +{ + int i; + + for (i = 0; i < gts->num_itime; i++) + if (gts->itime_table[i].time_us == time) + break; + + if (i == gts->num_itime) + return -EINVAL; + + *vals = gts->per_time_avail_scale_tables[i]; + *type = IIO_VAL_INT_PLUS_NANO; + *length = gts->num_hwgain * 2; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_avail_scales_for_time, IIO_GTS_HELPER); + +/** + * iio_gts_avail_times - helper for listing available integration times + * @gts: Gain time scale descriptor + * @vals: Returned array of supported times + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type, + int *length) +{ + if (!gts->num_avail_time_tables) + return -EINVAL; + + *vals = gts->avail_time_tables; + *type = IIO_VAL_INT; + *length = gts->num_avail_time_tables; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_avail_times, IIO_GTS_HELPER); + +/** + * iio_gts_find_sel_by_gain - find selector corresponding to a HW-gain + * @gts: Gain time scale descriptor + * @gain: HW-gain for which matching selector is searched for + * + * Return: a selector matching given HW-gain or -EINVAL if selector was + * not found. + */ +int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain) +{ + int i; + + for (i = 0; i < gts->num_hwgain; i++) + if (gts->hwgain_table[i].gain == gain) + return gts->hwgain_table[i].sel; + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_sel_by_gain, IIO_GTS_HELPER); + +/** + * iio_gts_find_gain_by_sel - find HW-gain corresponding to a selector + * @gts: Gain time scale descriptor + * @sel: selector for which matching HW-gain is searched for + * + * Return: a HW-gain matching given selector or -EINVAL if HW-gain was not + * found. + */ +int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel) +{ + int i; + + for (i = 0; i < gts->num_hwgain; i++) + if (gts->hwgain_table[i].sel == sel) + return gts->hwgain_table[i].gain; + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_by_sel, IIO_GTS_HELPER); + +/** + * iio_gts_get_min_gain - find smallest valid HW-gain + * @gts: Gain time scale descriptor + * + * Return: The smallest HW-gain -EINVAL if no HW-gains were in the tables. + */ +int iio_gts_get_min_gain(struct iio_gts *gts) +{ + int i, min = -EINVAL; + + for (i = 0; i < gts->num_hwgain; i++) { + int gain = gts->hwgain_table[i].gain; + + if (min == -EINVAL) + min = gain; + else + min = min(min, gain); + } + + return min; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_get_min_gain, IIO_GTS_HELPER); + +/** + * iio_find_closest_gain_low - Find the closest lower matching gain + * @gts: Gain time scale descriptor + * @gain: HW-gain for which the closest match is searched + * @in_range: indicate if the @gain was actually in the range of + * supported gains. + * + * Search for closest supported gain that is lower than or equal to the + * gain given as a parameter. This is usable for drivers which do not require + * user to request exact matching gain but rather for rounding to a supported + * gain value which is equal or lower (setting lower gain is typical for + * avoiding saturation) + * + * Return: The closest matching supported gain or -EINVAL if @gain + * was smaller than the smallest supported gain. + */ +int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range) +{ + int i, diff = 0; + int best = -1; + + *in_range = false; + + for (i = 0; i < gts->num_hwgain; i++) { + if (gain == gts->hwgain_table[i].gain) { + *in_range = true; + return gain; + } + + if (gain > gts->hwgain_table[i].gain) { + if (!diff) { + diff = gain - gts->hwgain_table[i].gain; + best = i; + } else { + int tmp = gain - gts->hwgain_table[i].gain; + + if (tmp < diff) { + diff = tmp; + best = i; + } + } + } else { + /* + * We found valid HW-gain which is greater than + * reference. So, unless we return a failure below we + * will have found an in-range gain + */ + *in_range = true; + } + } + /* The requested gain was smaller than anything we support */ + if (!diff) { + *in_range = false; + + return -EINVAL; + } + + return gts->hwgain_table[best].gain; +} +EXPORT_SYMBOL_NS_GPL(iio_find_closest_gain_low, IIO_GTS_HELPER); + +static int iio_gts_get_int_time_gain_multiplier_by_sel(struct iio_gts *gts, + int sel) +{ + const struct iio_itime_sel_mul *time; + + time = iio_gts_find_itime_by_sel(gts, sel); + if (!time) + return -EINVAL; + + return time->mul; +} + +/** + * iio_gts_find_gain_for_scale_using_time - Find gain by time and scale + * @gts: Gain time scale descriptor + * @time_sel: Integration time selector corresponding to the time gain is + * searched for + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * @gain: Pointer to value where gain is stored. + * + * In some cases the light sensors may want to find a gain setting which + * corresponds given scale and integration time. Sensors which fill the + * gain and time tables may use this helper to retrieve the gain. + * + * Return: 0 on success. -EINVAL if gain matching the parameters is not + * found. + */ +static int iio_gts_find_gain_for_scale_using_time(struct iio_gts *gts, int time_sel, + int scale_int, int scale_nano, + int *gain) +{ + u64 scale_linear; + int ret, mul; + + ret = iio_gts_linearize(scale_int, scale_nano, NANO, &scale_linear); + if (ret) + return ret; + + ret = iio_gts_get_int_time_gain_multiplier_by_sel(gts, time_sel); + if (ret < 0) + return ret; + + mul = ret; + + ret = gain_get_scale_fraction(gts->max_scale, scale_linear, mul, gain); + if (ret) + return ret; + + if (!iio_gts_valid_gain(gts, *gain)) + return -EINVAL; + + return 0; +} + +/** + * iio_gts_find_gain_sel_for_scale_using_time - Fetch gain selector. + * @gts: Gain time scale descriptor + * @time_sel: Integration time selector corresponding to the time gain is + * searched for + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * @gain_sel: Pointer to value where gain selector is stored. + * + * See iio_gts_find_gain_for_scale_using_time() for more information + */ +int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel, + int scale_int, int scale_nano, + int *gain_sel) +{ + int gain, ret; + + ret = iio_gts_find_gain_for_scale_using_time(gts, time_sel, scale_int, + scale_nano, &gain); + if (ret) + return ret; + + ret = iio_gts_find_sel_by_gain(gts, gain); + if (ret < 0) + return ret; + + *gain_sel = ret; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, IIO_GTS_HELPER); + +static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) +{ + const struct iio_itime_sel_mul *itime; + + if (!iio_gts_valid_gain(gts, gain)) + return -EINVAL; + + if (!gts->num_itime) + return gain; + + itime = iio_gts_find_itime_by_time(gts, time); + if (!itime) + return -EINVAL; + + return gain * itime->mul; +} + +static int iio_gts_get_scale_linear(struct iio_gts *gts, int gain, int time, + u64 *scale) +{ + int total_gain; + u64 tmp; + + total_gain = iio_gts_get_total_gain(gts, gain, time); + if (total_gain < 0) + return total_gain; + + tmp = gts->max_scale; + + do_div(tmp, total_gain); + + *scale = tmp; + + return 0; +} + +/** + * iio_gts_get_scale - get scale based on integration time and HW-gain + * @gts: Gain time scale descriptor + * @gain: HW-gain for which the scale is computed + * @time: Integration time for which the scale is computed + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * + * Compute scale matching the integration time and HW-gain given as parameter. + * + * Return: 0 on success. + */ +int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int, + int *scale_nano) +{ + u64 lin_scale; + int ret; + + ret = iio_gts_get_scale_linear(gts, gain, time, &lin_scale); + if (ret) + return ret; + + return iio_gts_delinearize(lin_scale, NANO, scale_int, scale_nano); +} +EXPORT_SYMBOL_NS_GPL(iio_gts_get_scale, IIO_GTS_HELPER); + +/** + * iio_gts_find_new_gain_sel_by_old_gain_time - compensate for time change + * @gts: Gain time scale descriptor + * @old_gain: Previously set gain + * @old_time_sel: Selector corresponding previously set time + * @new_time_sel: Selector corresponding new time to be set + * @new_gain: Pointer to value where new gain is to be written + * + * We may want to mitigate the scale change caused by setting a new integration + * time (for a light sensor) by also updating the (HW)gain. This helper computes + * new gain value to maintain the scale with new integration time. + * + * Return: 0 if an exactly matching supported new gain was found. When a + * non-zero value is returned, the @new_gain will be set to a negative or + * positive value. The negative value means that no gain could be computed. + * Positive value will be the "best possible new gain there could be". There + * can be two reasons why finding the "best possible" new gain is not deemed + * successful. 1) This new value cannot be supported by the hardware. 2) The new + * gain required to maintain the scale would not be an integer. In this case, + * the "best possible" new gain will be a floored optimal gain, which may or + * may not be supported by the hardware. + */ +int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, + int old_gain, int old_time_sel, + int new_time_sel, int *new_gain) +{ + const struct iio_itime_sel_mul *itime_old, *itime_new; + u64 scale; + int ret; + + *new_gain = -1; + + itime_old = iio_gts_find_itime_by_sel(gts, old_time_sel); + if (!itime_old) + return -EINVAL; + + itime_new = iio_gts_find_itime_by_sel(gts, new_time_sel); + if (!itime_new) + return -EINVAL; + + ret = iio_gts_get_scale_linear(gts, old_gain, itime_old->time_us, + &scale); + if (ret) + return ret; + + ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul, + new_gain); + if (ret) + return ret; + + if (!iio_gts_valid_gain(gts, *new_gain)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_sel_by_old_gain_time, IIO_GTS_HELPER); + +/** + * iio_gts_find_new_gain_by_old_gain_time - compensate for time change + * @gts: Gain time scale descriptor + * @old_gain: Previously set gain + * @old_time: Selector corresponding previously set time + * @new_time: Selector corresponding new time to be set + * @new_gain: Pointer to value where new gain is to be written + * + * We may want to mitigate the scale change caused by setting a new integration + * time (for a light sensor) by also updating the (HW)gain. This helper computes + * new gain value to maintain the scale with new integration time. + * + * Return: 0 if an exactly matching supported new gain was found. When a + * non-zero value is returned, the @new_gain will be set to a negative or + * positive value. The negative value means that no gain could be computed. + * Positive value will be the "best possible new gain there could be". There + * can be two reasons why finding the "best possible" new gain is not deemed + * successful. 1) This new value cannot be supported by the hardware. 2) The new + * gain required to maintain the scale would not be an integer. In this case, + * the "best possible" new gain will be a floored optimal gain, which may or + * may not be supported by the hardware. + */ +int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, + int old_time, int new_time, + int *new_gain) +{ + const struct iio_itime_sel_mul *itime_new; + u64 scale; + int ret; + + *new_gain = -1; + + itime_new = iio_gts_find_itime_by_time(gts, new_time); + if (!itime_new) + return -EINVAL; + + ret = iio_gts_get_scale_linear(gts, old_gain, old_time, &scale); + if (ret) + return ret; + + ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul, + new_gain); + if (ret) + return ret; + + if (!iio_gts_valid_gain(gts, *new_gain)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, IIO_GTS_HELPER); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); +MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers"); diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index a2f3cc2f65ef..784dc1e00310 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -192,6 +192,12 @@ static void iio_trigger_notify_done_atomic(struct iio_trigger *trig) schedule_work(&trig->reenable_work); } +/** + * iio_trigger_poll() - Call the IRQ trigger handler of the consumers + * @trig: trigger which occurred + * + * This function should only be called from a hard IRQ context. + */ void iio_trigger_poll(struct iio_trigger *trig) { int i; @@ -216,7 +222,14 @@ irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private) } EXPORT_SYMBOL(iio_trigger_generic_data_rdy_poll); -void iio_trigger_poll_chained(struct iio_trigger *trig) +/** + * iio_trigger_poll_nested() - Call the threaded trigger handler of the + * consumers + * @trig: trigger which occurred + * + * This function should only be called from a kernel thread context. + */ +void iio_trigger_poll_nested(struct iio_trigger *trig) { int i; @@ -231,7 +244,7 @@ void iio_trigger_poll_chained(struct iio_trigger *trig) } } } -EXPORT_SYMBOL(iio_trigger_poll_chained); +EXPORT_SYMBOL(iio_trigger_poll_nested); void iio_trigger_notify_done(struct iio_trigger *trig) { diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 0d4447df7200..6fa31fcd71a1 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -289,6 +289,20 @@ config JSA1212 To compile this driver as a module, choose M here: the module will be called jsa1212. +config ROHM_BU27034 + tristate "ROHM BU27034 ambient light sensor" + depends on I2C + select REGMAP_I2C + select IIO_GTS_HELPER + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Enable support for the ROHM BU27034 ambient light sensor. ROHM BU27034 + is an ambient light sesnor with 3 channels and 3 photo diodes capable + of detecting a very wide range of illuminance. + Typical application is adjusting LCD and backlight power of TVs and + mobile phones. + config RPR0521 tristate "ROHM RPR0521 ALS and proximity sensor driver" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index d74d2b5ff14c..985f6feaccd4 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_MAX44009) += max44009.o obj-$(CONFIG_NOA1305) += noa1305.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_PA12203001) += pa12203001.o +obj-$(CONFIG_ROHM_BU27034) += rohm-bu27034.o obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SI1133) += si1133.o obj-$(CONFIG_SI1145) += si1145.o diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index e1ff6f524f4b..2d91caf24dd0 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -108,7 +108,7 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) { switch (event) { case ACPI_ALS_NOTIFY_ILLUMINANCE: - iio_trigger_poll_chained(als->trig); + iio_trigger_poll_nested(als->trig); break; default: /* Unhandled event */ diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c index 3dadace09fe2..176dcad6e8e8 100644 --- a/drivers/iio/light/max44009.c +++ b/drivers/iio/light/max44009.c @@ -527,6 +527,12 @@ static int max44009_probe(struct i2c_client *client) return devm_iio_device_register(&client->dev, indio_dev); } +static const struct of_device_id max44009_of_match[] = { + { .compatible = "maxim,max44009" }, + { } +}; +MODULE_DEVICE_TABLE(of, max44009_of_match); + static const struct i2c_device_id max44009_id[] = { { "max44009", 0 }, { } @@ -536,18 +542,13 @@ MODULE_DEVICE_TABLE(i2c, max44009_id); static struct i2c_driver max44009_driver = { .driver = { .name = MAX44009_DRV_NAME, + .of_match_table = max44009_of_match, }, .probe_new = max44009_probe, .id_table = max44009_id, }; module_i2c_driver(max44009_driver); -static const struct of_device_id max44009_of_match[] = { - { .compatible = "maxim,max44009" }, - { } -}; -MODULE_DEVICE_TABLE(of, max44009_of_match); - MODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MAX44009 ambient light sensor driver"); diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c new file mode 100644 index 000000000000..e486dcf35eba --- /dev/null +++ b/drivers/iio/light/rohm-bu27034.c @@ -0,0 +1,1497 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BU27034 ROHM Ambient Light Sensor + * + * Copyright (c) 2023, ROHM Semiconductor. + * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/units.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/iio-gts-helper.h> +#include <linux/iio/kfifo_buf.h> + +#define BU27034_REG_SYSTEM_CONTROL 0x40 +#define BU27034_MASK_SW_RESET BIT(7) +#define BU27034_MASK_PART_ID GENMASK(5, 0) +#define BU27034_ID 0x19 +#define BU27034_REG_MODE_CONTROL1 0x41 +#define BU27034_MASK_MEAS_MODE GENMASK(2, 0) + +#define BU27034_REG_MODE_CONTROL2 0x42 +#define BU27034_MASK_D01_GAIN GENMASK(7, 3) +#define BU27034_MASK_D2_GAIN_HI GENMASK(7, 6) +#define BU27034_MASK_D2_GAIN_LO GENMASK(2, 0) + +#define BU27034_REG_MODE_CONTROL3 0x43 +#define BU27034_REG_MODE_CONTROL4 0x44 +#define BU27034_MASK_MEAS_EN BIT(0) +#define BU27034_MASK_VALID BIT(7) +#define BU27034_REG_DATA0_LO 0x50 +#define BU27034_REG_DATA1_LO 0x52 +#define BU27034_REG_DATA2_LO 0x54 +#define BU27034_REG_DATA2_HI 0x55 +#define BU27034_REG_MANUFACTURER_ID 0x92 +#define BU27034_REG_MAX BU27034_REG_MANUFACTURER_ID + +/* + * The BU27034 does not have interrupt to trigger the data read when a + * measurement has finished. Hence we poll the VALID bit in a thread. We will + * try to wake the thread BU27034_MEAS_WAIT_PREMATURE_MS milliseconds before + * the expected sampling time to prevent the drifting. + * + * If we constantly wake up a bit too late we would eventually skip a sample. + * And because the sleep can't wake up _exactly_ at given time this would be + * inevitable even if the sensor clock would be perfectly phase-locked to CPU + * clock - which we can't say is the case. + * + * This is still fragile. No matter how big advance do we have, we will still + * risk of losing a sample because things can in a rainy-day scenario be + * delayed a lot. Yet, more we reserve the time for polling, more we also lose + * the performance by spending cycles polling the register. So, selecting this + * value is a balancing dance between severity of wasting CPU time and severity + * of losing samples. + * + * In most cases losing the samples is not _that_ crucial because light levels + * tend to change slowly. + * + * Other option that was pointed to me would be always sleeping 1/2 of the + * measurement time, checking the VALID bit and just sleeping again if the bit + * was not set. That should be pretty tolerant against missing samples due to + * the scheduling delays while also not wasting much of cycles for polling. + * Downside is that the time-stamps would be very inaccurate as the wake-up + * would not really be tied to the sensor toggling the valid bit. This would also + * result 'jumps' in the time-stamps when the delay drifted so that wake-up was + * performed during the consecutive wake-ups (Or, when sensor and CPU clocks + * were very different and scheduling the wake-ups was very close to given + * timeout - and when the time-outs were very close to the actual sensor + * sampling, Eg. once in a blue moon, two consecutive time-outs would occur + * without having a sample ready). + */ +#define BU27034_MEAS_WAIT_PREMATURE_MS 5 +#define BU27034_DATA_WAIT_TIME_US 1000 +#define BU27034_TOTAL_DATA_WAIT_TIME_US (BU27034_MEAS_WAIT_PREMATURE_MS * 1000) + +#define BU27034_RETRY_LIMIT 18 + +enum { + BU27034_CHAN_ALS, + BU27034_CHAN_DATA0, + BU27034_CHAN_DATA1, + BU27034_CHAN_DATA2, + BU27034_NUM_CHANS +}; + +static const unsigned long bu27034_scan_masks[] = { + GENMASK(BU27034_CHAN_DATA2, BU27034_CHAN_ALS), 0 +}; + +/* + * Available scales with gain 1x - 4096x, timings 55, 100, 200, 400 mS + * Time impacts to gain: 1x, 2x, 4x, 8x. + * + * => Max total gain is HWGAIN * gain by integration time (8 * 4096) = 32768 + * + * Using NANO precision for scale we must use scale 64x corresponding gain 1x + * to avoid precision loss. (32x would result scale 976 562.5(nanos). + */ +#define BU27034_SCALE_1X 64 + +/* See the data sheet for the "Gain Setting" table */ +#define BU27034_GSEL_1X 0x00 /* 00000 */ +#define BU27034_GSEL_4X 0x08 /* 01000 */ +#define BU27034_GSEL_16X 0x0a /* 01010 */ +#define BU27034_GSEL_32X 0x0b /* 01011 */ +#define BU27034_GSEL_64X 0x0c /* 01100 */ +#define BU27034_GSEL_256X 0x18 /* 11000 */ +#define BU27034_GSEL_512X 0x19 /* 11001 */ +#define BU27034_GSEL_1024X 0x1a /* 11010 */ +#define BU27034_GSEL_2048X 0x1b /* 11011 */ +#define BU27034_GSEL_4096X 0x1c /* 11100 */ + +/* Available gain settings */ +static const struct iio_gain_sel_pair bu27034_gains[] = { + GAIN_SCALE_GAIN(1, BU27034_GSEL_1X), + GAIN_SCALE_GAIN(4, BU27034_GSEL_4X), + GAIN_SCALE_GAIN(16, BU27034_GSEL_16X), + GAIN_SCALE_GAIN(32, BU27034_GSEL_32X), + GAIN_SCALE_GAIN(64, BU27034_GSEL_64X), + GAIN_SCALE_GAIN(256, BU27034_GSEL_256X), + GAIN_SCALE_GAIN(512, BU27034_GSEL_512X), + GAIN_SCALE_GAIN(1024, BU27034_GSEL_1024X), + GAIN_SCALE_GAIN(2048, BU27034_GSEL_2048X), + GAIN_SCALE_GAIN(4096, BU27034_GSEL_4096X), +}; + +/* + * The IC has 5 modes for sampling time. 5 mS mode is exceptional as it limits + * the data collection to data0-channel only and cuts the supported range to + * 10 bit. It is not supported by the driver. + * + * "normal" modes are 55, 100, 200 and 400 mS modes - which do have direct + * multiplying impact to the register values (similar to gain). + * + * This means that if meas-mode is changed for example from 400 => 200, + * the scale is doubled. Eg, time impact to total gain is x1, x2, x4, x8. + */ +#define BU27034_MEAS_MODE_100MS 0 +#define BU27034_MEAS_MODE_55MS 1 +#define BU27034_MEAS_MODE_200MS 2 +#define BU27034_MEAS_MODE_400MS 4 + +static const struct iio_itime_sel_mul bu27034_itimes[] = { + GAIN_SCALE_ITIME_US(400000, BU27034_MEAS_MODE_400MS, 8), + GAIN_SCALE_ITIME_US(200000, BU27034_MEAS_MODE_200MS, 4), + GAIN_SCALE_ITIME_US(100000, BU27034_MEAS_MODE_100MS, 2), + GAIN_SCALE_ITIME_US(55000, BU27034_MEAS_MODE_55MS, 1), +}; + +#define BU27034_CHAN_DATA(_name, _ch2) \ +{ \ + .type = IIO_INTENSITY, \ + .channel = BU27034_CHAN_##_name, \ + .channel2 = (_ch2), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .address = BU27034_REG_##_name##_LO, \ + .scan_index = BU27034_CHAN_##_name, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .indexed = 1, \ +} + +static const struct iio_chan_spec bu27034_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = BU27034_CHAN_ALS, + .scan_index = BU27034_CHAN_ALS, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + /* + * The BU27034 DATA0 and DATA1 channels are both on the visible light + * area (mostly). The data0 sensitivity peaks at 500nm, DATA1 at 600nm. + * These wave lengths are pretty much on the border of colours making + * these a poor candidates for R/G/B standardization. Hence they're both + * marked as clear channels + */ + BU27034_CHAN_DATA(DATA0, IIO_MOD_LIGHT_CLEAR), + BU27034_CHAN_DATA(DATA1, IIO_MOD_LIGHT_CLEAR), + BU27034_CHAN_DATA(DATA2, IIO_MOD_LIGHT_IR), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +struct bu27034_data { + struct regmap *regmap; + struct device *dev; + /* + * Protect gain and time during scale adjustment and data reading. + * Protect measurement enabling/disabling. + */ + struct mutex mutex; + struct iio_gts gts; + struct task_struct *task; + __le16 raw[3]; + struct { + u32 mlux; + __le16 channels[3]; + s64 ts __aligned(8); + } scan; +}; + +struct bu27034_result { + u16 ch0; + u16 ch1; + u16 ch2; +}; + +static const struct regmap_range bu27034_volatile_ranges[] = { + { + .range_min = BU27034_REG_MODE_CONTROL4, + .range_max = BU27034_REG_MODE_CONTROL4, + }, { + .range_min = BU27034_REG_DATA0_LO, + .range_max = BU27034_REG_DATA2_HI, + }, +}; + +static const struct regmap_access_table bu27034_volatile_regs = { + .yes_ranges = &bu27034_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bu27034_volatile_ranges), +}; + +static const struct regmap_range bu27034_read_only_ranges[] = { + { + .range_min = BU27034_REG_DATA0_LO, + .range_max = BU27034_REG_DATA2_HI, + }, { + .range_min = BU27034_REG_MANUFACTURER_ID, + .range_max = BU27034_REG_MANUFACTURER_ID, + } +}; + +static const struct regmap_access_table bu27034_ro_regs = { + .no_ranges = &bu27034_read_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(bu27034_read_only_ranges), +}; + +static const struct regmap_config bu27034_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BU27034_REG_MAX, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &bu27034_volatile_regs, + .wr_table = &bu27034_ro_regs, +}; + +struct bu27034_gain_check { + int old_gain; + int new_gain; + int chan; +}; + +static int bu27034_get_gain_sel(struct bu27034_data *data, int chan) +{ + int ret, val; + + switch (chan) { + case BU27034_CHAN_DATA0: + case BU27034_CHAN_DATA1: + { + int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2, + [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3, + }; + ret = regmap_read(data->regmap, reg[chan], &val); + if (ret) + return ret; + + return FIELD_GET(BU27034_MASK_D01_GAIN, val); + } + case BU27034_CHAN_DATA2: + { + int d2_lo_bits = fls(BU27034_MASK_D2_GAIN_LO); + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL2, &val); + if (ret) + return ret; + + /* + * The data2 channel gain is composed by 5 non continuous bits + * [7:6], [2:0]. Thus when we combine the 5-bit 'selector' + * from register value we must right shift the high bits by 3. + */ + return FIELD_GET(BU27034_MASK_D2_GAIN_HI, val) << d2_lo_bits | + FIELD_GET(BU27034_MASK_D2_GAIN_LO, val); + } + default: + return -EINVAL; + } +} + +static int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain) +{ + int ret, sel; + + ret = bu27034_get_gain_sel(data, chan); + if (ret < 0) + return ret; + + sel = ret; + + ret = iio_gts_find_gain_by_sel(&data->gts, sel); + if (ret < 0) { + dev_err(data->dev, "chan %u: unknown gain value 0x%x\n", chan, + sel); + + return ret; + } + + *gain = ret; + + return 0; +} + +static int bu27034_get_int_time(struct bu27034_data *data) +{ + int ret, sel; + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &sel); + if (ret) + return ret; + + return iio_gts_find_int_time_by_sel(&data->gts, + sel & BU27034_MASK_MEAS_MODE); +} + +static int _bu27034_get_scale(struct bu27034_data *data, int channel, int *val, + int *val2) +{ + int gain, ret; + + ret = bu27034_get_gain(data, channel, &gain); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + return iio_gts_get_scale(&data->gts, gain, ret, val, val2); +} + +static int bu27034_get_scale(struct bu27034_data *data, int channel, int *val, + int *val2) +{ + int ret; + + if (channel == BU27034_CHAN_ALS) { + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + } + + mutex_lock(&data->mutex); + ret = _bu27034_get_scale(data, channel, val, val2); + mutex_unlock(&data->mutex); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_NANO; +} + +/* Caller should hold the lock to protect lux reading */ +static int bu27034_write_gain_sel(struct bu27034_data *data, int chan, int sel) +{ + static const int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2, + [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3, + }; + int mask, val; + + if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1) + return -EINVAL; + + val = FIELD_PREP(BU27034_MASK_D01_GAIN, sel); + + mask = BU27034_MASK_D01_GAIN; + + if (chan == BU27034_CHAN_DATA0) { + /* + * We keep the same gain for channel 2 as we set for channel 0 + * We can't allow them to be individually controlled because + * setting one will impact also the other. Also, if we don't + * always update both gains we may result unsupported bit + * combinations. + * + * This is not nice but this is yet another place where the + * user space must be prepared to surprizes. Namely, see chan 2 + * gain changed when chan 0 gain is changed. + * + * This is not fatal for most users though. I don't expect the + * channel 2 to be used in any generic cases - the intensity + * values provided by the sensor for IR area are not openly + * documented. Also, channel 2 is not used for visible light. + * + * So, if there is application which is written to utilize the + * channel 2 - then it is probably specifically targeted to this + * sensor and knows how to utilize those values. It is safe to + * hope such user can also cope with the gain changes. + */ + mask |= BU27034_MASK_D2_GAIN_LO; + + /* + * The D2 gain bits are directly the lowest bits of selector. + * Just do add those bits to the value + */ + val |= sel & BU27034_MASK_D2_GAIN_LO; + } + + return regmap_update_bits(data->regmap, reg[chan], mask, val); +} + +static int bu27034_set_gain(struct bu27034_data *data, int chan, int gain) +{ + int ret; + + /* + * We don't allow setting channel 2 gain as it messes up the + * gain for channel 0 - which shares the high bits + */ + if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1) + return -EINVAL; + + ret = iio_gts_find_sel_by_gain(&data->gts, gain); + if (ret < 0) + return ret; + + return bu27034_write_gain_sel(data, chan, ret); +} + +/* Caller should hold the lock to protect data->int_time */ +static int bu27034_set_int_time(struct bu27034_data *data, int time) +{ + int ret; + + ret = iio_gts_find_sel_by_int_time(&data->gts, time); + if (ret < 0) + return ret; + + return regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1, + BU27034_MASK_MEAS_MODE, ret); +} + +/* + * We try to change the time in such way that the scale is maintained for + * given channels by adjusting gain so that it compensates the time change. + */ +static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) +{ + struct bu27034_gain_check gains[] = { + { .chan = BU27034_CHAN_DATA0 }, + { .chan = BU27034_CHAN_DATA1 }, + }; + int numg = ARRAY_SIZE(gains); + int ret, int_time_old, i; + + mutex_lock(&data->mutex); + ret = bu27034_get_int_time(data); + if (ret < 0) + goto unlock_out; + + int_time_old = ret; + + if (!iio_gts_valid_time(&data->gts, time_us)) { + dev_err(data->dev, "Unsupported integration time %u\n", + time_us); + ret = -EINVAL; + + goto unlock_out; + } + + if (time_us == int_time_old) { + ret = 0; + goto unlock_out; + } + + for (i = 0; i < numg; i++) { + ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain); + if (ret) + goto unlock_out; + + ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts, + gains[i].old_gain, + int_time_old, time_us, + &gains[i].new_gain); + if (ret) { + int scale1, scale2; + bool ok; + + _bu27034_get_scale(data, gains[i].chan, &scale1, &scale2); + dev_dbg(data->dev, + "chan %u, can't support time %u with scale %u %u\n", + gains[i].chan, time_us, scale1, scale2); + + if (gains[i].new_gain < 0) + goto unlock_out; + + /* + * If caller requests for integration time change and we + * can't support the scale - then the caller should be + * prepared to 'pick up the pieces and deal with the + * fact that the scale changed'. + */ + ret = iio_find_closest_gain_low(&data->gts, + gains[i].new_gain, &ok); + + if (!ok) + dev_dbg(data->dev, + "optimal gain out of range for chan %u\n", + gains[i].chan); + + if (ret < 0) { + dev_dbg(data->dev, + "Total gain increase. Risk of saturation"); + ret = iio_gts_get_min_gain(&data->gts); + if (ret < 0) + goto unlock_out; + } + dev_dbg(data->dev, "chan %u scale changed\n", + gains[i].chan); + gains[i].new_gain = ret; + dev_dbg(data->dev, "chan %u new gain %u\n", + gains[i].chan, gains[i].new_gain); + } + } + + for (i = 0; i < numg; i++) { + ret = bu27034_set_gain(data, gains[i].chan, gains[i].new_gain); + if (ret) + goto unlock_out; + } + + ret = bu27034_set_int_time(data, time_us); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bu27034_set_scale(struct bu27034_data *data, int chan, + int val, int val2) +{ + int ret, time_sel, gain_sel, i; + bool found = false; + + if (chan == BU27034_CHAN_DATA2) + return -EINVAL; + + if (chan == BU27034_CHAN_ALS) { + if (val == 0 && val2 == 1000) + return 0; + + return -EINVAL; + } + + mutex_lock(&data->mutex); + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &time_sel); + if (ret) + goto unlock_out; + + ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, + val, val2 * 1000, &gain_sel); + if (ret) { + /* + * Could not support scale with given time. Need to change time. + * We still want to maintain the scale for all channels + */ + struct bu27034_gain_check gain; + int new_time_sel; + + /* + * Populate information for the other channel which should also + * maintain the scale. (Due to the HW limitations the chan2 + * gets the same gain as chan0, so we only need to explicitly + * set the chan 0 and 1). + */ + if (chan == BU27034_CHAN_DATA0) + gain.chan = BU27034_CHAN_DATA1; + else if (chan == BU27034_CHAN_DATA1) + gain.chan = BU27034_CHAN_DATA0; + + ret = bu27034_get_gain(data, gain.chan, &gain.old_gain); + if (ret) + goto unlock_out; + + /* + * Iterate through all the times to see if we find one which + * can support requested scale for requested channel, while + * maintaining the scale for other channels + */ + for (i = 0; i < data->gts.num_itime; i++) { + new_time_sel = data->gts.itime_table[i].sel; + + if (new_time_sel == time_sel) + continue; + + /* Can we provide requested scale with this time? */ + ret = iio_gts_find_gain_sel_for_scale_using_time( + &data->gts, new_time_sel, val, val2 * 1000, + &gain_sel); + if (ret) + continue; + + /* Can the other channel(s) maintain scale? */ + ret = iio_gts_find_new_gain_sel_by_old_gain_time( + &data->gts, gain.old_gain, time_sel, + new_time_sel, &gain.new_gain); + if (!ret) { + /* Yes - we found suitable time */ + found = true; + break; + } + } + if (!found) { + dev_dbg(data->dev, + "Can't set scale maintaining other channels\n"); + ret = -EINVAL; + + goto unlock_out; + } + + ret = bu27034_set_gain(data, gain.chan, gain.new_gain); + if (ret) + goto unlock_out; + + ret = regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1, + BU27034_MASK_MEAS_MODE, new_time_sel); + if (ret) + goto unlock_out; + } + + ret = bu27034_write_gain_sel(data, chan, gain_sel); +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +/* + * for (D1/D0 < 0.87): + * lx = 0.004521097 * D1 - 0.002663996 * D0 + + * 0.00012213 * D1 * D1 / D0 + * + * => 115.7400832 * ch1 / gain1 / mt - + * 68.1982976 * ch0 / gain0 / mt + + * 0.00012213 * 25600 * (ch1 / gain1 / mt) * 25600 * + * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) + * + * A = 0.00012213 * 25600 * (ch1 /gain1 / mt) * 25600 * + * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) + * => 0.00012213 * 25600 * (ch1 /gain1 / mt) * + * (ch1 /gain1 / mt) / (ch0 / gain0 / mt) + * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) / + * (ch0 / gain0) + * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) * + * gain0 / ch0 + * => 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /ch0 + * + * lx = (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / + * mt + A + * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / + * mt + 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt / + * ch0 + * + * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0 + + * 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) / + * mt + * + * For (0.87 <= D1/D0 < 1.00) + * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 0.87) * (0.385) + 1) + * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * + * 100 * ch1 / gain1 / mt) * ((D1/D0 - 0.87) * (0.385) + 1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * ((D1/D0 - 0.87) * (0.385) + 1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (0.385 * D1/D0 - 0.66505) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (0.385 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) - 0.66505) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (9856 * ch1 / gain1 / mt / (25600 * ch0 / gain0 / mt) + 0.66505) + * => 13.118336 * ch1 / (gain1 * mt) + * + 22.66064768 * ch0 / (gain0 * mt) + * + 8931.90144 * ch1 * ch1 * gain0 / + * (25600 * ch0 * gain1 * gain1 * mt) + * + 0.602694912 * ch1 / (gain1 * mt) + * + * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + * + 22.66064768 * ch0 / gain0 + * + 13.721030912 * ch1 / gain1 + * ] / mt + * + * For (D1/D0 >= 1.00) + * + * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 2.0) * (-0.05) + 1) + * => (0.001331* D0 + 0.0000354 * D1) * (-0.05D1/D0 + 1.1) + * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * + * 100 * ch1 / gain1 / mt) * (-0.05D1/D0 + 1.1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (-0.05 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) + 1.1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (-1280 * ch1 / (gain1 * mt * 25600 * ch0 / gain0 / mt) + 1.1) + * => (34.0736 * ch0 * -1280 * ch1 * gain0 * mt /( gain0 * mt * gain1 * mt * 25600 * ch0) + * + 34.0736 * 1.1 * ch0 / (gain0 * mt) + * + 0.90624 * ch1 * -1280 * ch1 *gain0 * mt / (gain1 * mt *gain1 * mt * 25600 * ch0) + * + 1.1 * 0.90624 * ch1 / (gain1 * mt) + * => -43614.208 * ch1 / (gain1 * mt * 25600) + * + 37.48096 ch0 / (gain0 * mt) + * - 1159.9872 * ch1 * ch1 * gain0 / (gain1 * gain1 * mt * 25600 * ch0) + * + 0.996864 ch1 / (gain1 * mt) + * => [ + * - 0.045312 * ch1 * ch1 * gain0 / (gain1 * gain1 * ch0) + * - 0.706816 * ch1 / gain1 + * + 37.48096 ch0 /gain0 + * ] * mt + * + * + * So, the first case (D1/D0 < 0.87) can be computed to a form: + * + * lx = (3.126528 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * 115.7400832 * ch1 / gain1 + + * -68.1982976 * ch0 / gain0 + * / mt + * + * Second case (0.87 <= D1/D0 < 1.00) goes to form: + * + * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * 13.721030912 * ch1 / gain1 + + * 22.66064768 * ch0 / gain0 + * ] / mt + * + * Third case (D1/D0 >= 1.00) goes to form: + * => [-0.045312 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * -0.706816 * ch1 / gain1 + + * 37.48096 ch0 /(gain0 + * ] / mt + * + * This can be unified to format: + * lx = [ + * A * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * B * ch1 / gain1 + + * C * ch0 / gain0 + * ] / mt + * + * For case 1: + * A = 3.126528, + * B = 115.7400832 + * C = -68.1982976 + * + * For case 2: + * A = 0.3489024 + * B = 13.721030912 + * C = 22.66064768 + * + * For case 3: + * A = -0.045312 + * B = -0.706816 + * C = 37.48096 + */ + +struct bu27034_lx_coeff { + unsigned int A; + unsigned int B; + unsigned int C; + /* Indicate which of the coefficients above are negative */ + bool is_neg[3]; +}; + +static inline u64 gain_mul_div_helper(u64 val, unsigned int gain, + unsigned int div) +{ + /* + * Max gain for a channel is 4096. The max u64 (0xffffffffffffffffULL) + * divided by 4096 is 0xFFFFFFFFFFFFF (GENMASK_ULL(51, 0)) (floored). + * Thus, the 0xFFFFFFFFFFFFF is the largest value we can safely multiply + * with the gain, no matter what gain is set. + * + * So, multiplication with max gain may overflow if val is greater than + * 0xFFFFFFFFFFFFF (52 bits set).. + * + * If this is the case we divide first. + */ + if (val < GENMASK_ULL(51, 0)) { + val *= gain; + do_div(val, div); + } else { + do_div(val, div); + val *= gain; + } + + return val; +} + +static u64 bu27034_fixp_calc_t1_64bit(unsigned int coeff, unsigned int ch0, + unsigned int ch1, unsigned int gain0, + unsigned int gain1) +{ + unsigned int helper; + u64 helper64; + + helper64 = (u64)coeff * (u64)ch1 * (u64)ch1; + + helper = gain1 * gain1; + if (helper > ch0) { + do_div(helper64, helper); + + return gain_mul_div_helper(helper64, gain0, ch0); + } + + do_div(helper64, ch0); + + return gain_mul_div_helper(helper64, gain0, helper); + +} + +static u64 bu27034_fixp_calc_t1(unsigned int coeff, unsigned int ch0, + unsigned int ch1, unsigned int gain0, + unsigned int gain1) +{ + unsigned int helper, tmp; + + /* + * Here we could overflow even the 64bit value. Hence we + * multiply with gain0 only after the divisions - even though + * it may result loss of accuracy + */ + helper = coeff * ch1 * ch1; + tmp = helper * gain0; + + helper = ch1 * ch1; + + if (check_mul_overflow(helper, coeff, &helper)) + return bu27034_fixp_calc_t1_64bit(coeff, ch0, ch1, gain0, gain1); + + if (check_mul_overflow(helper, gain0, &tmp)) + return bu27034_fixp_calc_t1_64bit(coeff, ch0, ch1, gain0, gain1); + + return tmp / (gain1 * gain1) / ch0; + +} + +static u64 bu27034_fixp_calc_t23(unsigned int coeff, unsigned int ch, + unsigned int gain) +{ + unsigned int helper; + u64 helper64; + + if (!check_mul_overflow(coeff, ch, &helper)) + return helper / gain; + + helper64 = (u64)coeff * (u64)ch; + do_div(helper64, gain); + + return helper64; +} + +static int bu27034_fixp_calc_lx(unsigned int ch0, unsigned int ch1, + unsigned int gain0, unsigned int gain1, + unsigned int meastime, int coeff_idx) +{ + static const struct bu27034_lx_coeff coeff[] = { + { + .A = 31265280, /* 3.126528 */ + .B = 1157400832, /*115.7400832 */ + .C = 681982976, /* -68.1982976 */ + .is_neg = {false, false, true}, + }, { + .A = 3489024, /* 0.3489024 */ + .B = 137210309, /* 13.721030912 */ + .C = 226606476, /* 22.66064768 */ + /* All terms positive */ + }, { + .A = 453120, /* -0.045312 */ + .B = 7068160, /* -0.706816 */ + .C = 374809600, /* 37.48096 */ + .is_neg = {true, true, false}, + } + }; + const struct bu27034_lx_coeff *c = &coeff[coeff_idx]; + u64 res = 0, terms[3]; + int i; + + if (coeff_idx >= ARRAY_SIZE(coeff)) + return -EINVAL; + + terms[0] = bu27034_fixp_calc_t1(c->A, ch0, ch1, gain0, gain1); + terms[1] = bu27034_fixp_calc_t23(c->B, ch1, gain1); + terms[2] = bu27034_fixp_calc_t23(c->C, ch0, gain0); + + /* First, add positive terms */ + for (i = 0; i < 3; i++) + if (!c->is_neg[i]) + res += terms[i]; + + /* No positive term => zero lux */ + if (!res) + return 0; + + /* Then, subtract negative terms (if any) */ + for (i = 0; i < 3; i++) + if (c->is_neg[i]) { + /* + * If the negative term is greater than positive - then + * the darkness has taken over and we are all doomed! Eh, + * I mean, then we can just return 0 lx and go out + */ + if (terms[i] >= res) + return 0; + + res -= terms[i]; + } + + meastime *= 10; + do_div(res, meastime); + + return (int) res; +} + +static bool bu27034_has_valid_sample(struct bu27034_data *data) +{ + int ret, val; + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL4, &val); + if (ret) { + dev_err(data->dev, "Read failed %d\n", ret); + + return false; + } + + return val & BU27034_MASK_VALID; +} + +/* + * Reading the register where VALID bit is clears this bit. (So does changing + * any gain / integration time configuration registers) The bit gets + * set when we have acquired new data. We use this bit to indicate data + * validity. + */ +static void bu27034_invalidate_read_data(struct bu27034_data *data) +{ + bu27034_has_valid_sample(data); +} + +static int bu27034_read_result(struct bu27034_data *data, int chan, int *res) +{ + int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_DATA0_LO, + [BU27034_CHAN_DATA1] = BU27034_REG_DATA1_LO, + [BU27034_CHAN_DATA2] = BU27034_REG_DATA2_LO, + }; + int valid, ret; + __le16 val; + + ret = regmap_read_poll_timeout(data->regmap, BU27034_REG_MODE_CONTROL4, + valid, (valid & BU27034_MASK_VALID), + BU27034_DATA_WAIT_TIME_US, 0); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, reg[chan], &val, sizeof(val)); + if (ret) + return ret; + + *res = le16_to_cpu(val); + + return 0; +} + +static int bu27034_get_result_unlocked(struct bu27034_data *data, __le16 *res, + int size) +{ + int ret = 0, retry_cnt = 0; + +retry: + /* Get new value from sensor if data is ready */ + if (bu27034_has_valid_sample(data)) { + ret = regmap_bulk_read(data->regmap, BU27034_REG_DATA0_LO, + res, size); + if (ret) + return ret; + + bu27034_invalidate_read_data(data); + } else { + /* No new data in sensor. Wait and retry */ + retry_cnt++; + + if (retry_cnt > BU27034_RETRY_LIMIT) { + dev_err(data->dev, "No data from sensor\n"); + + return -ETIMEDOUT; + } + + msleep(25); + + goto retry; + } + + return ret; +} + +static int bu27034_meas_set(struct bu27034_data *data, bool en) +{ + if (en) + return regmap_set_bits(data->regmap, BU27034_REG_MODE_CONTROL4, + BU27034_MASK_MEAS_EN); + + return regmap_clear_bits(data->regmap, BU27034_REG_MODE_CONTROL4, + BU27034_MASK_MEAS_EN); +} + +static int bu27034_get_single_result(struct bu27034_data *data, int chan, + int *val) +{ + int ret; + + if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA2) + return -EINVAL; + + ret = bu27034_meas_set(data, true); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + msleep(ret / 1000); + + return bu27034_read_result(data, chan, val); +} + +/* + * The formula given by vendor for computing luxes out of data0 and data1 + * (in open air) is as follows: + * + * Let's mark: + * D0 = data0/ch0_gain/meas_time_ms * 25600 + * D1 = data1/ch1_gain/meas_time_ms * 25600 + * + * Then: + * if (D1/D0 < 0.87) + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 3.45 + 1) + * else if (D1/D0 < 1) + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 0.385 + 1) + * else + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 2) * -0.05 + 1) + * + * We use it here. Users who have for example some colored lens + * need to modify the calculation but I hope this gives a starting point for + * those working with such devices. + */ + +static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val) +{ + unsigned int gain0, gain1, meastime; + unsigned int d1_d0_ratio_scaled; + u16 ch0, ch1; + u64 helper64; + int ret; + + /* + * We return 0 lux if calculation fails. This should be reasonably + * easy to spot from the buffers especially if raw-data channels show + * valid values + */ + *val = 0; + + ch0 = max_t(u16, 1, le16_to_cpu(res[0])); + ch1 = max_t(u16, 1, le16_to_cpu(res[1])); + + ret = bu27034_get_gain(data, BU27034_CHAN_DATA0, &gain0); + if (ret) + return ret; + + ret = bu27034_get_gain(data, BU27034_CHAN_DATA1, &gain1); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + meastime = ret; + + d1_d0_ratio_scaled = (unsigned int)ch1 * (unsigned int)gain0 * 100; + helper64 = (u64)ch1 * (u64)gain0 * 100LLU; + + if (helper64 != d1_d0_ratio_scaled) { + unsigned int div = (unsigned int)ch0 * gain1; + + do_div(helper64, div); + d1_d0_ratio_scaled = helper64; + } else { + d1_d0_ratio_scaled /= ch0 * gain1; + } + + if (d1_d0_ratio_scaled < 87) + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 0); + else if (d1_d0_ratio_scaled < 100) + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 1); + else + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 2); + + if (ret < 0) + return ret; + + *val = ret; + + return 0; + +} + +static int bu27034_get_mlux(struct bu27034_data *data, int chan, int *val) +{ + __le16 res[3]; + int ret; + + ret = bu27034_meas_set(data, true); + if (ret) + return ret; + + ret = bu27034_get_result_unlocked(data, &res[0], sizeof(res)); + if (ret) + return ret; + + ret = bu27034_calc_mlux(data, res, val); + if (ret) + return ret; + + ret = bu27034_meas_set(data, false); + if (ret) + dev_err(data->dev, "failed to disable measurement\n"); + + return 0; +} + +static int bu27034_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *val = bu27034_get_int_time(data); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return bu27034_get_scale(data, chan->channel, val, val2); + + case IIO_CHAN_INFO_RAW: + { + int (*result_get)(struct bu27034_data *data, int chan, int *val); + + if (chan->type == IIO_INTENSITY) + result_get = bu27034_get_single_result; + else if (chan->type == IIO_LIGHT) + result_get = bu27034_get_mlux; + else + return -EINVAL; + + /* Don't mess with measurement enabling while buffering */ + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + mutex_lock(&data->mutex); + /* + * Reading one channel at a time is inefficient but we + * don't care here. Buffered version should be used if + * performance is an issue. + */ + ret = result_get(data, chan->channel, val); + + mutex_unlock(&data->mutex); + iio_device_release_direct_mode(idev); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int bu27034_write_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = bu27034_set_scale(data, chan->channel, val, val2); + break; + case IIO_CHAN_INFO_INT_TIME: + ret = bu27034_try_set_int_time(data, val); + break; + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct_mode(idev); + + return ret; +} + +static int bu27034_read_avail(struct iio_dev *idev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return iio_gts_avail_times(&data->gts, vals, type, length); + case IIO_CHAN_INFO_SCALE: + return iio_gts_all_avail_scales(&data->gts, vals, type, length); + default: + return -EINVAL; + } +} + +static const struct iio_info bu27034_info = { + .read_raw = &bu27034_read_raw, + .write_raw = &bu27034_write_raw, + .read_avail = &bu27034_read_avail, +}; + +static int bu27034_chip_init(struct bu27034_data *data) +{ + int ret, sel; + + /* Reset */ + ret = regmap_update_bits(data->regmap, BU27034_REG_SYSTEM_CONTROL, + BU27034_MASK_SW_RESET, BU27034_MASK_SW_RESET); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); + + msleep(1); + /* + * Read integration time here to ensure it is in regmap cache. We do + * this to speed-up the int-time acquisition in the start of the buffer + * handling thread where longer delays could make it more likely we end + * up skipping a sample, and where the longer delays make timestamps + * less accurate. + */ + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &sel); + if (ret) + dev_err(data->dev, "reading integration time failed\n"); + + return 0; +} + +static int bu27034_wait_for_data(struct bu27034_data *data) +{ + int ret, val; + + ret = regmap_read_poll_timeout(data->regmap, BU27034_REG_MODE_CONTROL4, + val, val & BU27034_MASK_VALID, + BU27034_DATA_WAIT_TIME_US, + BU27034_TOTAL_DATA_WAIT_TIME_US); + if (ret) { + dev_err(data->dev, "data polling %s\n", + !(val & BU27034_MASK_VALID) ? "timeout" : "fail"); + + return ret; + } + + ret = regmap_bulk_read(data->regmap, BU27034_REG_DATA0_LO, + &data->scan.channels[0], + sizeof(data->scan.channels)); + if (ret) + return ret; + + bu27034_invalidate_read_data(data); + + return 0; +} + +static int bu27034_buffer_thread(void *arg) +{ + struct iio_dev *idev = arg; + struct bu27034_data *data; + int wait_ms; + + data = iio_priv(idev); + + wait_ms = bu27034_get_int_time(data); + wait_ms /= 1000; + + wait_ms -= BU27034_MEAS_WAIT_PREMATURE_MS; + + while (!kthread_should_stop()) { + int ret; + int64_t tstamp; + + msleep(wait_ms); + ret = bu27034_wait_for_data(data); + if (ret) + continue; + + tstamp = iio_get_time_ns(idev); + + if (test_bit(BU27034_CHAN_ALS, idev->active_scan_mask)) { + int mlux; + + ret = bu27034_calc_mlux(data, &data->scan.channels[0], + &mlux); + if (ret) + dev_err(data->dev, "failed to calculate lux\n"); + + /* + * The maximum Milli lux value we get with gain 1x time + * 55mS data ch0 = 0xffff ch1 = 0xffff fits in 26 bits + * so there should be no problem returning int from + * computations and casting it to u32 + */ + data->scan.mlux = (u32)mlux; + } + iio_push_to_buffers_with_timestamp(idev, &data->scan, tstamp); + } + + return 0; +} + +static int bu27034_buffer_enable(struct iio_dev *idev) +{ + struct bu27034_data *data = iio_priv(idev); + struct task_struct *task; + int ret; + + mutex_lock(&data->mutex); + ret = bu27034_meas_set(data, true); + if (ret) + goto unlock_out; + + task = kthread_run(bu27034_buffer_thread, idev, + "bu27034-buffering-%u", + iio_device_id(idev)); + if (IS_ERR(task)) { + ret = PTR_ERR(task); + goto unlock_out; + } + + data->task = task; + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bu27034_buffer_disable(struct iio_dev *idev) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + mutex_lock(&data->mutex); + if (data->task) { + kthread_stop(data->task); + data->task = NULL; + } + + ret = bu27034_meas_set(data, false); + mutex_unlock(&data->mutex); + + return ret; +} + +static const struct iio_buffer_setup_ops bu27034_buffer_ops = { + .postenable = &bu27034_buffer_enable, + .predisable = &bu27034_buffer_disable, +}; + +static int bu27034_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct bu27034_data *data; + struct regmap *regmap; + struct iio_dev *idev; + unsigned int part_id, reg; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &bu27034_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize Regmap\n"); + + idev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!idev) + return -ENOMEM; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); + + data = iio_priv(idev); + + ret = regmap_read(regmap, BU27034_REG_SYSTEM_CONTROL, ®); + if (ret) + return dev_err_probe(dev, ret, "Failed to access sensor\n"); + + part_id = FIELD_GET(BU27034_MASK_PART_ID, reg); + + if (part_id != BU27034_ID) + dev_warn(dev, "unknown device 0x%x\n", part_id); + + ret = devm_iio_init_iio_gts(dev, BU27034_SCALE_1X, 0, bu27034_gains, + ARRAY_SIZE(bu27034_gains), bu27034_itimes, + ARRAY_SIZE(bu27034_itimes), &data->gts); + if (ret) + return ret; + + mutex_init(&data->mutex); + data->regmap = regmap; + data->dev = dev; + + idev->channels = bu27034_channels; + idev->num_channels = ARRAY_SIZE(bu27034_channels); + idev->name = "bu27034"; + idev->info = &bu27034_info; + + idev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + idev->available_scan_masks = bu27034_scan_masks; + + ret = bu27034_chip_init(data); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup(dev, idev, &bu27034_buffer_ops); + if (ret) + return dev_err_probe(dev, ret, "buffer setup failed\n"); + + ret = devm_iio_device_register(dev, idev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Unable to register iio device\n"); + + return ret; +} + +static const struct of_device_id bu27034_of_match[] = { + { .compatible = "rohm,bu27034" }, + { } +}; +MODULE_DEVICE_TABLE(of, bu27034_of_match); + +static struct i2c_driver bu27034_i2c_driver = { + .driver = { + .name = "bu27034-als", + .of_match_table = bu27034_of_match, + }, + .probe_new = bu27034_probe, +}; +module_i2c_driver(bu27034_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); +MODULE_DESCRIPTION("ROHM BU27034 ambient light sensor driver"); +MODULE_IMPORT_NS(IIO_GTS_HELPER); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 668e444f6049..9d0218b7426e 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -431,7 +431,7 @@ static irqreturn_t rpr0521_drdy_irq_thread(int irq, void *private) struct rpr0521_data *data = iio_priv(indio_dev); if (rpr0521_is_triggered(data)) { - iio_trigger_poll_chained(data->drdy_trigger0); + iio_trigger_poll_nested(data->drdy_trigger0); return IRQ_HANDLED; } diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index c737d3e193ae..50f95c5d2060 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -161,7 +161,7 @@ static irqreturn_t st_uvis25_trigger_handler_thread(int irq, void *private) if (!(status & ST_UVIS25_REG_UV_DA_MASK)) return IRQ_NONE; - iio_trigger_poll_chained(hw->trig); + iio_trigger_poll_nested(hw->trig); return IRQ_HANDLED; } diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 5c44a36ab5b3..56d3963d3d66 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1077,7 +1077,7 @@ static irqreturn_t vcnl4010_irq_thread(int irq, void *p) } if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) - iio_trigger_poll_chained(indio_dev->trig); + iio_trigger_poll_nested(indio_dev->trig); end: return IRQ_HANDLED; diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 84148b944000..14e29330e972 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -89,7 +89,7 @@ static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private) IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), iio_get_time_ns(indio_dev)); - iio_trigger_poll_chained(data->drdy_trigger0); + iio_trigger_poll_nested(data->drdy_trigger0); return IRQ_HANDLED; } diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index b82f093f1e6a..0083e858c21e 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -118,7 +118,7 @@ static int lmp91000_read(struct lmp91000_data *data, int channel, int *val) data->chan_select = channel != LMP91000_REG_MODECN_3LEAD; - iio_trigger_poll_chained(data->trig); + iio_trigger_poll_nested(data->trig); ret = wait_for_completion_timeout(&data->completion, HZ); reinit_completion(&data->completion); diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index c9453389e4f7..02b97e89de50 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -17,14 +17,14 @@ config ABP060MG will be called abp060mg. config BMP280 - tristate "Bosch Sensortec BMP180/BMP280/BMP380 pressure sensor I2C driver" + tristate "Bosch Sensortec BMP180/BMP280/BMP380/BMP580 pressure sensor driver" depends on (I2C || SPI_MASTER) select REGMAP select BMP280_I2C if (I2C) select BMP280_SPI if (SPI_MASTER) help - Say yes here to build support for Bosch Sensortec BMP180, BMP280 and - BMP380 pressure and temperature sensors. Also supports the BME280 with + Say yes here to build support for Bosch Sensortec BMP180, BMP280, BMP380 + and BMP580 pressure and temperature sensors. Also supports the BME280 with an additional humidity sensor channel. To compile this driver as a module, choose M here: the core module diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index c0aff78489b4..6089f3f9d8f4 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -13,6 +13,7 @@ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf * * Notice: * The link to the bmp180 datasheet points to an outdated version missing these changes: @@ -27,6 +28,7 @@ #include <linux/bitfield.h> #include <linux/device.h> #include <linux/module.h> +#include <linux/nvmem-provider.h> #include <linux/regmap.h> #include <linux/delay.h> #include <linux/iio/iio.h> @@ -49,65 +51,6 @@ */ enum { AC1, AC2, AC3, AC4, AC5, AC6, B1, B2, MB, MC, MD }; -struct bmp180_calib { - s16 AC1; - s16 AC2; - s16 AC3; - u16 AC4; - u16 AC5; - u16 AC6; - s16 B1; - s16 B2; - s16 MB; - s16 MC; - s16 MD; -}; - -/* See datasheet Section 4.2.2. */ -struct bmp280_calib { - u16 T1; - s16 T2; - s16 T3; - u16 P1; - s16 P2; - s16 P3; - s16 P4; - s16 P5; - s16 P6; - s16 P7; - s16 P8; - s16 P9; - u8 H1; - s16 H2; - u8 H3; - s16 H4; - s16 H5; - s8 H6; -}; - -/* See datasheet Section 3.11.1. */ -struct bmp380_calib { - u16 T1; - u16 T2; - s8 T3; - s16 P1; - s16 P2; - s8 P3; - s8 P4; - u16 P5; - u16 P6; - s8 P7; - s8 P8; - s16 P9; - s8 P10; - s8 P11; -}; - -static const char *const bmp280_supply_names[] = { - "vddd", "vdda" -}; - -#define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names) enum bmp380_odr { BMP380_ODR_200HZ, @@ -130,92 +73,39 @@ enum bmp380_odr { BMP380_ODR_0_0015HZ, }; -struct bmp280_data { - struct device *dev; - struct mutex lock; - struct regmap *regmap; - struct completion done; - bool use_eoc; - const struct bmp280_chip_info *chip_info; - union { - struct bmp180_calib bmp180; - struct bmp280_calib bmp280; - struct bmp380_calib bmp380; - } calib; - struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; - unsigned int start_up_time; /* in microseconds */ - - /* log of base 2 of oversampling rate */ - u8 oversampling_press; - u8 oversampling_temp; - u8 oversampling_humid; - u8 iir_filter_coeff; - - /* - * BMP380 devices introduce sampling frequency configuration. See - * datasheet sections 3.3.3. and 4.3.19 for more details. - * - * BMx280 devices allowed indirect configuration of sampling frequency - * changing the t_standby duration between measurements, as detailed on - * section 3.6.3 of the datasheet. - */ - int sampling_freq; - - /* - * Carryover value from temperature conversion, used in pressure - * calculation. - */ - s32 t_fine; - - /* - * DMA (thus cache coherency maintenance) may require the - * transfer buffers to live in their own cache lines. - */ - union { - /* Sensor data buffer */ - u8 buf[3]; - /* Calibration data buffers */ - __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; - __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; - u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; - /* Miscellaneous, endianess-aware data buffers */ - __le16 le16; - __be16 be16; - } __aligned(IIO_DMA_MINALIGN); -}; - -struct bmp280_chip_info { - unsigned int id_reg; - - const struct iio_chan_spec *channels; - int num_channels; - unsigned int start_up_time; - - const int *oversampling_temp_avail; - int num_oversampling_temp_avail; - int oversampling_temp_default; - - const int *oversampling_press_avail; - int num_oversampling_press_avail; - int oversampling_press_default; - - const int *oversampling_humid_avail; - int num_oversampling_humid_avail; - int oversampling_humid_default; - - const int *iir_filter_coeffs_avail; - int num_iir_filter_coeffs_avail; - int iir_filter_coeff_default; - - const int (*sampling_freq_avail)[2]; - int num_sampling_freq_avail; - int sampling_freq_default; - - int (*chip_config)(struct bmp280_data *); - int (*read_temp)(struct bmp280_data *, int *); - int (*read_press)(struct bmp280_data *, int *, int *); - int (*read_humid)(struct bmp280_data *, int *, int *); - int (*read_calib)(struct bmp280_data *); +enum bmp580_odr { + BMP580_ODR_240HZ, + BMP580_ODR_218HZ, + BMP580_ODR_199HZ, + BMP580_ODR_179HZ, + BMP580_ODR_160HZ, + BMP580_ODR_149HZ, + BMP580_ODR_140HZ, + BMP580_ODR_129HZ, + BMP580_ODR_120HZ, + BMP580_ODR_110HZ, + BMP580_ODR_100HZ, + BMP580_ODR_89HZ, + BMP580_ODR_80HZ, + BMP580_ODR_70HZ, + BMP580_ODR_60HZ, + BMP580_ODR_50HZ, + BMP580_ODR_45HZ, + BMP580_ODR_40HZ, + BMP580_ODR_35HZ, + BMP580_ODR_30HZ, + BMP580_ODR_25HZ, + BMP580_ODR_20HZ, + BMP580_ODR_15HZ, + BMP580_ODR_10HZ, + BMP580_ODR_5HZ, + BMP580_ODR_4HZ, + BMP580_ODR_3HZ, + BMP580_ODR_2HZ, + BMP580_ODR_1HZ, + BMP580_ODR_0_5HZ, + BMP580_ODR_0_25HZ, + BMP580_ODR_0_125HZ, }; /* @@ -473,7 +363,7 @@ static u32 bmp280_compensate_press(struct bmp280_data *data, } static int bmp280_read_temp(struct bmp280_data *data, - int *val) + int *val, int *val2) { s32 adc_temp, comp_temp; int ret; @@ -513,7 +403,7 @@ static int bmp280_read_press(struct bmp280_data *data, int ret; /* Read and compensate temperature so we get a reading of t_fine. */ - ret = bmp280_read_temp(data, NULL); + ret = bmp280_read_temp(data, NULL, NULL); if (ret < 0) return ret; @@ -545,7 +435,7 @@ static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) int ret; /* Read and compensate temperature so we get a reading of t_fine. */ - ret = bmp280_read_temp(data, NULL); + ret = bmp280_read_temp(data, NULL, NULL); if (ret < 0) return ret; @@ -589,7 +479,7 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, ret = data->chip_info->read_press(data, val, val2); break; case IIO_TEMP: - ret = data->chip_info->read_temp(data, val); + ret = data->chip_info->read_temp(data, val, val2); break; default: ret = -EINVAL; @@ -905,8 +795,10 @@ static int bmp280_chip_config(struct bmp280_data *data) static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; -static const struct bmp280_chip_info bmp280_chip_info = { +const struct bmp280_chip_info bmp280_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BMP280_CHIP_ID, + .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 2, @@ -934,6 +826,7 @@ static const struct bmp280_chip_info bmp280_chip_info = { .read_press = bmp280_read_press, .read_calib = bmp280_read_calib, }; +EXPORT_SYMBOL_NS(bmp280_chip_info, IIO_BMP280); static int bme280_chip_config(struct bmp280_data *data) { @@ -953,8 +846,10 @@ static int bme280_chip_config(struct bmp280_data *data) return bmp280_chip_config(data); } -static const struct bmp280_chip_info bme280_chip_info = { +const struct bmp280_chip_info bme280_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BME280_CHIP_ID, + .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 3, @@ -977,6 +872,7 @@ static const struct bmp280_chip_info bme280_chip_info = { .read_humid = bmp280_read_humid, .read_calib = bme280_read_calib, }; +EXPORT_SYMBOL_NS(bme280_chip_info, IIO_BMP280); /* * Helper function to send a command to BMP3XX sensors. @@ -1095,7 +991,7 @@ static u32 bmp380_compensate_press(struct bmp280_data *data, u32 adc_press) return comp_press; } -static int bmp380_read_temp(struct bmp280_data *data, int *val) +static int bmp380_read_temp(struct bmp280_data *data, int *val, int *val2) { s32 comp_temp; u32 adc_temp; @@ -1135,7 +1031,7 @@ static int bmp380_read_press(struct bmp280_data *data, int *val, int *val2) int ret; /* Read and compensate for temperature so we get a reading of t_fine */ - ret = bmp380_read_temp(data, NULL); + ret = bmp380_read_temp(data, NULL, NULL); if (ret) return ret; @@ -1217,6 +1113,12 @@ static const int bmp380_odr_table[][2] = { [BMP380_ODR_0_0015HZ] = {0, 1526}, }; +static int bmp380_preinit(struct bmp280_data *data) +{ + /* BMP3xx requires soft-reset as part of initialization */ + return bmp380_cmd(data, BMP380_CMD_SOFT_RESET); +} + static int bmp380_chip_config(struct bmp280_data *data) { bool change = false, aux; @@ -1319,8 +1221,10 @@ static int bmp380_chip_config(struct bmp280_data *data) static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 }; static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128}; -static const struct bmp280_chip_info bmp380_chip_info = { +const struct bmp280_chip_info bmp380_chip_info = { .id_reg = BMP380_REG_ID, + .chip_id = BMP380_CHIP_ID, + .regmap_config = &bmp380_regmap_config, .start_up_time = 2000, .channels = bmp380_channels, .num_channels = 2, @@ -1345,7 +1249,508 @@ static const struct bmp280_chip_info bmp380_chip_info = { .read_temp = bmp380_read_temp, .read_press = bmp380_read_press, .read_calib = bmp380_read_calib, + .preinit = bmp380_preinit, +}; +EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280); + +static int bmp580_soft_reset(struct bmp280_data *data) +{ + unsigned int reg; + int ret; + + ret = regmap_write(data->regmap, BMP580_REG_CMD, BMP580_CMD_SOFT_RESET); + if (ret) { + dev_err(data->dev, "failed to send reset command to device\n"); + return ret; + } + usleep_range(2000, 2500); + + /* Dummy read of chip_id */ + ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, ®); + if (ret) { + dev_err(data->dev, "failed to reestablish comms after reset\n"); + return ret; + } + + ret = regmap_read(data->regmap, BMP580_REG_INT_STATUS, ®); + if (ret) { + dev_err(data->dev, "error reading interrupt status register\n"); + return ret; + } + if (!(reg & BMP580_INT_STATUS_POR_MASK)) { + dev_err(data->dev, "error resetting sensor\n"); + return -EINVAL; + } + + return 0; +} + +/** + * bmp580_nvm_operation() - Helper function to commit NVM memory operations + * @data: sensor data struct + * @is_write: flag to signal write operation + */ +static int bmp580_nvm_operation(struct bmp280_data *data, bool is_write) +{ + unsigned long timeout, poll; + unsigned int reg; + int ret; + + /* Check NVM ready flag */ + ret = regmap_read(data->regmap, BMP580_REG_STATUS, ®); + if (ret) { + dev_err(data->dev, "failed to check nvm status\n"); + return ret; + } + if (!(reg & BMP580_STATUS_NVM_RDY_MASK)) { + dev_err(data->dev, "sensor's nvm is not ready\n"); + return -EIO; + } + + /* Start NVM operation sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, BMP580_CMD_NVM_OP_SEQ_0); + if (ret) { + dev_err(data->dev, "failed to send nvm operation's first sequence\n"); + return ret; + } + if (is_write) { + /* Send NVM write sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, + BMP580_CMD_NVM_WRITE_SEQ_1); + if (ret) { + dev_err(data->dev, "failed to send nvm write sequence\n"); + return ret; + } + /* Datasheet says on 4.8.1.2 it takes approximately 10ms */ + poll = 2000; + timeout = 12000; + } else { + /* Send NVM read sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, + BMP580_CMD_NVM_READ_SEQ_1); + if (ret) { + dev_err(data->dev, "failed to send nvm read sequence\n"); + return ret; + } + /* Datasheet says on 4.8.1.1 it takes approximately 200us */ + poll = 50; + timeout = 400; + } + if (ret) { + dev_err(data->dev, "failed to write command sequence\n"); + return -EIO; + } + + /* Wait until NVM is ready again */ + ret = regmap_read_poll_timeout(data->regmap, BMP580_REG_STATUS, reg, + (reg & BMP580_STATUS_NVM_RDY_MASK), + poll, timeout); + if (ret) { + dev_err(data->dev, "error checking nvm operation status\n"); + return ret; + } + + /* Check NVM error flags */ + if ((reg & BMP580_STATUS_NVM_ERR_MASK) || (reg & BMP580_STATUS_NVM_CMD_ERR_MASK)) { + dev_err(data->dev, "error processing nvm operation\n"); + return -EIO; + } + + return 0; +} + +/* + * Contrary to previous sensors families, compensation algorithm is builtin. + * We are only required to read the register raw data and adapt the ranges + * for what is expected on IIO ABI. + */ + +static int bmp580_read_temp(struct bmp280_data *data, int *val, int *val2) +{ + s32 raw_temp; + int ret; + + ret = regmap_bulk_read(data->regmap, BMP580_REG_TEMP_XLSB, data->buf, + sizeof(data->buf)); + if (ret) { + dev_err(data->dev, "failed to read temperature\n"); + return ret; + } + + raw_temp = get_unaligned_le24(data->buf); + if (raw_temp == BMP580_TEMP_SKIPPED) { + dev_err(data->dev, "reading temperature skipped\n"); + return -EIO; + } + + /* + * Temperature is returned in Celsius degrees in fractional + * form down 2^16. We reescale by x1000 to return milli Celsius + * to respect IIO ABI. + */ + *val = raw_temp * 1000; + *val2 = 16; + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int bmp580_read_press(struct bmp280_data *data, int *val, int *val2) +{ + u32 raw_press; + int ret; + + ret = regmap_bulk_read(data->regmap, BMP580_REG_PRESS_XLSB, data->buf, + sizeof(data->buf)); + if (ret) { + dev_err(data->dev, "failed to read pressure\n"); + return ret; + } + + raw_press = get_unaligned_le24(data->buf); + if (raw_press == BMP580_PRESS_SKIPPED) { + dev_err(data->dev, "reading pressure skipped\n"); + return -EIO; + } + /* + * Pressure is returned in Pascals in fractional form down 2^16. + * We reescale /1000 to convert to kilopascal to respect IIO ABI. + */ + *val = raw_press; + *val2 = 64000; /* 2^6 * 1000 */ + return IIO_VAL_FRACTIONAL; +} + +static const int bmp580_odr_table[][2] = { + [BMP580_ODR_240HZ] = {240, 0}, + [BMP580_ODR_218HZ] = {218, 0}, + [BMP580_ODR_199HZ] = {199, 0}, + [BMP580_ODR_179HZ] = {179, 0}, + [BMP580_ODR_160HZ] = {160, 0}, + [BMP580_ODR_149HZ] = {149, 0}, + [BMP580_ODR_140HZ] = {140, 0}, + [BMP580_ODR_129HZ] = {129, 0}, + [BMP580_ODR_120HZ] = {120, 0}, + [BMP580_ODR_110HZ] = {110, 0}, + [BMP580_ODR_100HZ] = {100, 0}, + [BMP580_ODR_89HZ] = {89, 0}, + [BMP580_ODR_80HZ] = {80, 0}, + [BMP580_ODR_70HZ] = {70, 0}, + [BMP580_ODR_60HZ] = {60, 0}, + [BMP580_ODR_50HZ] = {50, 0}, + [BMP580_ODR_45HZ] = {45, 0}, + [BMP580_ODR_40HZ] = {40, 0}, + [BMP580_ODR_35HZ] = {35, 0}, + [BMP580_ODR_30HZ] = {30, 0}, + [BMP580_ODR_25HZ] = {25, 0}, + [BMP580_ODR_20HZ] = {20, 0}, + [BMP580_ODR_15HZ] = {15, 0}, + [BMP580_ODR_10HZ] = {10, 0}, + [BMP580_ODR_5HZ] = {5, 0}, + [BMP580_ODR_4HZ] = {4, 0}, + [BMP580_ODR_3HZ] = {3, 0}, + [BMP580_ODR_2HZ] = {2, 0}, + [BMP580_ODR_1HZ] = {1, 0}, + [BMP580_ODR_0_5HZ] = {0, 500000}, + [BMP580_ODR_0_25HZ] = {0, 250000}, + [BMP580_ODR_0_125HZ] = {0, 125000}, +}; + +static const int bmp580_nvmem_addrs[] = { 0x20, 0x21, 0x22 }; + +static int bmp580_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct bmp280_data *data = priv; + u16 *dst = val; + int ret, addr; + + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + + /* Set sensor in standby mode */ + ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS, + BMP580_ODR_DEEPSLEEP_DIS | + FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP)); + if (ret) { + dev_err(data->dev, "failed to change sensor to standby mode\n"); + goto exit; + } + /* Wait standby transition time */ + usleep_range(2500, 3000); + + while (bytes >= sizeof(*dst)) { + addr = bmp580_nvmem_addrs[offset / sizeof(*dst)]; + + ret = regmap_write(data->regmap, BMP580_REG_NVM_ADDR, + FIELD_PREP(BMP580_NVM_ROW_ADDR_MASK, addr)); + if (ret) { + dev_err(data->dev, "error writing nvm address\n"); + goto exit; + } + + ret = bmp580_nvm_operation(data, false); + if (ret) + goto exit; + + ret = regmap_bulk_read(data->regmap, BMP580_REG_NVM_DATA_LSB, &data->le16, + sizeof(data->le16)); + if (ret) { + dev_err(data->dev, "error reading nvm data regs\n"); + goto exit; + } + + *dst++ = le16_to_cpu(data->le16); + bytes -= sizeof(*dst); + offset += sizeof(*dst); + } +exit: + /* Restore chip config */ + data->chip_info->chip_config(data); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + return ret; +} + +static int bmp580_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct bmp280_data *data = priv; + u16 *buf = val; + int ret, addr; + + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + + /* Set sensor in standby mode */ + ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS, + BMP580_ODR_DEEPSLEEP_DIS | + FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP)); + if (ret) { + dev_err(data->dev, "failed to change sensor to standby mode\n"); + goto exit; + } + /* Wait standby transition time */ + usleep_range(2500, 3000); + + while (bytes >= sizeof(*buf)) { + addr = bmp580_nvmem_addrs[offset / sizeof(*buf)]; + + ret = regmap_write(data->regmap, BMP580_REG_NVM_ADDR, BMP580_NVM_PROG_EN | + FIELD_PREP(BMP580_NVM_ROW_ADDR_MASK, addr)); + if (ret) { + dev_err(data->dev, "error writing nvm address\n"); + goto exit; + } + data->le16 = cpu_to_le16(*buf++); + + ret = regmap_bulk_write(data->regmap, BMP580_REG_NVM_DATA_LSB, &data->le16, + sizeof(data->le16)); + if (ret) { + dev_err(data->dev, "error writing LSB NVM data regs\n"); + goto exit; + } + + ret = bmp580_nvm_operation(data, true); + if (ret) + goto exit; + + /* Disable programming mode bit */ + ret = regmap_update_bits(data->regmap, BMP580_REG_NVM_ADDR, + BMP580_NVM_PROG_EN, 0); + if (ret) { + dev_err(data->dev, "error resetting nvm write\n"); + goto exit; + } + + bytes -= sizeof(*buf); + offset += sizeof(*buf); + } +exit: + /* Restore chip config */ + data->chip_info->chip_config(data); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + return ret; +} + +static int bmp580_preinit(struct bmp280_data *data) +{ + struct nvmem_config config = { + .dev = data->dev, + .priv = data, + .name = "bmp580_nvmem", + .word_size = sizeof(u16), + .stride = sizeof(u16), + .size = 3 * sizeof(u16), + .reg_read = bmp580_nvmem_read, + .reg_write = bmp580_nvmem_write, + }; + unsigned int reg; + int ret; + + /* Issue soft-reset command */ + ret = bmp580_soft_reset(data); + if (ret) + return ret; + + /* Post powerup sequence */ + ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, ®); + if (ret) + return ret; + + /* Print warn message if we don't know the chip id */ + if (reg != BMP580_CHIP_ID && reg != BMP580_CHIP_ID_ALT) + dev_warn(data->dev, "preinit: unexpected chip_id\n"); + + ret = regmap_read(data->regmap, BMP580_REG_STATUS, ®); + if (ret) + return ret; + + /* Check nvm status */ + if (!(reg & BMP580_STATUS_NVM_RDY_MASK) || (reg & BMP580_STATUS_NVM_ERR_MASK)) { + dev_err(data->dev, "preinit: nvm error on powerup sequence\n"); + return -EIO; + } + + /* Register nvmem device */ + return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config)); +} + +static int bmp580_chip_config(struct bmp280_data *data) +{ + bool change = false, aux; + unsigned int tmp; + u8 reg_val; + int ret; + + /* Sets sensor in standby mode */ + ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS, + BMP580_ODR_DEEPSLEEP_DIS | + FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP)); + if (ret) { + dev_err(data->dev, "failed to change sensor to standby mode\n"); + return ret; + } + /* From datasheet's table 4: electrical characteristics */ + usleep_range(2500, 3000); + + /* Set default DSP mode settings */ + reg_val = FIELD_PREP(BMP580_DSP_COMP_MASK, BMP580_DSP_PRESS_TEMP_COMP_EN) | + BMP580_DSP_SHDW_IIR_TEMP_EN | BMP580_DSP_SHDW_IIR_PRESS_EN; + + ret = regmap_update_bits(data->regmap, BMP580_REG_DSP_CONFIG, + BMP580_DSP_COMP_MASK | + BMP580_DSP_SHDW_IIR_TEMP_EN | + BMP580_DSP_SHDW_IIR_PRESS_EN, reg_val); + + /* Configure oversampling */ + reg_val = FIELD_PREP(BMP580_OSR_TEMP_MASK, data->oversampling_temp) | + FIELD_PREP(BMP580_OSR_PRESS_MASK, data->oversampling_press) | + BMP580_OSR_PRESS_EN; + + ret = regmap_update_bits_check(data->regmap, BMP580_REG_OSR_CONFIG, + BMP580_OSR_TEMP_MASK | BMP580_OSR_PRESS_MASK | + BMP580_OSR_PRESS_EN, + reg_val, &aux); + if (ret) { + dev_err(data->dev, "failed to write oversampling register\n"); + return ret; + } + change = change || aux; + + /* Configure output data rate */ + ret = regmap_update_bits_check(data->regmap, BMP580_REG_ODR_CONFIG, BMP580_ODR_MASK, + FIELD_PREP(BMP580_ODR_MASK, data->sampling_freq), + &aux); + if (ret) { + dev_err(data->dev, "failed to write ODR configuration register\n"); + return ret; + } + change = change || aux; + + /* Set filter data */ + reg_val = FIELD_PREP(BMP580_DSP_IIR_PRESS_MASK, data->iir_filter_coeff) | + FIELD_PREP(BMP580_DSP_IIR_TEMP_MASK, data->iir_filter_coeff); + + ret = regmap_update_bits_check(data->regmap, BMP580_REG_DSP_IIR, + BMP580_DSP_IIR_PRESS_MASK | + BMP580_DSP_IIR_TEMP_MASK, + reg_val, &aux); + if (ret) { + dev_err(data->dev, "failed to write config register\n"); + return ret; + } + change = change || aux; + + /* Restore sensor to normal operation mode */ + ret = regmap_write_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK, + FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_NORMAL)); + if (ret) { + dev_err(data->dev, "failed to set normal mode\n"); + return ret; + } + /* From datasheet's table 4: electrical characteristics */ + usleep_range(3000, 3500); + + if (change) { + /* + * Check if ODR and OSR settings are valid or we are + * operating in a degraded mode. + */ + ret = regmap_read(data->regmap, BMP580_REG_EFF_OSR, &tmp); + if (ret) { + dev_err(data->dev, "error reading effective OSR register\n"); + return ret; + } + if (!(tmp & BMP580_EFF_OSR_VALID_ODR)) { + dev_warn(data->dev, "OSR and ODR incompatible settings detected\n"); + /* Set current OSR settings from data on effective OSR */ + data->oversampling_temp = FIELD_GET(BMP580_EFF_OSR_TEMP_MASK, tmp); + data->oversampling_press = FIELD_GET(BMP580_EFF_OSR_PRESS_MASK, tmp); + return -EINVAL; + } + } + + return 0; +} + +static const int bmp580_oversampling_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + +const struct bmp280_chip_info bmp580_chip_info = { + .id_reg = BMP580_REG_CHIP_ID, + .chip_id = BMP580_CHIP_ID, + .regmap_config = &bmp580_regmap_config, + .start_up_time = 2000, + .channels = bmp380_channels, + .num_channels = 2, + + .oversampling_temp_avail = bmp580_oversampling_avail, + .num_oversampling_temp_avail = ARRAY_SIZE(bmp580_oversampling_avail), + .oversampling_temp_default = ilog2(1), + + .oversampling_press_avail = bmp580_oversampling_avail, + .num_oversampling_press_avail = ARRAY_SIZE(bmp580_oversampling_avail), + .oversampling_press_default = ilog2(4), + + .sampling_freq_avail = bmp580_odr_table, + .num_sampling_freq_avail = ARRAY_SIZE(bmp580_odr_table) * 2, + .sampling_freq_default = BMP580_ODR_50HZ, + + .iir_filter_coeffs_avail = bmp380_iir_filter_coeffs_avail, + .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail), + .iir_filter_coeff_default = 2, + + .chip_config = bmp580_chip_config, + .read_temp = bmp580_read_temp, + .read_press = bmp580_read_press, + .preinit = bmp580_preinit, }; +EXPORT_SYMBOL_NS(bmp580_chip_info, IIO_BMP280); static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) { @@ -1467,7 +1872,7 @@ static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp) return (data->t_fine + 8) >> 4; } -static int bmp180_read_temp(struct bmp280_data *data, int *val) +static int bmp180_read_temp(struct bmp280_data *data, int *val, int *val2) { s32 adc_temp, comp_temp; int ret; @@ -1555,7 +1960,7 @@ static int bmp180_read_press(struct bmp280_data *data, int ret; /* Read and compensate temperature so we get a reading of t_fine. */ - ret = bmp180_read_temp(data, NULL); + ret = bmp180_read_temp(data, NULL, NULL); if (ret) return ret; @@ -1579,8 +1984,10 @@ static int bmp180_chip_config(struct bmp280_data *data) static const int bmp180_oversampling_temp_avail[] = { 1 }; static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; -static const struct bmp280_chip_info bmp180_chip_info = { +const struct bmp280_chip_info bmp180_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BMP180_CHIP_ID, + .regmap_config = &bmp180_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 2, @@ -1600,6 +2007,7 @@ static const struct bmp280_chip_info bmp180_chip_info = { .read_press = bmp180_read_press, .read_calib = bmp180_read_calib, }; +EXPORT_SYMBOL_NS(bmp180_chip_info, IIO_BMP280); static irqreturn_t bmp085_eoc_irq(int irq, void *d) { @@ -1661,11 +2069,10 @@ static void bmp280_regulators_disable(void *data) int bmp280_common_probe(struct device *dev, struct regmap *regmap, - unsigned int chip, + const struct bmp280_chip_info *chip_info, const char *name, int irq) { - const struct bmp280_chip_info *chip_info; struct iio_dev *indio_dev; struct bmp280_data *data; struct gpio_desc *gpiod; @@ -1684,22 +2091,6 @@ int bmp280_common_probe(struct device *dev, indio_dev->info = &bmp280_info; indio_dev->modes = INDIO_DIRECT_MODE; - switch (chip) { - case BMP180_CHIP_ID: - chip_info = &bmp180_chip_info; - break; - case BMP280_CHIP_ID: - chip_info = &bmp280_chip_info; - break; - case BME280_CHIP_ID: - chip_info = &bme280_chip_info; - break; - case BMP380_CHIP_ID: - chip_info = &bmp380_chip_info; - break; - default: - return -EINVAL; - } data->chip_info = chip_info; /* Apply initial values from chip info structure */ @@ -1751,17 +2142,17 @@ int bmp280_common_probe(struct device *dev, ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id); if (ret < 0) return ret; - if (chip_id != chip) { + if (chip_id != data->chip_info->chip_id) { dev_err(dev, "bad chip id: expected %x got %x\n", - chip, chip_id); + data->chip_info->chip_id, chip_id); return -EINVAL; } - /* BMP3xx requires soft-reset as part of initialization */ - if (chip_id == BMP380_CHIP_ID) { - ret = bmp380_cmd(data, BMP380_CMD_SOFT_RESET); - if (ret < 0) - return ret; + if (data->chip_info->preinit) { + ret = data->chip_info->preinit(data); + if (ret) + return dev_err_probe(data->dev, ret, + "error running preinit tasks\n"); } ret = data->chip_info->chip_config(data); @@ -1776,10 +2167,12 @@ int bmp280_common_probe(struct device *dev, * time once. They will not change. */ - ret = data->chip_info->read_calib(data); - if (ret < 0) - return dev_err_probe(data->dev, ret, - "failed to read calibration coefficients\n"); + if (data->chip_info->read_calib) { + ret = data->chip_info->read_calib(data); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "failed to read calibration coefficients\n"); + } /* * Attempt to grab an optional EOC IRQ - only the BMP085 has this diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 14eab086d24a..567b945e6427 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -8,25 +8,14 @@ static int bmp280_i2c_probe(struct i2c_client *client) { struct regmap *regmap; - const struct regmap_config *regmap_config; + const struct bmp280_chip_info *chip_info; const struct i2c_device_id *id = i2c_client_get_device_id(client); - switch (id->driver_data) { - case BMP180_CHIP_ID: - regmap_config = &bmp180_regmap_config; - break; - case BMP280_CHIP_ID: - case BME280_CHIP_ID: - regmap_config = &bmp280_regmap_config; - break; - case BMP380_CHIP_ID: - regmap_config = &bmp380_regmap_config; - break; - default: - return -EINVAL; - } + chip_info = device_get_match_data(&client->dev); + if (!chip_info) + chip_info = (const struct bmp280_chip_info *) id->driver_data; - regmap = devm_regmap_init_i2c(client, regmap_config); + regmap = devm_regmap_init_i2c(client, chip_info->regmap_config); if (IS_ERR(regmap)) { dev_err(&client->dev, "failed to allocate register map\n"); return PTR_ERR(regmap); @@ -34,27 +23,29 @@ static int bmp280_i2c_probe(struct i2c_client *client) return bmp280_common_probe(&client->dev, regmap, - id->driver_data, + chip_info, id->name, client->irq); } static const struct of_device_id bmp280_of_i2c_match[] = { - { .compatible = "bosch,bmp085", .data = (void *)BMP180_CHIP_ID }, - { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID }, - { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, - { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, - { .compatible = "bosch,bmp380", .data = (void *)BMP380_CHIP_ID }, + { .compatible = "bosch,bmp085", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp180", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp280", .data = &bmp280_chip_info }, + { .compatible = "bosch,bme280", .data = &bme280_chip_info }, + { .compatible = "bosch,bmp380", .data = &bmp380_chip_info }, + { .compatible = "bosch,bmp580", .data = &bmp580_chip_info }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); static const struct i2c_device_id bmp280_i2c_id[] = { - {"bmp085", BMP180_CHIP_ID }, - {"bmp180", BMP180_CHIP_ID }, - {"bmp280", BMP280_CHIP_ID }, - {"bme280", BME280_CHIP_ID }, - {"bmp380", BMP380_CHIP_ID }, + {"bmp085", (kernel_ulong_t)&bmp180_chip_info }, + {"bmp180", (kernel_ulong_t)&bmp180_chip_info }, + {"bmp280", (kernel_ulong_t)&bmp280_chip_info }, + {"bme280", (kernel_ulong_t)&bme280_chip_info }, + {"bmp380", (kernel_ulong_t)&bmp380_chip_info }, + {"bmp580", (kernel_ulong_t)&bmp580_chip_info }, { }, }; MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); diff --git a/drivers/iio/pressure/bmp280-regmap.c b/drivers/iio/pressure/bmp280-regmap.c index c98c67970265..3ee56720428c 100644 --- a/drivers/iio/pressure/bmp280-regmap.c +++ b/drivers/iio/pressure/bmp280-regmap.c @@ -115,6 +115,54 @@ static bool bmp380_is_volatile_reg(struct device *dev, unsigned int reg) } } +static bool bmp580_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP580_REG_NVM_DATA_MSB: + case BMP580_REG_NVM_DATA_LSB: + case BMP580_REG_NVM_ADDR: + case BMP580_REG_ODR_CONFIG: + case BMP580_REG_OSR_CONFIG: + case BMP580_REG_INT_SOURCE: + case BMP580_REG_INT_CONFIG: + case BMP580_REG_OOR_THR_MSB: + case BMP580_REG_OOR_THR_LSB: + case BMP580_REG_OOR_CONFIG: + case BMP580_REG_OOR_RANGE: + case BMP580_REG_IF_CONFIG: + case BMP580_REG_FIFO_CONFIG: + case BMP580_REG_FIFO_SEL: + case BMP580_REG_DSP_CONFIG: + case BMP580_REG_DSP_IIR: + case BMP580_REG_CMD: + return true; + default: + return false; + } +} + +static bool bmp580_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP580_REG_NVM_DATA_MSB: + case BMP580_REG_NVM_DATA_LSB: + case BMP580_REG_FIFO_COUNT: + case BMP580_REG_INT_STATUS: + case BMP580_REG_PRESS_XLSB: + case BMP580_REG_PRESS_LSB: + case BMP580_REG_PRESS_MSB: + case BMP580_REG_FIFO_DATA: + case BMP580_REG_TEMP_XLSB: + case BMP580_REG_TEMP_LSB: + case BMP580_REG_TEMP_MSB: + case BMP580_REG_EFF_OSR: + case BMP580_REG_STATUS: + return true; + default: + return false; + } +} + const struct regmap_config bmp280_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -138,3 +186,15 @@ const struct regmap_config bmp380_regmap_config = { .volatile_reg = bmp380_is_volatile_reg, }; EXPORT_SYMBOL_NS(bmp380_regmap_config, IIO_BMP280); + +const struct regmap_config bmp580_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BMP580_REG_CMD, + .cache_type = REGCACHE_RBTREE, + + .writeable_reg = bmp580_is_writeable_reg, + .volatile_reg = bmp580_is_volatile_reg, +}; +EXPORT_SYMBOL_NS(bmp580_regmap_config, IIO_BMP280); diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 011c68e07ebf..1dff9bb7c4e9 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -47,8 +47,8 @@ static struct regmap_bus bmp280_regmap_bus = { static int bmp280_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); + const struct bmp280_chip_info *chip_info; struct regmap *regmap; - const struct regmap_config *regmap_config; int ret; spi->bits_per_word = 8; @@ -58,25 +58,14 @@ static int bmp280_spi_probe(struct spi_device *spi) return ret; } - switch (id->driver_data) { - case BMP180_CHIP_ID: - regmap_config = &bmp180_regmap_config; - break; - case BMP280_CHIP_ID: - case BME280_CHIP_ID: - regmap_config = &bmp280_regmap_config; - break; - case BMP380_CHIP_ID: - regmap_config = &bmp380_regmap_config; - break; - default: - return -EINVAL; - } + chip_info = device_get_match_data(&spi->dev); + if (!chip_info) + chip_info = (const struct bmp280_chip_info *) id->driver_data; regmap = devm_regmap_init(&spi->dev, &bmp280_regmap_bus, &spi->dev, - regmap_config); + chip_info->regmap_config); if (IS_ERR(regmap)) { dev_err(&spi->dev, "failed to allocate register map\n"); return PTR_ERR(regmap); @@ -84,28 +73,30 @@ static int bmp280_spi_probe(struct spi_device *spi) return bmp280_common_probe(&spi->dev, regmap, - id->driver_data, + chip_info, id->name, spi->irq); } static const struct of_device_id bmp280_of_spi_match[] = { - { .compatible = "bosch,bmp085", }, - { .compatible = "bosch,bmp180", }, - { .compatible = "bosch,bmp181", }, - { .compatible = "bosch,bmp280", }, - { .compatible = "bosch,bme280", }, - { .compatible = "bosch,bmp380", }, + { .compatible = "bosch,bmp085", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp180", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp181", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp280", .data = &bmp280_chip_info }, + { .compatible = "bosch,bme280", .data = &bmp280_chip_info }, + { .compatible = "bosch,bmp380", .data = &bmp380_chip_info }, + { .compatible = "bosch,bmp580", .data = &bmp580_chip_info }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); static const struct spi_device_id bmp280_spi_id[] = { - { "bmp180", BMP180_CHIP_ID }, - { "bmp181", BMP180_CHIP_ID }, - { "bmp280", BMP280_CHIP_ID }, - { "bme280", BME280_CHIP_ID }, - { "bmp380", BMP380_CHIP_ID }, + { "bmp180", (kernel_ulong_t)&bmp180_chip_info }, + { "bmp181", (kernel_ulong_t)&bmp180_chip_info }, + { "bmp280", (kernel_ulong_t)&bmp280_chip_info }, + { "bme280", (kernel_ulong_t)&bmp280_chip_info }, + { "bmp380", (kernel_ulong_t)&bmp380_chip_info }, + { "bmp580", (kernel_ulong_t)&bmp580_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, bmp280_spi_id); diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index c791325c7416..5c0563ce7572 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -1,7 +1,114 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include <linux/bitops.h> #include <linux/device.h> +#include <linux/iio/iio.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> + + +/* BMP580 specific registers */ +#define BMP580_REG_CMD 0x7E +#define BMP580_REG_EFF_OSR 0x38 +#define BMP580_REG_ODR_CONFIG 0x37 +#define BMP580_REG_OSR_CONFIG 0x36 +#define BMP580_REG_IF_CONFIG 0x13 +#define BMP580_REG_REV_ID 0x02 +#define BMP580_REG_CHIP_ID 0x01 +/* OOR allows to configure a pressure alarm */ +#define BMP580_REG_OOR_CONFIG 0x35 +#define BMP580_REG_OOR_RANGE 0x34 +#define BMP580_REG_OOR_THR_MSB 0x33 +#define BMP580_REG_OOR_THR_LSB 0x32 +/* DSP registers (IIR filters) */ +#define BMP580_REG_DSP_IIR 0x31 +#define BMP580_REG_DSP_CONFIG 0x30 +/* NVM access registers */ +#define BMP580_REG_NVM_DATA_MSB 0x2D +#define BMP580_REG_NVM_DATA_LSB 0x2C +#define BMP580_REG_NVM_ADDR 0x2B +/* Status registers */ +#define BMP580_REG_STATUS 0x28 +#define BMP580_REG_INT_STATUS 0x27 +#define BMP580_REG_CHIP_STATUS 0x11 +/* Data registers */ +#define BMP580_REG_FIFO_DATA 0x29 +#define BMP580_REG_PRESS_MSB 0x22 +#define BMP580_REG_PRESS_LSB 0x21 +#define BMP580_REG_PRESS_XLSB 0x20 +#define BMP580_REG_TEMP_MSB 0x1F +#define BMP580_REG_TEMP_LSB 0x1E +#define BMP580_REG_TEMP_XLSB 0x1D +/* FIFO config registers */ +#define BMP580_REG_FIFO_SEL 0x18 +#define BMP580_REG_FIFO_COUNT 0x17 +#define BMP580_REG_FIFO_CONFIG 0x16 +/* Interruptions config registers */ +#define BMP580_REG_INT_SOURCE 0x15 +#define BMP580_REG_INT_CONFIG 0x14 + +#define BMP580_CMD_NOOP 0x00 +#define BMP580_CMD_EXTMODE_SEQ_0 0x73 +#define BMP580_CMD_EXTMODE_SEQ_1 0xB4 +#define BMP580_CMD_EXTMODE_SEQ_2 0x69 +#define BMP580_CMD_NVM_OP_SEQ_0 0x5D +#define BMP580_CMD_NVM_READ_SEQ_1 0xA5 +#define BMP580_CMD_NVM_WRITE_SEQ_1 0xA0 +#define BMP580_CMD_SOFT_RESET 0xB6 + +#define BMP580_INT_STATUS_POR_MASK BIT(4) + +#define BMP580_STATUS_CORE_RDY_MASK BIT(0) +#define BMP580_STATUS_NVM_RDY_MASK BIT(1) +#define BMP580_STATUS_NVM_ERR_MASK BIT(2) +#define BMP580_STATUS_NVM_CMD_ERR_MASK BIT(3) + +#define BMP580_OSR_PRESS_MASK GENMASK(5, 3) +#define BMP580_OSR_TEMP_MASK GENMASK(2, 0) +#define BMP580_OSR_PRESS_EN BIT(6) +#define BMP580_EFF_OSR_PRESS_MASK GENMASK(5, 3) +#define BMP580_EFF_OSR_TEMP_MASK GENMASK(2, 0) +#define BMP580_EFF_OSR_VALID_ODR BIT(7) + +#define BMP580_ODR_MASK GENMASK(6, 2) +#define BMP580_MODE_MASK GENMASK(1, 0) +#define BMP580_MODE_SLEEP 0 +#define BMP580_MODE_NORMAL 1 +#define BMP580_MODE_FORCED 2 +#define BMP580_MODE_CONTINOUS 3 +#define BMP580_ODR_DEEPSLEEP_DIS BIT(7) + +#define BMP580_DSP_COMP_MASK GENMASK(1, 0) +#define BMP580_DSP_COMP_DIS 0 +#define BMP580_DSP_TEMP_COMP_EN 1 +/* + * In section 7.27 of datasheet, modes 2 and 3 are technically the same. + * Pressure compensation means also enabling temperature compensation + */ +#define BMP580_DSP_PRESS_COMP_EN 2 +#define BMP580_DSP_PRESS_TEMP_COMP_EN 3 +#define BMP580_DSP_IIR_FORCED_FLUSH BIT(2) +#define BMP580_DSP_SHDW_IIR_TEMP_EN BIT(3) +#define BMP580_DSP_FIFO_IIR_TEMP_EN BIT(4) +#define BMP580_DSP_SHDW_IIR_PRESS_EN BIT(5) +#define BMP580_DSP_FIFO_IIR_PRESS_EN BIT(6) +#define BMP580_DSP_OOR_IIR_PRESS_EN BIT(7) + +#define BMP580_DSP_IIR_PRESS_MASK GENMASK(5, 3) +#define BMP580_DSP_IIR_TEMP_MASK GENMASK(2, 0) +#define BMP580_FILTER_OFF 0 +#define BMP580_FILTER_1X 1 +#define BMP580_FILTER_3X 2 +#define BMP580_FILTER_7X 3 +#define BMP580_FILTER_15X 4 +#define BMP580_FILTER_31X 5 +#define BMP580_FILTER_63X 6 +#define BMP580_FILTER_127X 7 + +#define BMP580_NVM_ROW_ADDR_MASK GENMASK(5, 0) +#define BMP580_NVM_PROG_EN BIT(6) + +#define BMP580_TEMP_SKIPPED 0x7f7f7f +#define BMP580_PRESS_SKIPPED 0x7f7f7f /* BMP380 specific registers */ #define BMP380_REG_CMD 0x7E @@ -181,6 +288,8 @@ #define BMP280_REG_ID 0xD0 #define BMP380_CHIP_ID 0x50 +#define BMP580_CHIP_ID 0x50 +#define BMP580_CHIP_ID_ALT 0x51 #define BMP180_CHIP_ID 0x55 #define BMP280_CHIP_ID 0x58 #define BME280_CHIP_ID 0x60 @@ -191,15 +300,177 @@ #define BMP280_PRESS_SKIPPED 0x80000 #define BMP280_HUMIDITY_SKIPPED 0x8000 +/* Core exported structs */ + +static const char *const bmp280_supply_names[] = { + "vddd", "vdda" +}; + +#define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names) + +struct bmp180_calib { + s16 AC1; + s16 AC2; + s16 AC3; + u16 AC4; + u16 AC5; + u16 AC6; + s16 B1; + s16 B2; + s16 MB; + s16 MC; + s16 MD; +}; + +/* See datasheet Section 4.2.2. */ +struct bmp280_calib { + u16 T1; + s16 T2; + s16 T3; + u16 P1; + s16 P2; + s16 P3; + s16 P4; + s16 P5; + s16 P6; + s16 P7; + s16 P8; + s16 P9; + u8 H1; + s16 H2; + u8 H3; + s16 H4; + s16 H5; + s8 H6; +}; + +/* See datasheet Section 3.11.1. */ +struct bmp380_calib { + u16 T1; + u16 T2; + s8 T3; + s16 P1; + s16 P2; + s8 P3; + s8 P4; + u16 P5; + u16 P6; + s8 P7; + s8 P8; + s16 P9; + s8 P10; + s8 P11; +}; + +struct bmp280_data { + struct device *dev; + struct mutex lock; + struct regmap *regmap; + struct completion done; + bool use_eoc; + const struct bmp280_chip_info *chip_info; + union { + struct bmp180_calib bmp180; + struct bmp280_calib bmp280; + struct bmp380_calib bmp380; + } calib; + struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; + unsigned int start_up_time; /* in microseconds */ + + /* log of base 2 of oversampling rate */ + u8 oversampling_press; + u8 oversampling_temp; + u8 oversampling_humid; + u8 iir_filter_coeff; + + /* + * BMP380 devices introduce sampling frequency configuration. See + * datasheet sections 3.3.3. and 4.3.19 for more details. + * + * BMx280 devices allowed indirect configuration of sampling frequency + * changing the t_standby duration between measurements, as detailed on + * section 3.6.3 of the datasheet. + */ + int sampling_freq; + + /* + * Carryover value from temperature conversion, used in pressure + * calculation. + */ + s32 t_fine; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + union { + /* Sensor data buffer */ + u8 buf[3]; + /* Calibration data buffers */ + __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; + __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; + u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; + /* Miscellaneous, endianess-aware data buffers */ + __le16 le16; + __be16 be16; + } __aligned(IIO_DMA_MINALIGN); +}; + +struct bmp280_chip_info { + unsigned int id_reg; + const unsigned int chip_id; + + const struct regmap_config *regmap_config; + + const struct iio_chan_spec *channels; + int num_channels; + unsigned int start_up_time; + + const int *oversampling_temp_avail; + int num_oversampling_temp_avail; + int oversampling_temp_default; + + const int *oversampling_press_avail; + int num_oversampling_press_avail; + int oversampling_press_default; + + const int *oversampling_humid_avail; + int num_oversampling_humid_avail; + int oversampling_humid_default; + + const int *iir_filter_coeffs_avail; + int num_iir_filter_coeffs_avail; + int iir_filter_coeff_default; + + const int (*sampling_freq_avail)[2]; + int num_sampling_freq_avail; + int sampling_freq_default; + + int (*chip_config)(struct bmp280_data *); + int (*read_temp)(struct bmp280_data *, int *, int *); + int (*read_press)(struct bmp280_data *, int *, int *); + int (*read_humid)(struct bmp280_data *, int *, int *); + int (*read_calib)(struct bmp280_data *); + int (*preinit)(struct bmp280_data *); +}; + +/* Chip infos for each variant */ +extern const struct bmp280_chip_info bmp180_chip_info; +extern const struct bmp280_chip_info bmp280_chip_info; +extern const struct bmp280_chip_info bme280_chip_info; +extern const struct bmp280_chip_info bmp380_chip_info; +extern const struct bmp280_chip_info bmp580_chip_info; + /* Regmap configurations */ extern const struct regmap_config bmp180_regmap_config; extern const struct regmap_config bmp280_regmap_config; extern const struct regmap_config bmp380_regmap_config; +extern const struct regmap_config bmp580_regmap_config; /* Probe called from different transports */ int bmp280_common_probe(struct device *dev, struct regmap *regmap, - unsigned int chip, + const struct bmp280_chip_info *, const char *name, int irq); diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 67119a9b95fc..421e059d1f19 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -829,7 +829,7 @@ static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data) } /* New sample available: dispatch internal trigger consumers. */ - iio_trigger_poll_chained(priv->trigger); + iio_trigger_poll_nested(priv->trigger); if (cont) /* diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index ebc95cf8f5f4..96fa97451cbf 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -257,7 +257,7 @@ static void as3935_event_work(struct work_struct *work) switch (val) { case AS3935_EVENT_INT: - iio_trigger_poll_chained(st->trig); + iio_trigger_poll_nested(st->trig); break; case AS3935_DISTURB_INT: case AS3935_NOISE_INT: diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index 977cf17cec52..9a40ca32bb1c 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -783,73 +783,75 @@ static int sx9324_write_raw(struct iio_dev *indio_dev, static const struct sx_common_reg_default sx9324_default_regs[] = { { SX9324_REG_IRQ_MSK, 0x00 }, - { SX9324_REG_IRQ_CFG0, 0x00 }, - { SX9324_REG_IRQ_CFG1, SX9324_REG_IRQ_CFG1_FAILCOND }, - { SX9324_REG_IRQ_CFG2, 0x00 }, - { SX9324_REG_GNRL_CTRL0, SX9324_REG_GNRL_CTRL0_SCANPERIOD_100MS }, + { SX9324_REG_IRQ_CFG0, 0x00, "irq_cfg0" }, + { SX9324_REG_IRQ_CFG1, SX9324_REG_IRQ_CFG1_FAILCOND, "irq_cfg1" }, + { SX9324_REG_IRQ_CFG2, 0x00, "irq_cfg2" }, + { SX9324_REG_GNRL_CTRL0, SX9324_REG_GNRL_CTRL0_SCANPERIOD_100MS, "gnrl_ctrl0" }, /* * The lower 4 bits should not be set as it enable sensors measurements. * Turning the detection on before the configuration values are set to * good values can cause the device to return erroneous readings. */ - { SX9324_REG_GNRL_CTRL1, SX9324_REG_GNRL_CTRL1_PAUSECTRL }, + { SX9324_REG_GNRL_CTRL1, SX9324_REG_GNRL_CTRL1_PAUSECTRL, "gnrl_ctrl1" }, - { SX9324_REG_AFE_CTRL0, SX9324_REG_AFE_CTRL0_RINT_LOWEST }, - { SX9324_REG_AFE_CTRL3, 0x00 }, + { SX9324_REG_AFE_CTRL0, SX9324_REG_AFE_CTRL0_RINT_LOWEST, "afe_ctrl0" }, + { SX9324_REG_AFE_CTRL3, 0x00, "afe_ctrl3" }, { SX9324_REG_AFE_CTRL4, SX9324_REG_AFE_CTRL4_FREQ_83_33HZ | - SX9324_REG_AFE_CTRL4_RES_100 }, - { SX9324_REG_AFE_CTRL6, 0x00 }, + SX9324_REG_AFE_CTRL4_RES_100, "afe_ctrl4" }, + { SX9324_REG_AFE_CTRL6, 0x00, "afe_ctrl6" }, { SX9324_REG_AFE_CTRL7, SX9324_REG_AFE_CTRL4_FREQ_83_33HZ | - SX9324_REG_AFE_CTRL4_RES_100 }, + SX9324_REG_AFE_CTRL4_RES_100, "afe_ctrl7" }, /* TODO(gwendal): PHx use chip default or all grounded? */ - { SX9324_REG_AFE_PH0, 0x29 }, - { SX9324_REG_AFE_PH1, 0x26 }, - { SX9324_REG_AFE_PH2, 0x1a }, - { SX9324_REG_AFE_PH3, 0x16 }, + { SX9324_REG_AFE_PH0, 0x29, "afe_ph0" }, + { SX9324_REG_AFE_PH1, 0x26, "afe_ph1" }, + { SX9324_REG_AFE_PH2, 0x1a, "afe_ph2" }, + { SX9324_REG_AFE_PH3, 0x16, "afe_ph3" }, { SX9324_REG_AFE_CTRL8, SX9324_REG_AFE_CTRL8_RESERVED | - SX9324_REG_AFE_CTRL8_RESFILTIN_4KOHM }, - { SX9324_REG_AFE_CTRL9, SX9324_REG_AFE_CTRL9_AGAIN_1 }, + SX9324_REG_AFE_CTRL8_RESFILTIN_4KOHM, "afe_ctrl8" }, + { SX9324_REG_AFE_CTRL9, SX9324_REG_AFE_CTRL9_AGAIN_1, "afe_ctrl9" }, { SX9324_REG_PROX_CTRL0, SX9324_REG_PROX_CTRL0_GAIN_1 << SX9324_REG_PROX_CTRL0_GAIN_SHIFT | - SX9324_REG_PROX_CTRL0_RAWFILT_1P50 }, + SX9324_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl0" }, { SX9324_REG_PROX_CTRL1, SX9324_REG_PROX_CTRL0_GAIN_1 << SX9324_REG_PROX_CTRL0_GAIN_SHIFT | - SX9324_REG_PROX_CTRL0_RAWFILT_1P50 }, - { SX9324_REG_PROX_CTRL2, SX9324_REG_PROX_CTRL2_AVGNEG_THRESH_16K }, + SX9324_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl1" }, + { SX9324_REG_PROX_CTRL2, SX9324_REG_PROX_CTRL2_AVGNEG_THRESH_16K, "prox_ctrl2" }, { SX9324_REG_PROX_CTRL3, SX9324_REG_PROX_CTRL3_AVGDEB_2SAMPLES | - SX9324_REG_PROX_CTRL3_AVGPOS_THRESH_16K }, + SX9324_REG_PROX_CTRL3_AVGPOS_THRESH_16K, "prox_ctrl3" }, { SX9324_REG_PROX_CTRL4, SX9324_REG_PROX_CTRL4_AVGNEG_FILT_2 | - SX9324_REG_PROX_CTRL4_AVGPOS_FILT_256 }, - { SX9324_REG_PROX_CTRL5, 0x00 }, - { SX9324_REG_PROX_CTRL6, SX9324_REG_PROX_CTRL6_PROXTHRESH_32 }, - { SX9324_REG_PROX_CTRL7, SX9324_REG_PROX_CTRL6_PROXTHRESH_32 }, - { SX9324_REG_ADV_CTRL0, 0x00 }, - { SX9324_REG_ADV_CTRL1, 0x00 }, - { SX9324_REG_ADV_CTRL2, 0x00 }, - { SX9324_REG_ADV_CTRL3, 0x00 }, - { SX9324_REG_ADV_CTRL4, 0x00 }, + SX9324_REG_PROX_CTRL4_AVGPOS_FILT_256, "prox_ctrl4" }, + { SX9324_REG_PROX_CTRL5, 0x00, "prox_ctrl5" }, + { SX9324_REG_PROX_CTRL6, SX9324_REG_PROX_CTRL6_PROXTHRESH_32, "prox_ctrl6" }, + { SX9324_REG_PROX_CTRL7, SX9324_REG_PROX_CTRL6_PROXTHRESH_32, "prox_ctrl7" }, + { SX9324_REG_ADV_CTRL0, 0x00, "adv_ctrl0" }, + { SX9324_REG_ADV_CTRL1, 0x00, "adv_ctrl1" }, + { SX9324_REG_ADV_CTRL2, 0x00, "adv_ctrl2" }, + { SX9324_REG_ADV_CTRL3, 0x00, "adv_ctrl3" }, + { SX9324_REG_ADV_CTRL4, 0x00, "adv_ctrl4" }, { SX9324_REG_ADV_CTRL5, SX9324_REG_ADV_CTRL5_STARTUP_SENSOR_1 | - SX9324_REG_ADV_CTRL5_STARTUP_METHOD_1 }, - { SX9324_REG_ADV_CTRL6, 0x00 }, - { SX9324_REG_ADV_CTRL7, 0x00 }, - { SX9324_REG_ADV_CTRL8, 0x00 }, - { SX9324_REG_ADV_CTRL9, 0x00 }, + SX9324_REG_ADV_CTRL5_STARTUP_METHOD_1, "adv_ctrl5" }, + { SX9324_REG_ADV_CTRL6, 0x00, "adv_ctrl6" }, + { SX9324_REG_ADV_CTRL7, 0x00, "adv_ctrl7" }, + { SX9324_REG_ADV_CTRL8, 0x00, "adv_ctrl8" }, + { SX9324_REG_ADV_CTRL9, 0x00, "adv_ctrl9" }, /* Body/Table threshold */ - { SX9324_REG_ADV_CTRL10, 0x00 }, - { SX9324_REG_ADV_CTRL11, 0x00 }, - { SX9324_REG_ADV_CTRL12, 0x00 }, + { SX9324_REG_ADV_CTRL10, 0x00, "adv_ctrl10" }, + { SX9324_REG_ADV_CTRL11, 0x00, "adv_ctrl11" }, + { SX9324_REG_ADV_CTRL12, 0x00, "adv_ctrl12" }, /* TODO(gwendal): SAR currenly disabled */ - { SX9324_REG_ADV_CTRL13, 0x00 }, - { SX9324_REG_ADV_CTRL14, 0x00 }, - { SX9324_REG_ADV_CTRL15, 0x00 }, - { SX9324_REG_ADV_CTRL16, 0x00 }, - { SX9324_REG_ADV_CTRL17, 0x00 }, - { SX9324_REG_ADV_CTRL18, 0x00 }, - { SX9324_REG_ADV_CTRL19, SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION }, - { SX9324_REG_ADV_CTRL20, SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION }, + { SX9324_REG_ADV_CTRL13, 0x00, "adv_ctrl13" }, + { SX9324_REG_ADV_CTRL14, 0x00, "adv_ctrl14" }, + { SX9324_REG_ADV_CTRL15, 0x00, "adv_ctrl15" }, + { SX9324_REG_ADV_CTRL16, 0x00, "adv_ctrl16" }, + { SX9324_REG_ADV_CTRL17, 0x00, "adv_ctrl17" }, + { SX9324_REG_ADV_CTRL18, 0x00, "adv_ctrl18" }, + { SX9324_REG_ADV_CTRL19, + SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION, "adv_ctrl19" }, + { SX9324_REG_ADV_CTRL20, + SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION, "adv_ctrl20" }, }; /* Activate all channels and perform an initial compensation. */ @@ -889,13 +891,15 @@ sx9324_get_default_reg(struct device *dev, int idx, const char *res; memcpy(reg_def, &sx9324_default_regs[idx], sizeof(*reg_def)); + + sx_common_get_raw_register_config(dev, reg_def); switch (reg_def->reg) { case SX9324_REG_AFE_PH0: case SX9324_REG_AFE_PH1: case SX9324_REG_AFE_PH2: case SX9324_REG_AFE_PH3: ph = reg_def->reg - SX9324_REG_AFE_PH0; - scnprintf(prop, ARRAY_SIZE(prop), "semtech,ph%d-pin", ph); + snprintf(prop, ARRAY_SIZE(prop), "semtech,ph%d-pin", ph); count = device_property_count_u32(dev, prop); if (count != ARRAY_SIZE(pin_defs)) diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c index 6e19d22e6a01..a50d9176411a 100644 --- a/drivers/iio/proximity/sx9360.c +++ b/drivers/iio/proximity/sx9360.c @@ -663,37 +663,37 @@ static int sx9360_write_raw(struct iio_dev *indio_dev, static const struct sx_common_reg_default sx9360_default_regs[] = { { SX9360_REG_IRQ_MSK, 0x00 }, - { SX9360_REG_IRQ_CFG, 0x00 }, + { SX9360_REG_IRQ_CFG, 0x00, "irq_cfg" }, /* * The lower 2 bits should not be set as it enable sensors measurements. * Turning the detection on before the configuration values are set to * good values can cause the device to return erroneous readings. */ - { SX9360_REG_GNRL_CTRL0, 0x00 }, - { SX9360_REG_GNRL_CTRL1, 0x00 }, - { SX9360_REG_GNRL_CTRL2, SX9360_REG_GNRL_CTRL2_PERIOD_102MS }, + { SX9360_REG_GNRL_CTRL0, 0x00, "gnrl_ctrl0" }, + { SX9360_REG_GNRL_CTRL1, 0x00, "gnrl_ctrl1" }, + { SX9360_REG_GNRL_CTRL2, SX9360_REG_GNRL_CTRL2_PERIOD_102MS, "gnrl_ctrl2" }, - { SX9360_REG_AFE_CTRL1, SX9360_REG_AFE_CTRL1_RESFILTIN_0OHMS }, + { SX9360_REG_AFE_CTRL1, SX9360_REG_AFE_CTRL1_RESFILTIN_0OHMS, "afe_ctrl0" }, { SX9360_REG_AFE_PARAM0_PHR, SX9360_REG_AFE_PARAM0_RSVD | - SX9360_REG_AFE_PARAM0_RESOLUTION_128 }, + SX9360_REG_AFE_PARAM0_RESOLUTION_128, "afe_param0_phr" }, { SX9360_REG_AFE_PARAM1_PHR, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF | - SX9360_REG_AFE_PARAM1_FREQ_83_33HZ }, + SX9360_REG_AFE_PARAM1_FREQ_83_33HZ, "afe_param1_phr" }, { SX9360_REG_AFE_PARAM0_PHM, SX9360_REG_AFE_PARAM0_RSVD | - SX9360_REG_AFE_PARAM0_RESOLUTION_128 }, + SX9360_REG_AFE_PARAM0_RESOLUTION_128, "afe_param0_phm" }, { SX9360_REG_AFE_PARAM1_PHM, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF | - SX9360_REG_AFE_PARAM1_FREQ_83_33HZ }, + SX9360_REG_AFE_PARAM1_FREQ_83_33HZ, "afe_param1_phm" }, { SX9360_REG_PROX_CTRL0_PHR, SX9360_REG_PROX_CTRL0_GAIN_1 | - SX9360_REG_PROX_CTRL0_RAWFILT_1P50 }, + SX9360_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl0_phr" }, { SX9360_REG_PROX_CTRL0_PHM, SX9360_REG_PROX_CTRL0_GAIN_1 | - SX9360_REG_PROX_CTRL0_RAWFILT_1P50 }, - { SX9360_REG_PROX_CTRL1, SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K }, + SX9360_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl0_phm" }, + { SX9360_REG_PROX_CTRL1, SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K, "prox_ctrl1" }, { SX9360_REG_PROX_CTRL2, SX9360_REG_PROX_CTRL2_AVGDEB_2SAMPLES | - SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K }, + SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K, "prox_ctrl2" }, { SX9360_REG_PROX_CTRL3, SX9360_REG_PROX_CTRL3_AVGNEG_FILT_2 | - SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256 }, - { SX9360_REG_PROX_CTRL4, 0x00 }, - { SX9360_REG_PROX_CTRL5, SX9360_REG_PROX_CTRL5_PROXTHRESH_32 }, + SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256, "prox_ctrl3" }, + { SX9360_REG_PROX_CTRL4, 0x00, "prox_ctrl4" }, + { SX9360_REG_PROX_CTRL5, SX9360_REG_PROX_CTRL5_PROXTHRESH_32, "prox_ctrl5" }, }; /* Activate all channels and perform an initial compensation. */ diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 8794e75e5bf9..9b2cfcade6a4 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -1051,8 +1051,8 @@ MODULE_DEVICE_TABLE(i2c, sx9500_id); static struct i2c_driver sx9500_driver = { .driver = { .name = SX9500_DRIVER_NAME, - .acpi_match_table = ACPI_PTR(sx9500_acpi_match), - .of_match_table = of_match_ptr(sx9500_of_match), + .acpi_match_table = sx9500_acpi_match, + .of_match_table = sx9500_of_match, .pm = pm_sleep_ptr(&sx9500_pm_ops), }, .probe_new = sx9500_probe, diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index eba9256730ec..fe07d1444ac3 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -424,6 +424,27 @@ static const struct iio_buffer_setup_ops sx_common_buffer_setup_ops = { .postdisable = sx_common_buffer_postdisable, }; +void sx_common_get_raw_register_config(struct device *dev, + struct sx_common_reg_default *reg_def) +{ +#ifdef CONFIG_ACPI + struct acpi_device *adev = ACPI_COMPANION(dev); + u32 raw = 0, ret; + char prop[80]; + + if (!reg_def->property || !adev) + return; + + snprintf(prop, ARRAY_SIZE(prop), "%s,reg_%s", acpi_device_hid(adev), reg_def->property); + ret = device_property_read_u32(dev, prop, &raw); + if (ret) + return; + + reg_def->def = raw; +#endif +} +EXPORT_SYMBOL_NS_GPL(sx_common_get_raw_register_config, SEMTECH_PROX); + #define SX_COMMON_SOFT_RESET 0xde static int sx_common_init_device(struct device *dev, struct iio_dev *indio_dev) diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h index 49d4517103b0..101b90e52ff2 100644 --- a/drivers/iio/proximity/sx_common.h +++ b/drivers/iio/proximity/sx_common.h @@ -8,6 +8,7 @@ #ifndef IIO_SX_COMMON_H #define IIO_SX_COMMON_H +#include <linux/acpi.h> #include <linux/iio/iio.h> #include <linux/iio/types.h> #include <linux/regulator/consumer.h> @@ -26,6 +27,7 @@ static_assert(SX_COMMON_MAX_NUM_CHANNELS < BITS_PER_LONG); struct sx_common_reg_default { u8 reg; u8 def; + const char *property; }; /** @@ -101,7 +103,6 @@ struct sx_common_chip_info { * @client: I2C client structure. * @trig: IIO trigger object. * @regmap: Register map. - * @num_default_regs: Number of default registers to set at init. * @chan_prox_stat: Last reading of the proximity status for each channel. * We only send an event to user space when this changes. * @trigger_enabled: True when the device trigger is enabled. @@ -149,6 +150,9 @@ int sx_common_probe(struct i2c_client *client, const struct sx_common_chip_info *chip_info, const struct regmap_config *regmap_config); +void sx_common_get_raw_register_config(struct device *dev, + struct sx_common_reg_default *reg_def); + /* 3 is the number of events defined by a single phase. */ extern const struct iio_event_spec sx_common_events[3]; diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index f9b8f2b570f6..638e3a5bd6b8 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -16,6 +16,7 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/limits.h> +#include <linux/property.h> #include <linux/iio/iio.h> @@ -31,17 +32,19 @@ #define TMP117_REG_DEVICE_ID 0xF #define TMP117_RESOLUTION_10UC 78125 -#define TMP117_DEVICE_ID 0x0117 #define MICRODEGREE_PER_10MILLIDEGREE 10000 +#define TMP116_DEVICE_ID 0x1116 +#define TMP117_DEVICE_ID 0x0117 + struct tmp117_data { struct i2c_client *client; s16 calibbias; }; static int tmp117_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *channel, int *val, - int *val2, long mask) + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) { struct tmp117_data *data = iio_priv(indio_dev); s32 ret; @@ -49,7 +52,7 @@ static int tmp117_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: ret = i2c_smbus_read_word_swapped(data->client, - TMP117_REG_TEMP); + TMP117_REG_TEMP); if (ret < 0) return ret; *val = sign_extend32(ret, 15); @@ -57,7 +60,7 @@ static int tmp117_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_CALIBBIAS: ret = i2c_smbus_read_word_swapped(data->client, - TMP117_REG_TEMP_OFFSET); + TMP117_REG_TEMP_OFFSET); if (ret < 0) return ret; *val = sign_extend32(ret, 15); @@ -79,9 +82,8 @@ static int tmp117_read_raw(struct iio_dev *indio_dev, } } -static int tmp117_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *channel, int val, - int val2, long mask) +static int tmp117_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec + const *channel, int val, int val2, long mask) { struct tmp117_data *data = iio_priv(indio_dev); s16 off; @@ -104,7 +106,16 @@ static const struct iio_chan_spec tmp117_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_chan_spec tmp116_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), }, }; @@ -115,23 +126,41 @@ static const struct iio_info tmp117_info = { static int tmp117_identify(struct i2c_client *client) { + const struct i2c_device_id *id; + unsigned long match_data; int dev_id; dev_id = i2c_smbus_read_word_swapped(client, TMP117_REG_DEVICE_ID); if (dev_id < 0) return dev_id; - if (dev_id != TMP117_DEVICE_ID) { - dev_err(&client->dev, "TMP117 not found\n"); - return -ENODEV; + + switch (dev_id) { + case TMP116_DEVICE_ID: + case TMP117_DEVICE_ID: + return dev_id; } - return 0; + + dev_info(&client->dev, "Unknown device id (0x%x), use fallback compatible\n", + dev_id); + + match_data = (uintptr_t)device_get_match_data(&client->dev); + if (match_data) + return match_data; + + id = i2c_client_get_device_id(client); + if (id) + return id->driver_data; + + dev_err(&client->dev, "Failed to identify unsupported device\n"); + + return -ENODEV; } static int tmp117_probe(struct i2c_client *client) { struct tmp117_data *data; struct iio_dev *indio_dev; - int ret; + int ret, dev_id; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EOPNOTSUPP; @@ -140,6 +169,8 @@ static int tmp117_probe(struct i2c_client *client) if (ret < 0) return ret; + dev_id = ret; + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -148,24 +179,35 @@ static int tmp117_probe(struct i2c_client *client) data->client = client; data->calibbias = 0; - indio_dev->name = "tmp117"; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &tmp117_info; - indio_dev->channels = tmp117_channels; - indio_dev->num_channels = ARRAY_SIZE(tmp117_channels); + switch (dev_id) { + case TMP116_DEVICE_ID: + indio_dev->channels = tmp116_channels; + indio_dev->num_channels = ARRAY_SIZE(tmp116_channels); + indio_dev->name = "tmp116"; + break; + case TMP117_DEVICE_ID: + indio_dev->channels = tmp117_channels; + indio_dev->num_channels = ARRAY_SIZE(tmp117_channels); + indio_dev->name = "tmp117"; + break; + } return devm_iio_device_register(&client->dev, indio_dev); } static const struct of_device_id tmp117_of_match[] = { - { .compatible = "ti,tmp117", }, + { .compatible = "ti,tmp116", .data = (void *)TMP116_DEVICE_ID }, + { .compatible = "ti,tmp117", .data = (void *)TMP117_DEVICE_ID }, { } }; MODULE_DEVICE_TABLE(of, tmp117_of_match); static const struct i2c_device_id tmp117_id[] = { - { "tmp117", 0 }, + { "tmp116", TMP116_DEVICE_ID }, + { "tmp117", TMP117_DEVICE_ID }, { } }; MODULE_DEVICE_TABLE(i2c, tmp117_id); diff --git a/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c index 96ec06bbe546..7aaed0611899 100644 --- a/drivers/iio/trigger/iio-trig-loop.c +++ b/drivers/iio/trigger/iio-trig-loop.c @@ -46,7 +46,7 @@ static int iio_loop_thread(void *data) set_freezable(); do { - iio_trigger_poll_chained(trig); + iio_trigger_poll_nested(trig); } while (likely(!kthread_freezable_should_stop(NULL))); return 0; |