summaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-04-27 21:07:50 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2023-04-27 21:07:50 +0200
commitcec24b8b6bb841a19b5c5555b600a511a8988100 (patch)
treeb12115ba8e6e6929cea0658ee3c9dae9aad8a82d /drivers/iio
parentMerge tag 'driver-core-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/... (diff)
parentmcb-lpc: Reallocate memory region to avoid memory overlapping (diff)
downloadlinux-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')
-rw-r--r--drivers/iio/Kconfig3
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/accel/bma400_core.c2
-rw-r--r--drivers/iio/accel/kionix-kx022a.c3
-rw-r--r--drivers/iio/accel/mma8452.c2
-rw-r--r--drivers/iio/accel/msa311.c2
-rw-r--r--drivers/iio/accel/st_accel.h1
-rw-r--r--drivers/iio/accel/st_accel_core.c1
-rw-r--r--drivers/iio/accel/st_accel_i2c.c5
-rw-r--r--drivers/iio/accel/st_accel_spi.c5
-rw-r--r--drivers/iio/adc/Kconfig10
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/ad7606.c2
-rw-r--r--drivers/iio/adc/at91-sama5d2_adc.c8
-rw-r--r--drivers/iio/adc/axp20x_adc.c77
-rw-r--r--drivers/iio/adc/max11410.c2
-rw-r--r--drivers/iio/adc/meson_saradc.c21
-rw-r--r--drivers/iio/adc/palmas_gpadc.c615
-rw-r--r--drivers/iio/adc/qcom-pm8xxx-xoadc.c2
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c2
-rw-r--r--drivers/iio/adc/stm32-adc.c6
-rw-r--r--drivers/iio/adc/ti-ads1100.c445
-rw-r--r--drivers/iio/addac/Kconfig2
-rw-r--r--drivers/iio/addac/ad74413r.c44
-rw-r--r--drivers/iio/addac/stx104.c462
-rw-r--r--drivers/iio/chemical/sps30_i2c.c6
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_trigger.c4
-rw-r--r--drivers/iio/dac/Kconfig1
-rw-r--r--drivers/iio/dac/ad5592r-base.c5
-rw-r--r--drivers/iio/dac/cio-dac.c68
-rw-r--r--drivers/iio/dac/max5522.c2
-rw-r--r--drivers/iio/frequency/admv1013.c21
-rw-r--r--drivers/iio/gyro/fxas21002c_core.c2
-rw-r--r--drivers/iio/gyro/mpu3050-core.c2
-rw-r--r--drivers/iio/humidity/hts221_buffer.c2
-rw-r--r--drivers/iio/imu/adis16475.c6
-rw-r--r--drivers/iio/imu/st_lsm6dsx/Kconfig4
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h15
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c59
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c99
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c21
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c5
-rw-r--r--drivers/iio/industrialio-gts-helper.c1077
-rw-r--r--drivers/iio/industrialio-trigger.c17
-rw-r--r--drivers/iio/light/Kconfig14
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/acpi-als.c2
-rw-r--r--drivers/iio/light/max44009.c13
-rw-r--r--drivers/iio/light/rohm-bu27034.c1497
-rw-r--r--drivers/iio/light/rpr0521.c2
-rw-r--r--drivers/iio/light/st_uvis25_core.c2
-rw-r--r--drivers/iio/light/vcnl4000.c2
-rw-r--r--drivers/iio/light/vcnl4035.c2
-rw-r--r--drivers/iio/potentiostat/lmp91000.c2
-rw-r--r--drivers/iio/pressure/Kconfig6
-rw-r--r--drivers/iio/pressure/bmp280-core.c765
-rw-r--r--drivers/iio/pressure/bmp280-i2c.c45
-rw-r--r--drivers/iio/pressure/bmp280-regmap.c60
-rw-r--r--drivers/iio/pressure/bmp280-spi.c47
-rw-r--r--drivers/iio/pressure/bmp280.h273
-rw-r--r--drivers/iio/pressure/zpa2326.c2
-rw-r--r--drivers/iio/proximity/as3935.c2
-rw-r--r--drivers/iio/proximity/sx9324.c96
-rw-r--r--drivers/iio/proximity/sx9360.c32
-rw-r--r--drivers/iio/proximity/sx9500.c4
-rw-r--r--drivers/iio/proximity/sx_common.c21
-rw-r--r--drivers/iio/proximity/sx_common.h6
-rw-r--r--drivers/iio/temperature/tmp117.c80
-rw-r--r--drivers/iio/trigger/iio-trig-loop.c2
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, &regval);
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(&reg->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), &reg->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, &reg->ssr_ad);
- while (ioread8(&reg->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(&reg->ssr_ad);
+ mutex_unlock(&priv->lock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
/* get ADC bipolar/unipolar configuration */
- adc_config = ioread8(&reg->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(&reg->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],
+ &gts->avail_all_scales_table[i * 2],
+ &gts->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(&times[j + 1], &times[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(&gts->itime_table[t]);
+ if (ret)
+ return ret;
+ }
+
+ for (g = 0; g < gts->num_hwgain; g++) {
+ ret = sanity_check_gain(&gts->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,
+ &gts->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, &reg);
+ 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, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to reestablish comms after reset\n");
+ return ret;
+ }
+
+ ret = regmap_read(data->regmap, BMP580_REG_INT_STATUS, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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;