summaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/accel/Kconfig2
-rw-r--r--drivers/iio/accel/bma400.h4
-rw-r--r--drivers/iio/accel/bma400_core.c29
-rw-r--r--drivers/iio/accel/mma9551_core.c10
-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/Kconfig34
-rw-r--r--drivers/iio/adc/Makefile3
-rw-r--r--drivers/iio/adc/ad7291.c2
-rw-r--r--drivers/iio/adc/at91-sama5d2_adc.c4
-rw-r--r--drivers/iio/adc/ep93xx_adc.c8
-rw-r--r--drivers/iio/adc/imx93_adc.c484
-rw-r--r--drivers/iio/adc/max11410.c3
-rw-r--r--drivers/iio/adc/qcom-spmi-adc5.c8
-rw-r--r--drivers/iio/adc/stm32-dfsdm-core.c99
-rw-r--r--drivers/iio/adc/stm32-dfsdm.h60
-rw-r--r--drivers/iio/adc/ti-adc128s052.c54
-rw-r--r--drivers/iio/adc/ti-ads7924.c474
-rw-r--r--drivers/iio/adc/ti-lmp92064.c332
-rw-r--r--drivers/iio/adc/xilinx-ams.c9
-rw-r--r--drivers/iio/cdc/ad7746.c3
-rw-r--r--drivers/iio/chemical/scd30_core.c46
-rw-r--r--drivers/iio/common/scmi_sensors/scmi_iio.c4
-rw-r--r--drivers/iio/dac/Kconfig21
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/ad5686.c7
-rw-r--r--drivers/iio/dac/ad5686.h1
-rw-r--r--drivers/iio/dac/ad5696-i2c.c2
-rw-r--r--drivers/iio/dac/max5522.c207
-rw-r--r--drivers/iio/imu/bno055/bno055_ser_trace.c2
-rw-r--r--drivers/iio/imu/kmx61.c2
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h5
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c12
-rw-r--r--drivers/iio/industrialio-core.c64
-rw-r--r--drivers/iio/light/Makefile2
-rw-r--r--drivers/iio/light/max44009.c5
-rw-r--r--drivers/iio/light/tsl2563.c189
-rw-r--r--drivers/iio/light/vcnl4000.c449
-rw-r--r--drivers/iio/magnetometer/Kconfig14
-rw-r--r--drivers/iio/magnetometer/Makefile2
-rw-r--r--drivers/iio/magnetometer/st_magn.h1
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c1
-rw-r--r--drivers/iio/magnetometer/st_magn_i2c.c5
-rw-r--r--drivers/iio/magnetometer/st_magn_spi.c5
-rw-r--r--drivers/iio/magnetometer/tmag5273.c743
-rw-r--r--drivers/iio/pressure/ms5611.h4
-rw-r--r--drivers/iio/pressure/ms5611_core.c49
-rw-r--r--drivers/iio/pressure/ms5611_i2c.c6
-rw-r--r--drivers/iio/pressure/ms5611_spi.c6
51 files changed, 2981 insertions, 508 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 03ac410c162e..b6b45d359f28 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -380,7 +380,7 @@ config IIO_ST_ACCEL_3AXIS
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics accelerometers:
- LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
+ LSM303C, LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
LNG2DM, LIS3DE, LIS2DE12, LIS2HH12
diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index 36edbaff4f7f..932358b45f17 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -141,10 +141,6 @@
#define BMA400_SCALE_MIN 9577
#define BMA400_SCALE_MAX 76617
-#define BMA400_NUM_REGULATORS 2
-#define BMA400_VDD_REGULATOR 0
-#define BMA400_VDDIO_REGULATOR 1
-
extern const struct regmap_config bma400_regmap_config;
int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index b612d0146d4d..623f37cbaf50 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -98,7 +98,6 @@ enum bma400_activity {
struct bma400_data {
struct device *dev;
struct regmap *regmap;
- struct regulator_bulk_data regulators[BMA400_NUM_REGULATORS];
struct mutex mutex; /* data register lock */
struct iio_mount_matrix orientation;
enum bma400_power_mode power_mode;
@@ -832,13 +831,6 @@ static void bma400_init_tables(void)
}
}
-static void bma400_regulators_disable(void *data_ptr)
-{
- struct bma400_data *data = data_ptr;
-
- regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
-}
-
static void bma400_power_disable(void *data_ptr)
{
struct bma400_data *data = data_ptr;
@@ -868,30 +860,17 @@ static enum iio_modifier bma400_act_to_mod(enum bma400_activity activity)
static int bma400_init(struct bma400_data *data)
{
+ static const char * const regulator_names[] = { "vdd", "vddio" };
unsigned int val;
int ret;
- data->regulators[BMA400_VDD_REGULATOR].supply = "vdd";
- data->regulators[BMA400_VDDIO_REGULATOR].supply = "vddio";
- ret = devm_regulator_bulk_get(data->dev,
- ARRAY_SIZE(data->regulators),
- data->regulators);
+ ret = devm_regulator_bulk_get_enable(data->dev,
+ ARRAY_SIZE(regulator_names),
+ regulator_names);
if (ret)
return dev_err_probe(data->dev, ret, "Failed to get regulators: %d\n",
ret);
- ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
- data->regulators);
- if (ret) {
- dev_err(data->dev, "Failed to enable regulators: %d\n",
- ret);
- return ret;
- }
-
- ret = devm_add_action_or_reset(data->dev, bma400_regulators_disable, data);
- if (ret)
- return ret;
-
/* Try to read chip_id register. It must return 0x90. */
ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val);
if (ret) {
diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
index 64ca7d7a9673..b898f865fb87 100644
--- a/drivers/iio/accel/mma9551_core.c
+++ b/drivers/iio/accel/mma9551_core.c
@@ -296,9 +296,12 @@ int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, (u8 *)&v, 2);
+ if (ret < 0)
+ return ret;
+
*val = be16_to_cpu(v);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_NS(mma9551_read_config_word, IIO_MMA9551);
@@ -354,9 +357,12 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, (u8 *)&v, 2);
+ if (ret < 0)
+ return ret;
+
*val = be16_to_cpu(v);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_NS(mma9551_read_status_word, IIO_MMA9551);
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
index 5b0f54e33d9e..56ed0c776d4a 100644
--- a/drivers/iio/accel/st_accel.h
+++ b/drivers/iio/accel/st_accel.h
@@ -37,6 +37,7 @@
#define LIS2DE12_ACCEL_DEV_NAME "lis2de12"
#define LIS2HH12_ACCEL_DEV_NAME "lis2hh12"
#define LIS302DL_ACCEL_DEV_NAME "lis302dl"
+#define LSM303C_ACCEL_DEV_NAME "lsm303c_accel"
#define SC7A20_ACCEL_DEV_NAME "sc7a20"
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index c8c8eb15c34e..6b8562f684d5 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -929,6 +929,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS2HH12_ACCEL_DEV_NAME,
+ [1] = LSM303C_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
.odr = {
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index 45ee0ddc133c..3f02fd5d5946 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -112,6 +112,10 @@ static const struct of_device_id st_accel_of_match[] = {
.data = LIS302DL_ACCEL_DEV_NAME,
},
{
+ .compatible = "st,lsm303c-accel",
+ .data = LSM303C_ACCEL_DEV_NAME,
+ },
+ {
.compatible = "silan,sc7a20",
.data = SC7A20_ACCEL_DEV_NAME,
},
@@ -151,6 +155,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LIS2DE12_ACCEL_DEV_NAME },
{ LIS2HH12_ACCEL_DEV_NAME },
{ LIS302DL_ACCEL_DEV_NAME },
+ { LSM303C_ACCEL_DEV_NAME },
{ SC7A20_ACCEL_DEV_NAME },
{},
};
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index 6c0917750288..5740dc1820bd 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -96,6 +96,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lis302dl",
.data = LIS302DL_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "st,lsm303c-accel",
+ .data = LSM303C_ACCEL_DEV_NAME,
+ },
{}
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -152,6 +156,7 @@ static const struct spi_device_id st_accel_id_table[] = {
{ LIS3DHH_ACCEL_DEV_NAME },
{ LIS3DE_ACCEL_DEV_NAME },
{ LIS302DL_ACCEL_DEV_NAME },
+ { LSM303C_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 99cd305b59d9..45af2302be53 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -441,7 +441,8 @@ config ENVELOPE_DETECTOR
config EP93XX_ADC
tristate "Cirrus Logic EP93XX ADC driver"
- depends on ARCH_EP93XX
+ depends on ARCH_EP93XX || COMPILE_TEST
+ depends on HAS_IOMEM
help
Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic.
It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this
@@ -565,6 +566,16 @@ config IMX8QXP_ADC
This driver can also be built as a module. If so, the module will be
called imx8qxp-adc.
+config IMX93_ADC
+ tristate "IMX93 ADC driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for IMX93 ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called imx93_adc.
+
config LP8788_ADC
tristate "LP8788 ADC driver"
depends on MFD_LP8788
@@ -1207,6 +1218,17 @@ config TI_ADS1015
This driver can also be built as a module. If so, the module will be
called ti-ads1015.
+config TI_ADS7924
+ tristate "Texas Instruments ADS7924 ADC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for Texas Instruments ADS7924
+ 4 channels, 12-bit I2C ADC chip.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads7924.
+
config TI_ADS7950
tristate "Texas Instruments ADS7950 ADC driver"
depends on SPI && GPIOLIB
@@ -1274,6 +1296,16 @@ config TI_AM335X_ADC
To compile this driver as a module, choose M here: the module will be
called ti_am335x_adc.
+config TI_LMP92064
+ tristate "Texas Instruments LMP92064 ADC driver"
+ depends on SPI
+ help
+ Say yes here to build support for the LMP92064 Precision Current and Voltage
+ sensor.
+
+ This driver can also be built as a module. If so, the module will be called
+ ti-lmp92064.
+
config TI_TLC4541
tristate "Texas Instruments TLC4541 ADC driver"
depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 4ef41a7dfac6..36c18177322a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_HI8435) += hi8435.o
obj-$(CONFIG_HX711) += hx711.o
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o
+obj-$(CONFIG_IMX93_ADC) += imx93_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o
obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o
@@ -107,12 +108,14 @@ 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_ADS7924) += ti-ads7924.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+obj-$(CONFIG_TI_LMP92064) += ti-lmp92064.o
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c
index 3dd0105f63d7..f9ee189925de 100644
--- a/drivers/iio/adc/ad7291.c
+++ b/drivers/iio/adc/ad7291.c
@@ -179,7 +179,7 @@ static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan,
offset = AD7291_VOLTAGE_OFFSET;
break;
default:
- return 0;
+ return 0;
}
switch (info) {
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index ed4f8501bda8..50d02e5fc6fc 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -2181,7 +2181,7 @@ static ssize_t at91_adc_get_fifo_state(struct device *dev,
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
- return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan);
+ return sysfs_emit(buf, "%d\n", !!st->dma_st.dma_chan);
}
static ssize_t at91_adc_get_watermark(struct device *dev,
@@ -2190,7 +2190,7 @@ static ssize_t at91_adc_get_watermark(struct device *dev,
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
- return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark);
+ return sysfs_emit(buf, "%d\n", st->dma_st.watermark);
}
static IIO_DEVICE_ATTR(hwfifo_enabled, 0444,
diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c
index fd5a9404c8dc..a35e6cead67d 100644
--- a/drivers/iio/adc/ep93xx_adc.c
+++ b/drivers/iio/adc/ep93xx_adc.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
/*
* This code could benefit from real HR Timers, but jiffy granularity would
@@ -227,9 +228,16 @@ static int ep93xx_adc_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id ep93xx_adc_of_ids[] = {
+ { .compatible = "cirrus,ep9301-adc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ep93xx_adc_of_ids);
+
static struct platform_driver ep93xx_adc_driver = {
.driver = {
.name = "ep93xx-adc",
+ .of_match_table = ep93xx_adc_of_ids,
},
.probe = ep93xx_adc_probe,
.remove = ep93xx_adc_remove,
diff --git a/drivers/iio/adc/imx93_adc.c b/drivers/iio/adc/imx93_adc.c
new file mode 100644
index 000000000000..a775d2e40567
--- /dev/null
+++ b/drivers/iio/adc/imx93_adc.c
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NXP i.MX93 ADC driver
+ *
+ * Copyright 2023 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#define IMX93_ADC_DRIVER_NAME "imx93-adc"
+
+/* Register map definition */
+#define IMX93_ADC_MCR 0x00
+#define IMX93_ADC_MSR 0x04
+#define IMX93_ADC_ISR 0x10
+#define IMX93_ADC_IMR 0x20
+#define IMX93_ADC_CIMR0 0x24
+#define IMX93_ADC_CTR0 0x94
+#define IMX93_ADC_NCMR0 0xA4
+#define IMX93_ADC_PCDR0 0x100
+#define IMX93_ADC_PCDR1 0x104
+#define IMX93_ADC_PCDR2 0x108
+#define IMX93_ADC_PCDR3 0x10c
+#define IMX93_ADC_PCDR4 0x110
+#define IMX93_ADC_PCDR5 0x114
+#define IMX93_ADC_PCDR6 0x118
+#define IMX93_ADC_PCDR7 0x11c
+#define IMX93_ADC_CALSTAT 0x39C
+
+/* ADC bit shift */
+#define IMX93_ADC_MCR_MODE_MASK BIT(29)
+#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
+#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
+#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
+#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
+#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
+#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
+#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
+#define IMX93_ADC_ISR_ECH_MASK BIT(0)
+#define IMX93_ADC_ISR_EOC_MASK BIT(1)
+#define IMX93_ADC_ISR_EOC_ECH_MASK (IMX93_ADC_ISR_EOC_MASK | \
+ IMX93_ADC_ISR_ECH_MASK)
+#define IMX93_ADC_IMR_JEOC_MASK BIT(3)
+#define IMX93_ADC_IMR_JECH_MASK BIT(2)
+#define IMX93_ADC_IMR_EOC_MASK BIT(1)
+#define IMX93_ADC_IMR_ECH_MASK BIT(0)
+#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
+
+/* ADC status */
+#define IMX93_ADC_MSR_ADCSTATUS_IDLE 0
+#define IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN 1
+#define IMX93_ADC_MSR_ADCSTATUS_WAIT_STATE 2
+#define IMX93_ADC_MSR_ADCSTATUS_BUSY_IN_CALIBRATION 3
+#define IMX93_ADC_MSR_ADCSTATUS_SAMPLE 4
+#define IMX93_ADC_MSR_ADCSTATUS_CONVERSION 6
+
+#define IMX93_ADC_TIMEOUT msecs_to_jiffies(100)
+
+struct imx93_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *ipg_clk;
+ int irq;
+ struct regulator *vref;
+ /* lock to protect against multiple access to the device */
+ struct mutex lock;
+ struct completion completion;
+};
+
+#define IMX93_ADC_CHAN(_idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec imx93_adc_iio_channels[] = {
+ IMX93_ADC_CHAN(0),
+ IMX93_ADC_CHAN(1),
+ IMX93_ADC_CHAN(2),
+ IMX93_ADC_CHAN(3),
+};
+
+static void imx93_adc_power_down(struct imx93_adc *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) ==
+ IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN),
+ 1, 50);
+ if (ret == -ETIMEDOUT)
+ dev_warn(adc->dev,
+ "ADC do not in power down mode, current MSR is %x\n",
+ msr);
+}
+
+static void imx93_adc_power_up(struct imx93_adc *adc)
+{
+ u32 mcr;
+
+ /* bring ADC out of power down state, in idle state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+}
+
+static void imx93_adc_config_ad_clk(struct imx93_adc *adc)
+{
+ u32 mcr;
+
+ /* put adc in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config the AD_CLK equal to bus clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ imx93_adc_power_up(adc);
+}
+
+static int imx93_adc_calibration(struct imx93_adc *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ /* make sure ADC in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config SAR controller operating clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ imx93_adc_power_up(adc);
+
+ /*
+ * TODO: we use the default TSAMP/NRSMPL/AVGEN in MCR,
+ * can add the setting of these bit if need in future.
+ */
+
+ /* run calibration */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* wait calibration to be finished */
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 1000, 2000000);
+ if (ret == -ETIMEDOUT) {
+ dev_warn(adc->dev, "ADC do not finish calibration in 2 min!\n");
+ imx93_adc_power_down(adc);
+ return ret;
+ }
+
+ /* check whether calbration is success or not */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+ if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
+ dev_warn(adc->dev, "ADC calibration failed!\n");
+ imx93_adc_power_down(adc);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int imx93_adc_read_channel_conversion(struct imx93_adc *adc,
+ int channel_number,
+ int *result)
+{
+ u32 channel;
+ u32 imr, mcr, pcda;
+ long ret;
+
+ reinit_completion(&adc->completion);
+
+ /* config channel mask register */
+ channel = 1 << channel_number;
+ writel(channel, adc->regs + IMX93_ADC_NCMR0);
+
+ /* TODO: can config desired sample time in CTRn if need */
+
+ /* config interrupt mask */
+ imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
+ writel(imr, adc->regs + IMX93_ADC_IMR);
+ writel(channel, adc->regs + IMX93_ADC_CIMR0);
+
+ /* config one-shot mode */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* start normal conversion */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = wait_for_completion_interruptible_timeout(&adc->completion,
+ IMX93_ADC_TIMEOUT);
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ if (ret < 0)
+ return ret;
+
+ pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel_number * 4);
+
+ *result = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
+
+ return ret;
+}
+
+static int imx93_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct imx93_adc *adc = iio_priv(indio_dev);
+ struct device *dev = adc->dev;
+ long ret;
+ u32 vref_uv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ pm_runtime_get_sync(dev);
+ mutex_lock(&adc->lock);
+ ret = imx93_adc_read_channel_conversion(adc, chan->channel, val);
+ mutex_unlock(&adc->lock);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_sync_autosuspend(dev);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = vref_uv = regulator_get_voltage(adc->vref);
+ if (ret < 0)
+ return ret;
+ *val = vref_uv / 1000;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = clk_get_rate(adc->ipg_clk);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t imx93_adc_isr(int irq, void *dev_id)
+{
+ struct imx93_adc *adc = dev_id;
+ u32 isr, eoc, unexpected;
+
+ isr = readl(adc->regs + IMX93_ADC_ISR);
+
+ if (FIELD_GET(IMX93_ADC_ISR_EOC_ECH_MASK, isr)) {
+ eoc = isr & IMX93_ADC_ISR_EOC_ECH_MASK;
+ writel(eoc, adc->regs + IMX93_ADC_ISR);
+ complete(&adc->completion);
+ }
+
+ unexpected = isr & ~IMX93_ADC_ISR_EOC_ECH_MASK;
+ if (unexpected) {
+ writel(unexpected, adc->regs + IMX93_ADC_ISR);
+ dev_err(adc->dev, "Unexpected interrupt 0x%08x.\n", unexpected);
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info imx93_adc_iio_info = {
+ .read_raw = &imx93_adc_read_raw,
+};
+
+static int imx93_adc_probe(struct platform_device *pdev)
+{
+ struct imx93_adc *adc;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed allocating iio device\n");
+
+ adc = iio_priv(indio_dev);
+ adc->dev = dev;
+
+ mutex_init(&adc->lock);
+ adc->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(adc->regs))
+ return dev_err_probe(dev, PTR_ERR(adc->regs),
+ "Failed getting ioremap resource\n");
+
+ /* The third irq is for ADC conversion usage */
+ adc->irq = platform_get_irq(pdev, 2);
+ if (adc->irq < 0)
+ return adc->irq;
+
+ adc->ipg_clk = devm_clk_get(dev, "ipg");
+ if (IS_ERR(adc->ipg_clk))
+ return dev_err_probe(dev, PTR_ERR(adc->ipg_clk),
+ "Failed getting clock.\n");
+
+ adc->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(adc->vref))
+ return dev_err_probe(dev, PTR_ERR(adc->vref),
+ "Failed getting reference voltage.\n");
+
+ ret = regulator_enable(adc->vref);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable reference voltage.\n");
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&adc->completion);
+
+ indio_dev->name = "imx93-adc";
+ indio_dev->info = &imx93_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = imx93_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(imx93_adc_iio_channels);
+
+ ret = clk_prepare_enable(adc->ipg_clk);
+ if (ret) {
+ dev_err_probe(dev, ret,
+ "Failed to enable ipg clock.\n");
+ goto error_regulator_disable;
+ }
+
+ ret = request_irq(adc->irq, imx93_adc_isr, 0, IMX93_ADC_DRIVER_NAME, adc);
+ if (ret < 0) {
+ dev_err_probe(dev, ret,
+ "Failed requesting irq, irq = %d\n", adc->irq);
+ goto error_ipg_clk_disable;
+ }
+
+ ret = imx93_adc_calibration(adc);
+ if (ret < 0)
+ goto error_free_adc_irq;
+
+ imx93_adc_config_ad_clk(adc);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err_probe(dev, ret,
+ "Failed to register this iio device.\n");
+ goto error_adc_power_down;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, 50);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+
+error_adc_power_down:
+ imx93_adc_power_down(adc);
+error_free_adc_irq:
+ free_irq(adc->irq, adc);
+error_ipg_clk_disable:
+ clk_disable_unprepare(adc->ipg_clk);
+error_regulator_disable:
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static int imx93_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct imx93_adc *adc = iio_priv(indio_dev);
+ struct device *dev = adc->dev;
+
+ /* adc power down need clock on */
+ pm_runtime_get_sync(dev);
+
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_put_noidle(dev);
+
+ iio_device_unregister(indio_dev);
+ imx93_adc_power_down(adc);
+ free_irq(adc->irq, adc);
+ clk_disable_unprepare(adc->ipg_clk);
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static int imx93_adc_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx93_adc *adc = iio_priv(indio_dev);
+
+ imx93_adc_power_down(adc);
+ clk_disable_unprepare(adc->ipg_clk);
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static int imx93_adc_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx93_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(adc->vref);
+ if (ret) {
+ dev_err(dev,
+ "Can't enable adc reference top voltage, err = %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(adc->ipg_clk);
+ if (ret) {
+ dev_err(dev, "Could not prepare or enable clock.\n");
+ goto err_disable_reg;
+ }
+
+ imx93_adc_power_up(adc);
+
+ return 0;
+
+err_disable_reg:
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(imx93_adc_pm_ops,
+ imx93_adc_runtime_suspend,
+ imx93_adc_runtime_resume, NULL);
+
+static const struct of_device_id imx93_adc_match[] = {
+ { .compatible = "nxp,imx93-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx93_adc_match);
+
+static struct platform_driver imx93_adc_driver = {
+ .probe = imx93_adc_probe,
+ .remove = imx93_adc_remove,
+ .driver = {
+ .name = IMX93_ADC_DRIVER_NAME,
+ .of_match_table = imx93_adc_match,
+ .pm = pm_ptr(&imx93_adc_pm_ops),
+ },
+};
+
+module_platform_driver(imx93_adc_driver);
+
+MODULE_DESCRIPTION("NXP i.MX93 ADC driver");
+MODULE_AUTHOR("Haibo Chen <haibo.chen@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c
index fdc9f03135b5..b74b689ee7de 100644
--- a/drivers/iio/adc/max11410.c
+++ b/drivers/iio/adc/max11410.c
@@ -4,7 +4,6 @@
*
* Copyright 2022 Analog Devices Inc.
*/
-#include <asm-generic/unaligned.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -16,6 +15,8 @@
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
+#include <asm/unaligned.h>
+
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
index 821fee60a765..e90c299c913a 100644
--- a/drivers/iio/adc/qcom-spmi-adc5.c
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -543,6 +543,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_DEFAULT)
[ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 0,
SCALE_HW_CALIB_XOTHERM)
+ [ADC5_BAT_ID_100K_PU] = ADC5_CHAN_TEMP("bat_id", 0,
+ SCALE_HW_CALIB_DEFAULT)
[ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0,
@@ -894,10 +896,8 @@ static int adc5_probe(struct platform_device *pdev)
mutex_init(&adc->lock);
ret = adc5_get_fw_data(adc);
- if (ret) {
- dev_err(dev, "adc get dt data failed\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "adc get dt data failed\n");
irq_eoc = platform_get_irq(pdev, 0);
if (irq_eoc < 0) {
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
index a3d4de6ba4c2..0362df285a57 100644
--- a/drivers/iio/adc/stm32-dfsdm-core.c
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -6,6 +6,7 @@
* Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -19,7 +20,15 @@
#include "stm32-dfsdm.h"
+/**
+ * struct stm32_dfsdm_dev_data - DFSDM compatible configuration data
+ * @ipid: DFSDM identification number. Used only if hardware provides identification registers
+ * @num_filters: DFSDM number of filters. Unused if identification registers are available
+ * @num_channels: DFSDM number of channels. Unused if identification registers are available
+ * @regmap_cfg: SAI register map configuration pointer
+ */
struct stm32_dfsdm_dev_data {
+ u32 ipid;
unsigned int num_filters;
unsigned int num_channels;
const struct regmap_config *regmap_cfg;
@@ -27,8 +36,6 @@ struct stm32_dfsdm_dev_data {
#define STM32H7_DFSDM_NUM_FILTERS 4
#define STM32H7_DFSDM_NUM_CHANNELS 8
-#define STM32MP1_DFSDM_NUM_FILTERS 6
-#define STM32MP1_DFSDM_NUM_CHANNELS 8
static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
{
@@ -75,8 +82,7 @@ static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = {
};
static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = {
- .num_filters = STM32MP1_DFSDM_NUM_FILTERS,
- .num_channels = STM32MP1_DFSDM_NUM_CHANNELS,
+ .ipid = STM32MP15_IPIDR_NUMBER,
.regmap_cfg = &stm32mp1_dfsdm_regmap_cfg,
};
@@ -295,6 +301,65 @@ static const struct of_device_id stm32_dfsdm_of_match[] = {
};
MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
+static int stm32_dfsdm_probe_identification(struct platform_device *pdev,
+ struct dfsdm_priv *priv,
+ const struct stm32_dfsdm_dev_data *dev_data)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ struct stm32_dfsdm *dfsdm = &priv->dfsdm;
+ const char *compat;
+ int ret, count = 0;
+ u32 id, val;
+
+ if (!dev_data->ipid) {
+ dfsdm->num_fls = dev_data->num_filters;
+ dfsdm->num_chs = dev_data->num_channels;
+ return 0;
+ }
+
+ ret = regmap_read(dfsdm->regmap, DFSDM_IPIDR, &id);
+ if (ret)
+ return ret;
+
+ if (id != dev_data->ipid) {
+ dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id);
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(np, child) {
+ ret = of_property_read_string(child, "compatible", &compat);
+ if (ret)
+ continue;
+ /* Count only child nodes with dfsdm compatible */
+ if (strstr(compat, "dfsdm"))
+ count++;
+ }
+
+ ret = regmap_read(dfsdm->regmap, DFSDM_HWCFGR, &val);
+ if (ret)
+ return ret;
+
+ dfsdm->num_fls = FIELD_GET(DFSDM_HWCFGR_NBF_MASK, val);
+ dfsdm->num_chs = FIELD_GET(DFSDM_HWCFGR_NBT_MASK, val);
+
+ if (count > dfsdm->num_fls) {
+ dev_err(&pdev->dev, "Unexpected child number: %d", count);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(dfsdm->regmap, DFSDM_VERR, &val);
+ if (ret)
+ return ret;
+
+ dev_dbg(&pdev->dev, "DFSDM version: %lu.%lu. %d channels/%d filters\n",
+ FIELD_GET(DFSDM_VERR_MAJREV_MASK, val),
+ FIELD_GET(DFSDM_VERR_MINREV_MASK, val),
+ dfsdm->num_chs, dfsdm->num_fls);
+
+ return 0;
+}
+
static int stm32_dfsdm_probe(struct platform_device *pdev)
{
struct dfsdm_priv *priv;
@@ -311,18 +376,6 @@ static int stm32_dfsdm_probe(struct platform_device *pdev)
dev_data = of_device_get_match_data(&pdev->dev);
dfsdm = &priv->dfsdm;
- dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
- sizeof(*dfsdm->fl_list), GFP_KERNEL);
- if (!dfsdm->fl_list)
- return -ENOMEM;
-
- dfsdm->num_fls = dev_data->num_filters;
- dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
- sizeof(*dfsdm->ch_list),
- GFP_KERNEL);
- if (!dfsdm->ch_list)
- return -ENOMEM;
- dfsdm->num_chs = dev_data->num_channels;
ret = stm32_dfsdm_parse_of(pdev, priv);
if (ret < 0)
@@ -338,6 +391,20 @@ static int stm32_dfsdm_probe(struct platform_device *pdev)
return ret;
}
+ ret = stm32_dfsdm_probe_identification(pdev, priv, dev_data);
+ if (ret < 0)
+ return ret;
+
+ dfsdm->fl_list = devm_kcalloc(&pdev->dev, dfsdm->num_fls,
+ sizeof(*dfsdm->fl_list), GFP_KERNEL);
+ if (!dfsdm->fl_list)
+ return -ENOMEM;
+
+ dfsdm->ch_list = devm_kcalloc(&pdev->dev, dfsdm->num_chs,
+ sizeof(*dfsdm->ch_list), GFP_KERNEL);
+ if (!dfsdm->ch_list)
+ return -ENOMEM;
+
platform_set_drvdata(pdev, dfsdm);
ret = stm32_dfsdm_clk_prepare_enable(dfsdm);
diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
index 4afc1f528b78..570a1552aec4 100644
--- a/drivers/iio/adc/stm32-dfsdm.h
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -13,25 +13,29 @@
/*
* STM32 DFSDM - global register map
- * ________________________________________________________
- * | Offset | Registers block |
- * --------------------------------------------------------
- * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS |
- * --------------------------------------------------------
- * | 0x020 | CHANNEL 1 |
- * --------------------------------------------------------
- * | ... | ..... |
- * --------------------------------------------------------
- * | 0x0E0 | CHANNEL 7 |
- * --------------------------------------------------------
- * | 0x100 | FILTER 0 + COMMON FILTER FIELDs |
- * --------------------------------------------------------
- * | 0x200 | FILTER 1 |
- * --------------------------------------------------------
- * | 0x300 | FILTER 2 |
- * --------------------------------------------------------
- * | 0x400 | FILTER 3 |
- * --------------------------------------------------------
+ * __________________________________________________________
+ * | Offset | Registers block |
+ * ----------------------------------------------------------
+ * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS |
+ * ----------------------------------------------------------
+ * | 0x020 | CHANNEL 1 |
+ * ----------------------------------------------------------
+ * | ... | ..... |
+ * ----------------------------------------------------------
+ * | 0x20 x n | CHANNEL n |
+ * ----------------------------------------------------------
+ * | 0x100 | FILTER 0 + COMMON FILTER FIELDs |
+ * ----------------------------------------------------------
+ * | 0x200 | FILTER 1 |
+ * ----------------------------------------------------------
+ * | | ..... |
+ * ----------------------------------------------------------
+ * | 0x100 x m | FILTER m |
+ * ----------------------------------------------------------
+ * | | ..... |
+ * ----------------------------------------------------------
+ * | 0x7F0-7FC | Identification registers |
+ * ----------------------------------------------------------
*/
/*
@@ -231,6 +235,24 @@
#define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8)
#define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
+/*
+ * Identification register definitions
+ */
+#define DFSDM_HWCFGR 0x7F0
+#define DFSDM_VERR 0x7F4
+#define DFSDM_IPIDR 0x7F8
+#define DFSDM_SIDR 0x7FC
+
+/* HWCFGR: Hardware configuration register */
+#define DFSDM_HWCFGR_NBT_MASK GENMASK(7, 0)
+#define DFSDM_HWCFGR_NBF_MASK GENMASK(15, 8)
+
+/* VERR: Version register */
+#define DFSDM_VERR_MINREV_MASK GENMASK(3, 0)
+#define DFSDM_VERR_MAJREV_MASK GENMASK(7, 4)
+
+#define STM32MP15_IPIDR_NUMBER 0x00110031
+
/* DFSDM filter order */
enum stm32_dfsdm_sinc_order {
DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index b3d5b9b7255b..a456ea78462f 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -9,14 +9,13 @@
* https://www.ti.com/lit/ds/symlink/adc124s021.pdf
*/
-#include <linux/acpi.h>
#include <linux/err.h>
-#include <linux/spi/spi.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
struct adc128_configuration {
const struct iio_chan_spec *channels;
@@ -139,16 +138,11 @@ static void adc128_disable_regulator(void *reg)
static int adc128_probe(struct spi_device *spi)
{
+ const struct adc128_configuration *config;
struct iio_dev *indio_dev;
- unsigned int config;
struct adc128 *adc;
int ret;
- if (dev_fwnode(&spi->dev))
- config = (unsigned long) device_get_match_data(&spi->dev);
- else
- config = spi_get_device_id(spi)->driver_data;
-
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
@@ -160,8 +154,10 @@ static int adc128_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &adc128_info;
- indio_dev->channels = adc128_config[config].channels;
- indio_dev->num_channels = adc128_config[config].num_channels;
+ config = spi_get_device_match_data(spi);
+
+ indio_dev->channels = config->channels;
+ indio_dev->num_channels = config->num_channels;
adc->reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(adc->reg))
@@ -181,42 +177,40 @@ static int adc128_probe(struct spi_device *spi)
}
static const struct of_device_id adc128_of_match[] = {
- { .compatible = "ti,adc128s052", .data = (void*)0L, },
- { .compatible = "ti,adc122s021", .data = (void*)1L, },
- { .compatible = "ti,adc122s051", .data = (void*)1L, },
- { .compatible = "ti,adc122s101", .data = (void*)1L, },
- { .compatible = "ti,adc124s021", .data = (void*)2L, },
- { .compatible = "ti,adc124s051", .data = (void*)2L, },
- { .compatible = "ti,adc124s101", .data = (void*)2L, },
+ { .compatible = "ti,adc128s052", .data = &adc128_config[0] },
+ { .compatible = "ti,adc122s021", .data = &adc128_config[1] },
+ { .compatible = "ti,adc122s051", .data = &adc128_config[1] },
+ { .compatible = "ti,adc122s101", .data = &adc128_config[1] },
+ { .compatible = "ti,adc124s021", .data = &adc128_config[2] },
+ { .compatible = "ti,adc124s051", .data = &adc128_config[2] },
+ { .compatible = "ti,adc124s101", .data = &adc128_config[2] },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, adc128_of_match);
static const struct spi_device_id adc128_id[] = {
- { "adc128s052", 0 }, /* index into adc128_config */
- { "adc122s021", 1 },
- { "adc122s051", 1 },
- { "adc122s101", 1 },
- { "adc124s021", 2 },
- { "adc124s051", 2 },
- { "adc124s101", 2 },
+ { "adc128s052", (kernel_ulong_t)&adc128_config[0] },
+ { "adc122s021", (kernel_ulong_t)&adc128_config[1] },
+ { "adc122s051", (kernel_ulong_t)&adc128_config[1] },
+ { "adc122s101", (kernel_ulong_t)&adc128_config[1] },
+ { "adc124s021", (kernel_ulong_t)&adc128_config[2] },
+ { "adc124s051", (kernel_ulong_t)&adc128_config[2] },
+ { "adc124s101", (kernel_ulong_t)&adc128_config[2] },
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);
-#ifdef CONFIG_ACPI
static const struct acpi_device_id adc128_acpi_match[] = {
- { "AANT1280", 2 }, /* ADC124S021 compatible ACPI ID */
+ { "AANT1280", (kernel_ulong_t)&adc128_config[2] },
{ }
};
MODULE_DEVICE_TABLE(acpi, adc128_acpi_match);
-#endif
static struct spi_driver adc128_driver = {
.driver = {
.name = "adc128s052",
.of_match_table = adc128_of_match,
- .acpi_match_table = ACPI_PTR(adc128_acpi_match),
+ .acpi_match_table = adc128_acpi_match,
},
.probe = adc128_probe,
.id_table = adc128_id,
diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c
new file mode 100644
index 000000000000..b02abb026966
--- /dev/null
+++ b/drivers/iio/adc/ti-ads7924.c
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IIO driver for Texas Instruments ADS7924 ADC, 12-bit, 4-Channels, I2C
+ *
+ * Author: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+ * Copyright 2022 DimOnOff
+ *
+ * based on iio/adc/ti-ads1015.c
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * Datasheet: https://www.ti.com/lit/gpn/ads7924
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#define ADS7924_CHANNELS 4
+#define ADS7924_BITS 12
+#define ADS7924_DATA_SHIFT 4
+
+/* Registers. */
+#define ADS7924_MODECNTRL_REG 0x00
+#define ADS7924_INTCNTRL_REG 0x01
+#define ADS7924_DATA0_U_REG 0x02
+#define ADS7924_DATA0_L_REG 0x03
+#define ADS7924_DATA1_U_REG 0x04
+#define ADS7924_DATA1_L_REG 0x05
+#define ADS7924_DATA2_U_REG 0x06
+#define ADS7924_DATA2_L_REG 0x07
+#define ADS7924_DATA3_U_REG 0x08
+#define ADS7924_DATA3_L_REG 0x09
+#define ADS7924_ULR0_REG 0x0A
+#define ADS7924_LLR0_REG 0x0B
+#define ADS7924_ULR1_REG 0x0C
+#define ADS7924_LLR1_REG 0x0D
+#define ADS7924_ULR2_REG 0x0E
+#define ADS7924_LLR2_REG 0x0F
+#define ADS7924_ULR3_REG 0x10
+#define ADS7924_LLR3_REG 0x11
+#define ADS7924_INTCONFIG_REG 0x12
+#define ADS7924_SLPCONFIG_REG 0x13
+#define ADS7924_ACQCONFIG_REG 0x14
+#define ADS7924_PWRCONFIG_REG 0x15
+#define ADS7924_RESET_REG 0x16
+
+/*
+ * Register address INC bit: when set to '1', the register address is
+ * automatically incremented after every register read which allows convenient
+ * reading of multiple registers. Set INC to '0' when reading a single register.
+ */
+#define ADS7924_AUTO_INCREMENT_BIT BIT(7)
+
+#define ADS7924_MODECNTRL_MODE_MASK GENMASK(7, 2)
+
+#define ADS7924_MODECNTRL_SEL_MASK GENMASK(1, 0)
+
+#define ADS7924_CFG_INTPOL_BIT 1
+#define ADS7924_CFG_INTTRIG_BIT 0
+
+#define ADS7924_CFG_INTPOL_MASK BIT(ADS7924_CFG_INTPOL_BIT)
+#define ADS7924_CFG_INTTRIG_MASK BIT(ADS7924_CFG_INTTRIG_BIT)
+
+/* Interrupt pin polarity */
+#define ADS7924_CFG_INTPOL_LOW 0
+#define ADS7924_CFG_INTPOL_HIGH 1
+
+/* Interrupt pin signaling */
+#define ADS7924_CFG_INTTRIG_LEVEL 0
+#define ADS7924_CFG_INTTRIG_EDGE 1
+
+/* Mode control values */
+#define ADS7924_MODECNTRL_IDLE 0x00
+#define ADS7924_MODECNTRL_AWAKE 0x20
+#define ADS7924_MODECNTRL_MANUAL_SINGLE 0x30
+#define ADS7924_MODECNTRL_MANUAL_SCAN 0x32
+#define ADS7924_MODECNTRL_AUTO_SINGLE 0x31
+#define ADS7924_MODECNTRL_AUTO_SCAN 0x33
+#define ADS7924_MODECNTRL_AUTO_SINGLE_SLEEP 0x39
+#define ADS7924_MODECNTRL_AUTO_SCAN_SLEEP 0x3B
+#define ADS7924_MODECNTRL_AUTO_BURST_SLEEP 0x3F
+
+#define ADS7924_ACQTIME_MASK GENMASK(4, 0)
+
+#define ADS7924_PWRUPTIME_MASK GENMASK(4, 0)
+
+/*
+ * The power-up time is allowed to elapse whenever the device has been shutdown
+ * in idle mode. Power-up time can allow external circuits, such as an
+ * operational amplifier, between the MUXOUT and ADCIN pins to turn on.
+ * The nominal time programmed by the PUTIME[4:0] register bits is given by:
+ * t PU = PWRUPTIME[4:0] × 2 μs
+ * If a power-up time is not required, set the bits to '0' to effectively bypass.
+ */
+#define ADS7924_PWRUPTIME_US 0 /* Bypass (0us). */
+
+/*
+ * Acquisition Time according to ACQTIME[4:0] register bits.
+ * The Acquisition Time is given by:
+ * t ACQ = (ACQTIME[4:0] × 2 μs) + 6 μs
+ * Using default value of 0 for ACQTIME[4:0] results in a minimum acquisition
+ * time of 6us.
+ */
+#define ADS7924_ACQTIME_US 6
+
+/* The conversion time is always 4μs and cannot be programmed by the user. */
+#define ADS7924_CONVTIME_US 4
+
+#define ADS7924_TOTAL_CONVTIME_US (ADS7924_PWRUPTIME_US + ADS7924_ACQTIME_US + \
+ ADS7924_CONVTIME_US)
+
+#define ADS7924_V_CHAN(_chan, _addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _chan, \
+ .address = _addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "AIN"#_chan, \
+}
+
+struct ads7924_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator *vref_reg;
+
+ /* GPIO descriptor for device hard-reset pin. */
+ struct gpio_desc *reset_gpio;
+
+ /*
+ * Protects ADC ops, e.g: concurrent sysfs/buffered
+ * data reads, configuration updates
+ */
+ struct mutex lock;
+
+ /*
+ * Set to true when the ADC is switched to the continuous-conversion
+ * mode and exits from a power-down state. This flag is used to avoid
+ * getting the stale result from the conversion register.
+ */
+ bool conv_invalid;
+};
+
+static bool ads7924_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADS7924_MODECNTRL_REG:
+ case ADS7924_INTCNTRL_REG:
+ case ADS7924_ULR0_REG:
+ case ADS7924_LLR0_REG:
+ case ADS7924_ULR1_REG:
+ case ADS7924_LLR1_REG:
+ case ADS7924_ULR2_REG:
+ case ADS7924_LLR2_REG:
+ case ADS7924_ULR3_REG:
+ case ADS7924_LLR3_REG:
+ case ADS7924_INTCONFIG_REG:
+ case ADS7924_SLPCONFIG_REG:
+ case ADS7924_ACQCONFIG_REG:
+ case ADS7924_PWRCONFIG_REG:
+ case ADS7924_RESET_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config ads7924_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ADS7924_RESET_REG,
+ .writeable_reg = ads7924_is_writeable_reg,
+};
+
+static const struct iio_chan_spec ads7924_channels[] = {
+ ADS7924_V_CHAN(0, ADS7924_DATA0_U_REG),
+ ADS7924_V_CHAN(1, ADS7924_DATA1_U_REG),
+ ADS7924_V_CHAN(2, ADS7924_DATA2_U_REG),
+ ADS7924_V_CHAN(3, ADS7924_DATA3_U_REG),
+};
+
+static int ads7924_get_adc_result(struct ads7924_data *data,
+ struct iio_chan_spec const *chan, int *val)
+{
+ int ret;
+ __be16 be_val;
+
+ if (chan->channel < 0 || chan->channel >= ADS7924_CHANNELS)
+ return -EINVAL;
+
+ if (data->conv_invalid) {
+ int conv_time;
+
+ conv_time = ADS7924_TOTAL_CONVTIME_US;
+ /* Allow 10% for internal clock inaccuracy. */
+ conv_time += conv_time / 10;
+ usleep_range(conv_time, conv_time + 1);
+ data->conv_invalid = false;
+ }
+
+ ret = regmap_raw_read(data->regmap, ADS7924_AUTO_INCREMENT_BIT |
+ chan->address, &be_val, sizeof(be_val));
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpu(be_val) >> ADS7924_DATA_SHIFT;
+
+ return 0;
+}
+
+static int ads7924_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret, vref_uv;
+ struct ads7924_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->lock);
+ ret = ads7924_get_adc_result(data, chan, val);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ vref_uv = regulator_get_voltage(data->vref_reg);
+ if (vref_uv < 0)
+ return vref_uv;
+
+ *val = vref_uv / 1000; /* Convert reg voltage to mV */
+ *val2 = ADS7924_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ads7924_info = {
+ .read_raw = ads7924_read_raw,
+};
+
+static int ads7924_get_channels_config(struct i2c_client *client,
+ struct iio_dev *indio_dev)
+{
+ struct ads7924_data *priv = iio_priv(indio_dev);
+ struct device *dev = priv->dev;
+ struct fwnode_handle *node;
+ int num_channels = 0;
+
+ device_for_each_child_node(dev, node) {
+ u32 pval;
+ unsigned int channel;
+
+ if (fwnode_property_read_u32(node, "reg", &pval)) {
+ dev_err(dev, "invalid reg on %pfw\n", node);
+ continue;
+ }
+
+ channel = pval;
+ if (channel >= ADS7924_CHANNELS) {
+ dev_err(dev, "invalid channel index %d on %pfw\n",
+ channel, node);
+ continue;
+ }
+
+ num_channels++;
+ }
+
+ if (!num_channels)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ads7924_set_conv_mode(struct ads7924_data *data, int mode)
+{
+ int ret;
+ unsigned int mode_field;
+ struct device *dev = data->dev;
+
+ /*
+ * When switching between modes, be sure to first select the Awake mode
+ * and then switch to the desired mode. This procedure ensures the
+ * internal control logic is properly synchronized.
+ */
+ if (mode != ADS7924_MODECNTRL_IDLE) {
+ mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK,
+ ADS7924_MODECNTRL_AWAKE);
+
+ ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG,
+ ADS7924_MODECNTRL_MODE_MASK,
+ mode_field);
+ if (ret) {
+ dev_err(dev, "failed to set awake mode (%pe)\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ }
+
+ mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK, mode);
+
+ ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG,
+ ADS7924_MODECNTRL_MODE_MASK, mode_field);
+ if (ret)
+ dev_err(dev, "failed to set mode %d (%pe)\n", mode,
+ ERR_PTR(ret));
+
+ return ret;
+}
+
+static int ads7924_reset(struct iio_dev *indio_dev)
+{
+ struct ads7924_data *data = iio_priv(indio_dev);
+
+ if (data->reset_gpio) {
+ gpiod_set_value(data->reset_gpio, 1); /* Assert. */
+ /* Educated guess: assert time not specified in datasheet... */
+ mdelay(100);
+ gpiod_set_value(data->reset_gpio, 0); /* Deassert. */
+ return 0;
+ }
+
+ /*
+ * A write of 10101010 to this register will generate a
+ * software reset of the ADS7924.
+ */
+ return regmap_write(data->regmap, ADS7924_RESET_REG, 0b10101010);
+};
+
+static void ads7924_reg_disable(void *data)
+{
+ regulator_disable(data);
+}
+
+static void ads7924_set_idle_mode(void *data)
+{
+ ads7924_set_conv_mode(data, ADS7924_MODECNTRL_IDLE);
+}
+
+static int ads7924_probe(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev;
+ struct ads7924_data *data;
+ struct device *dev = &client->dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return dev_err_probe(dev, -ENOMEM,
+ "failed to allocate iio device\n");
+
+ data = iio_priv(indio_dev);
+
+ data->dev = dev;
+
+ /* Initialize the reset GPIO as output with an initial value of 0. */
+ data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(data->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(data->reset_gpio),
+ "failed to get request reset GPIO\n");
+
+ mutex_init(&data->lock);
+
+ indio_dev->name = "ads7924";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->channels = ads7924_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ads7924_channels);
+ indio_dev->info = &ads7924_info;
+
+ ret = ads7924_get_channels_config(client, indio_dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to get channels configuration\n");
+
+ data->regmap = devm_regmap_init_i2c(client, &ads7924_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "failed to init regmap\n");
+
+ data->vref_reg = devm_regulator_get(dev, "vref");
+ if (IS_ERR(data->vref_reg))
+ return dev_err_probe(dev, PTR_ERR(data->vref_reg),
+ "failed to get vref regulator\n");
+
+ ret = regulator_enable(data->vref_reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to enable regulator\n");
+
+ ret = devm_add_action_or_reset(dev, ads7924_reg_disable, data->vref_reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to add regulator disable action\n");
+
+ ret = ads7924_reset(indio_dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to reset device\n");
+
+ ret = ads7924_set_conv_mode(data, ADS7924_MODECNTRL_AUTO_SCAN);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set conversion mode\n");
+
+ ret = devm_add_action_or_reset(dev, ads7924_set_idle_mode, data);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to add idle mode action\n");
+
+ /* Use minimum signal acquire time. */
+ ret = regmap_update_bits(data->regmap, ADS7924_ACQCONFIG_REG,
+ ADS7924_ACQTIME_MASK,
+ FIELD_PREP(ADS7924_ACQTIME_MASK, 0));
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to configure signal acquire time\n");
+
+ /* Disable power-up time. */
+ ret = regmap_update_bits(data->regmap, ADS7924_PWRCONFIG_REG,
+ ADS7924_PWRUPTIME_MASK,
+ FIELD_PREP(ADS7924_PWRUPTIME_MASK, 0));
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to configure power-up time\n");
+
+ data->conv_invalid = true;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to register IIO device\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id ads7924_id[] = {
+ { "ads7924", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ads7924_id);
+
+static const struct of_device_id ads7924_of_match[] = {
+ { .compatible = "ti,ads7924", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ads7924_of_match);
+
+static struct i2c_driver ads7924_driver = {
+ .driver = {
+ .name = "ads7924",
+ .of_match_table = ads7924_of_match,
+ },
+ .probe_new = ads7924_probe,
+ .id_table = ads7924_id,
+};
+
+module_i2c_driver(ads7924_driver);
+
+MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@dimonoff.com>");
+MODULE_DESCRIPTION("Texas Instruments ADS7924 ADC I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c
new file mode 100644
index 000000000000..c30ed824924f
--- /dev/null
+++ b/drivers/iio/adc/ti-lmp92064.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments LMP92064 SPI ADC driver
+ *
+ * Copyright (c) 2022 Leonard Göhrs <kernel@pengutronix.de>, Pengutronix
+ *
+ * Based on linux/drivers/iio/adc/ti-tsc2046.c
+ * Copyright (c) 2021 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+
+#define TI_LMP92064_REG_CONFIG_A 0x0000
+#define TI_LMP92064_REG_CONFIG_B 0x0001
+#define TI_LMP92064_REG_CHIP_REV 0x0006
+
+#define TI_LMP92064_REG_MFR_ID1 0x000C
+#define TI_LMP92064_REG_MFR_ID2 0x000D
+
+#define TI_LMP92064_REG_REG_UPDATE 0x000F
+#define TI_LMP92064_REG_CONFIG_REG 0x0100
+#define TI_LMP92064_REG_STATUS 0x0103
+
+#define TI_LMP92064_REG_DATA_VOUT_LSB 0x0200
+#define TI_LMP92064_REG_DATA_VOUT_MSB 0x0201
+#define TI_LMP92064_REG_DATA_COUT_LSB 0x0202
+#define TI_LMP92064_REG_DATA_COUT_MSB 0x0203
+
+#define TI_LMP92064_VAL_CONFIG_A 0x99
+#define TI_LMP92064_VAL_CONFIG_B 0x00
+#define TI_LMP92064_VAL_STATUS_OK 0x01
+
+/*
+ * Channel number definitions for the two channels of the device
+ * - IN Current (INC)
+ * - IN Voltage (INV)
+ */
+#define TI_LMP92064_CHAN_INC 0
+#define TI_LMP92064_CHAN_INV 1
+
+static const struct regmap_range lmp92064_readable_reg_ranges[] = {
+ regmap_reg_range(TI_LMP92064_REG_CONFIG_A, TI_LMP92064_REG_CHIP_REV),
+ regmap_reg_range(TI_LMP92064_REG_MFR_ID1, TI_LMP92064_REG_MFR_ID2),
+ regmap_reg_range(TI_LMP92064_REG_REG_UPDATE, TI_LMP92064_REG_REG_UPDATE),
+ regmap_reg_range(TI_LMP92064_REG_CONFIG_REG, TI_LMP92064_REG_CONFIG_REG),
+ regmap_reg_range(TI_LMP92064_REG_STATUS, TI_LMP92064_REG_STATUS),
+ regmap_reg_range(TI_LMP92064_REG_DATA_VOUT_LSB, TI_LMP92064_REG_DATA_COUT_MSB),
+};
+
+static const struct regmap_access_table lmp92064_readable_regs = {
+ .yes_ranges = lmp92064_readable_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(lmp92064_readable_reg_ranges),
+};
+
+static const struct regmap_range lmp92064_writable_reg_ranges[] = {
+ regmap_reg_range(TI_LMP92064_REG_CONFIG_A, TI_LMP92064_REG_CONFIG_B),
+ regmap_reg_range(TI_LMP92064_REG_REG_UPDATE, TI_LMP92064_REG_REG_UPDATE),
+ regmap_reg_range(TI_LMP92064_REG_CONFIG_REG, TI_LMP92064_REG_CONFIG_REG),
+};
+
+static const struct regmap_access_table lmp92064_writable_regs = {
+ .yes_ranges = lmp92064_writable_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(lmp92064_writable_reg_ranges),
+};
+
+static const struct regmap_config lmp92064_spi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = TI_LMP92064_REG_DATA_COUT_MSB,
+ .rd_table = &lmp92064_readable_regs,
+ .wr_table = &lmp92064_writable_regs,
+};
+
+struct lmp92064_adc_priv {
+ int shunt_resistor_uohm;
+ struct spi_device *spi;
+ struct regmap *regmap;
+};
+
+static const struct iio_chan_spec lmp92064_adc_channels[] = {
+ {
+ .type = IIO_CURRENT,
+ .address = TI_LMP92064_CHAN_INC,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .datasheet_name = "INC",
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .address = TI_LMP92064_CHAN_INV,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .datasheet_name = "INV",
+ },
+};
+
+static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res)
+{
+ __be16 raw[2];
+ int ret;
+
+ /*
+ * The ADC only latches in new samples if all DATA registers are read
+ * in descending sequential order.
+ * The ADC auto-decrements the register index with each clocked byte.
+ * Read both channels in single SPI transfer by selecting the highest
+ * register using the command below and clocking out all four data
+ * bytes.
+ */
+
+ ret = regmap_bulk_read(priv->regmap, TI_LMP92064_REG_DATA_COUT_MSB,
+ &raw, sizeof(raw));
+
+ if (ret) {
+ dev_err(&priv->spi->dev, "regmap_bulk_read failed: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ res[0] = be16_to_cpu(raw[0]);
+ res[1] = be16_to_cpu(raw[1]);
+
+ return 0;
+}
+
+static int lmp92064_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct lmp92064_adc_priv *priv = iio_priv(indio_dev);
+ u16 raw[2];
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = lmp92064_read_meas(priv, raw);
+ if (ret < 0)
+ return ret;
+
+ *val = (chan->address == TI_LMP92064_CHAN_INC) ? raw[0] : raw[1];
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->address == TI_LMP92064_CHAN_INC) {
+ /*
+ * processed (mA) = raw * current_lsb (mA)
+ * current_lsb (mA) = shunt_voltage_lsb (nV) / shunt_resistor (uOhm)
+ * shunt_voltage_lsb (nV) = 81920000 / 4096 = 20000
+ */
+ *val = 20000;
+ *val2 = priv->shunt_resistor_uohm;
+ } else {
+ /*
+ * processed (mV) = raw * voltage_lsb (mV)
+ * voltage_lsb (mV) = 2048 / 4096
+ */
+ *val = 2048;
+ *val2 = 4096;
+ }
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int lmp92064_reset(struct lmp92064_adc_priv *priv,
+ struct gpio_desc *gpio_reset)
+{
+ unsigned int status;
+ int ret, i;
+
+ if (gpio_reset) {
+ /*
+ * Perform a hard reset if gpio_reset is available.
+ * The datasheet specifies a very low 3.5ns reset pulse duration and does not
+ * specify how long to wait after a reset to access the device.
+ * Use more conservative pulse lengths to allow analog RC filtering of the
+ * reset line at the board level (as recommended in the datasheet).
+ */
+ gpiod_set_value_cansleep(gpio_reset, 1);
+ usleep_range(1, 10);
+ gpiod_set_value_cansleep(gpio_reset, 0);
+ usleep_range(500, 750);
+ } else {
+ /*
+ * Perform a soft-reset if not.
+ * Also write default values to the config registers that are not
+ * affected by soft reset.
+ */
+ ret = regmap_write(priv->regmap, TI_LMP92064_REG_CONFIG_A,
+ TI_LMP92064_VAL_CONFIG_A);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(priv->regmap, TI_LMP92064_REG_CONFIG_B,
+ TI_LMP92064_VAL_CONFIG_B);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * Wait for the device to signal readiness to prevent reading bogus data
+ * and make sure device is actually connected.
+ * The datasheet does not specify how long this takes but usually it is
+ * not more than 3-4 iterations of this loop.
+ */
+ for (i = 0; i < 10; i++) {
+ ret = regmap_read(priv->regmap, TI_LMP92064_REG_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ if (status == TI_LMP92064_VAL_STATUS_OK)
+ return 0;
+
+ usleep_range(1000, 2000);
+ }
+
+ /*
+ * No (correct) response received.
+ * Device is mostly likely not connected to the bus.
+ */
+ return -ENXIO;
+}
+
+static const struct iio_info lmp92064_adc_info = {
+ .read_raw = lmp92064_read_raw,
+};
+
+static int lmp92064_adc_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct lmp92064_adc_priv *priv;
+ struct gpio_desc *gpio_reset;
+ struct iio_dev *indio_dev;
+ u32 shunt_resistor_uohm;
+ struct regmap *regmap;
+ int ret;
+
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error in SPI setup\n");
+
+ regmap = devm_regmap_init_spi(spi, &lmp92064_spi_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to set up SPI regmap\n");
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+
+ priv->spi = spi;
+ priv->regmap = regmap;
+
+ ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms",
+ &shunt_resistor_uohm);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to get shunt-resistor value\n");
+
+ /*
+ * The shunt resistance is passed to userspace as the denominator of an iio
+ * fraction. Make sure it is in range for that.
+ */
+ if (shunt_resistor_uohm == 0 || shunt_resistor_uohm > INT_MAX) {
+ dev_err(dev, "Shunt resistance is out of range\n");
+ return -EINVAL;
+ }
+
+ priv->shunt_resistor_uohm = shunt_resistor_uohm;
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_get_enable(dev, "vdig");
+ if (ret)
+ return ret;
+
+ gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio_reset))
+ return dev_err_probe(dev, PTR_ERR(gpio_reset),
+ "Failed to get GPIO reset pin\n");
+
+ ret = lmp92064_reset(priv, gpio_reset);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to reset device\n");
+
+ indio_dev->name = "lmp92064";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = lmp92064_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels);
+ indio_dev->info = &lmp92064_adc_info;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id lmp92064_id_table[] = {
+ { "lmp92064" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, lmp92064_id_table);
+
+static const struct of_device_id lmp92064_of_table[] = {
+ { .compatible = "ti,lmp92064" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lmp92064_of_table);
+
+static struct spi_driver lmp92064_adc_driver = {
+ .driver = {
+ .name = "lmp92064",
+ .of_match_table = lmp92064_of_table,
+ },
+ .probe = lmp92064_adc_probe,
+ .id_table = lmp92064_id_table,
+};
+module_spi_driver(lmp92064_adc_driver);
+
+MODULE_AUTHOR("Leonard Göhrs <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("TI LMP92064 ADC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c
index a507d2e17079..34cf336b3490 100644
--- a/drivers/iio/adc/xilinx-ams.c
+++ b/drivers/iio/adc/xilinx-ams.c
@@ -1220,8 +1220,7 @@ static int ams_init_module(struct iio_dev *indio_dev,
int num_channels = 0;
int ret;
- if (fwnode_property_match_string(fwnode, "compatible",
- "xlnx,zynqmp-ams-ps") == 0) {
+ if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams-ps")) {
ams->ps_base = fwnode_iomap(fwnode, 0);
if (!ams->ps_base)
return -ENXIO;
@@ -1232,8 +1231,7 @@ static int ams_init_module(struct iio_dev *indio_dev,
/* add PS channels to iio device channels */
memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels));
num_channels = ARRAY_SIZE(ams_ps_channels);
- } else if (fwnode_property_match_string(fwnode, "compatible",
- "xlnx,zynqmp-ams-pl") == 0) {
+ } else if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams-pl")) {
ams->pl_base = fwnode_iomap(fwnode, 0);
if (!ams->pl_base)
return -ENXIO;
@@ -1247,8 +1245,7 @@ static int ams_init_module(struct iio_dev *indio_dev,
num_channels += AMS_PL_MAX_FIXED_CHANNEL;
num_channels = ams_get_ext_chan(fwnode, channels,
num_channels);
- } else if (fwnode_property_match_string(fwnode, "compatible",
- "xlnx,zynqmp-ams") == 0) {
+ } else if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams")) {
/* add AMS channels to iio device channels */
memcpy(channels, ams_ctrl_channels, sizeof(ams_ctrl_channels));
num_channels += ARRAY_SIZE(ams_ctrl_channels);
diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c
index 6f68651ce1d5..a1db5469f2d1 100644
--- a/drivers/iio/cdc/ad7746.c
+++ b/drivers/iio/cdc/ad7746.c
@@ -285,8 +285,7 @@ static int ad7746_select_channel(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- if (chip->capdac_set != chan->channel)
- chip->capdac_set = chan->channel;
+ chip->capdac_set = chan->channel;
break;
case IIO_VOLTAGE:
case IIO_TEMP:
diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c
index 682fca39d14d..7be5a45cf71a 100644
--- a/drivers/iio/chemical/scd30_core.c
+++ b/drivers/iio/chemical/scd30_core.c
@@ -354,7 +354,7 @@ static ssize_t sampling_frequency_available_show(struct device *dev, struct devi
ssize_t len = 0;
do {
- len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09u ", 1000000000 / i);
+ len += sysfs_emit_at(buf, len, "0.%09u ", 1000000000 / i);
/*
* Not all values fit PAGE_SIZE buffer hence print every 6th
* (each frequency differs by 6s in time domain from the
@@ -380,7 +380,7 @@ static ssize_t calibration_auto_enable_show(struct device *dev, struct device_at
ret = scd30_command_read(state, CMD_ASC, &val);
mutex_unlock(&state->lock);
- return ret ?: sprintf(buf, "%d\n", val);
+ return ret ?: sysfs_emit(buf, "%d\n", val);
}
static ssize_t calibration_auto_enable_store(struct device *dev, struct device_attribute *attr,
@@ -414,7 +414,7 @@ static ssize_t calibration_forced_value_show(struct device *dev, struct device_a
ret = scd30_command_read(state, CMD_FRC, &val);
mutex_unlock(&state->lock);
- return ret ?: sprintf(buf, "%d\n", val);
+ return ret ?: sysfs_emit(buf, "%d\n", val);
}
static ssize_t calibration_forced_value_store(struct device *dev, struct device_attribute *attr,
@@ -642,10 +642,8 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev)
trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
iio_device_id(indio_dev));
- if (!trig) {
- dev_err(dev, "failed to allocate trigger\n");
- return -ENOMEM;
- }
+ if (!trig)
+ return dev_err_probe(dev, -ENOMEM, "failed to allocate trigger\n");
trig->ops = &scd30_trigger_ops;
iio_trigger_set_drvdata(trig, indio_dev);
@@ -667,9 +665,9 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev)
IRQF_NO_AUTOEN,
indio_dev->name, indio_dev);
if (ret)
- dev_err(dev, "failed to request irq\n");
+ return dev_err_probe(dev, ret, "failed to request irq\n");
- return ret;
+ return 0;
}
int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
@@ -717,17 +715,13 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
return ret;
ret = scd30_reset(state);
- if (ret) {
- dev_err(dev, "failed to reset device: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to reset device\n");
if (state->irq > 0) {
ret = scd30_setup_trigger(indio_dev);
- if (ret) {
- dev_err(dev, "failed to setup trigger: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to setup trigger\n");
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, scd30_trigger_handler, NULL);
@@ -735,23 +729,17 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
return ret;
ret = scd30_command_read(state, CMD_FW_VERSION, &val);
- if (ret) {
- dev_err(dev, "failed to read firmware version: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to read firmware version\n");
dev_info(dev, "firmware version: %d.%d\n", val >> 8, (char)val);
ret = scd30_command_write(state, CMD_MEAS_INTERVAL, state->meas_interval);
- if (ret) {
- dev_err(dev, "failed to set measurement interval: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set measurement interval\n");
ret = scd30_command_write(state, CMD_START_MEAS, state->pressure_comp);
- if (ret) {
- dev_err(dev, "failed to start measurement: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to start measurement\n");
ret = devm_add_action_or_reset(dev, scd30_stop_meas, state);
if (ret)
diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
index d92f7f651f7b..0c2caf3570db 100644
--- a/drivers/iio/common/scmi_sensors/scmi_iio.c
+++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
@@ -400,12 +400,12 @@ static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev,
rem = do_div(resolution,
int_pow(10, abs(exponent))
);
- len = scnprintf(buf, PAGE_SIZE,
+ len = sysfs_emit(buf,
"[%lld %llu.%llu %lld]\n", min_range,
resolution, rem, max_range);
} else {
resolution = resolution * int_pow(10, exponent);
- len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n",
+ len = sysfs_emit(buf, "[%lld %llu %lld]\n",
min_range, resolution, max_range);
}
}
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 80521bd28d0f..d3f90cf86143 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -162,10 +162,10 @@ config AD5696_I2C
depends on I2C
select AD5686
help
- Say yes here to build support for Analog Devices AD5311R, AD5338R,
- AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, AD5693R,
- AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to Analog
- converters.
+ Say yes here to build support for Analog Devices AD5311R, AD5337,
+ AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693,
+ AD5693R, AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to
+ Analog converters.
To compile this driver as a module, choose M here: the module will be
called ad5696.
@@ -357,6 +357,19 @@ config MAX517
This driver can also be built as a module. If so, the module
will be called max517.
+config MAX5522
+ tristate "Maxim MAX5522 DAC driver"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+ help
+ Say Y here if you want to build a driver for the Maxim MAX5522.
+
+ MAX5522 is a dual, ultra-low-power, 10-Bit, voltage-output
+ digital to analog converter (DAC) offering rail-to-rail buffered
+ voltage outputs.
+
+ If compiled as a module, it will be called max5522.
+
config MAX5821
tristate "Maxim MAX5821 DAC driver"
depends on I2C
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index ec3e42713f00..6c74fea21736 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_LTC2632) += ltc2632.o
obj-$(CONFIG_LTC2688) += ltc2688.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
+obj-$(CONFIG_MAX5522) += max5522.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4922) += mcp4922.o
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 15361d8bbf94..57cc0f0eedc6 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -258,6 +258,7 @@ static const struct iio_chan_spec name[] = { \
DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2);
DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6);
+DECLARE_AD5338_CHANNELS(ad5337r_channels, 8, 8);
DECLARE_AD5338_CHANNELS(ad5338r_channels, 10, 6);
DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4);
DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4);
@@ -283,6 +284,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
.num_channels = 1,
.regmap_type = AD5693_REGMAP,
},
+ [ID_AD5337R] = {
+ .channels = ad5337r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 2,
+ .regmap_type = AD5686_REGMAP,
+ },
[ID_AD5338R] = {
.channels = ad5338r_channels,
.int_vref_mv = 2500,
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index b7ade3a6b9b6..760f852911df 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -54,6 +54,7 @@
enum ad5686_supported_device_ids {
ID_AD5310R,
ID_AD5311R,
+ ID_AD5337R,
ID_AD5338R,
ID_AD5671R,
ID_AD5672R,
diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c
index 160e80cf9135..8a95f0278018 100644
--- a/drivers/iio/dac/ad5696-i2c.c
+++ b/drivers/iio/dac/ad5696-i2c.c
@@ -72,6 +72,7 @@ static void ad5686_i2c_remove(struct i2c_client *i2c)
static const struct i2c_device_id ad5686_i2c_id[] = {
{"ad5311r", ID_AD5311R},
+ {"ad5337r", ID_AD5337R},
{"ad5338r", ID_AD5338R},
{"ad5671r", ID_AD5671R},
{"ad5673r", ID_AD5673R},
@@ -92,6 +93,7 @@ MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id);
static const struct of_device_id ad5686_of_match[] = {
{ .compatible = "adi,ad5311r" },
+ { .compatible = "adi,ad5337r" },
{ .compatible = "adi,ad5338r" },
{ .compatible = "adi,ad5671r" },
{ .compatible = "adi,ad5675r" },
diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c
new file mode 100644
index 000000000000..00ba4e98fb9c
--- /dev/null
+++ b/drivers/iio/dac/max5522.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Maxim MAX5522
+ * Dual, Ultra-Low-Power 10-Bit, Voltage-Output DACs
+ *
+ * Copyright 2022 Timesys Corp.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+
+#define MAX5522_MAX_ADDR 15
+#define MAX5522_CTRL_NONE 0
+#define MAX5522_CTRL_LOAD_IN_A 9
+#define MAX5522_CTRL_LOAD_IN_B 10
+
+#define MAX5522_REG_DATA(x) ((x) + MAX5522_CTRL_LOAD_IN_A)
+
+struct max5522_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+struct max5522_state {
+ struct regmap *regmap;
+ const struct max5522_chip_info *chip_info;
+ unsigned short dac_cache[2];
+ struct regulator *vrefin_reg;
+};
+
+#define MAX5522_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 10, \
+ .storagebits = 16, \
+ .shift = 2, \
+ } \
+}
+
+const struct iio_chan_spec max5522_channels[] = {
+ MAX5522_CHANNEL(0),
+ MAX5522_CHANNEL(1),
+};
+
+enum max5522_type {
+ ID_MAX5522,
+};
+
+static const struct max5522_chip_info max5522_chip_info_tbl[] = {
+ [ID_MAX5522] = {
+ .name = "max5522",
+ .channels = max5522_channels,
+ .num_channels = 2,
+ },
+};
+
+static inline int max5522_info_to_reg(struct iio_chan_spec const *chan)
+{
+ return MAX5522_REG_DATA(chan->channel);
+}
+
+static int max5522_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct max5522_state *state = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ *val = state->dac_cache[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(state->vrefin_reg);
+ if (ret < 0)
+ return -EINVAL;
+ *val = ret / 1000;
+ *val2 = 10;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int max5522_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct max5522_state *state = iio_priv(indio_dev);
+ int rval;
+
+ if (val > 1023 || val < 0)
+ return -EINVAL;
+
+ rval = regmap_write(state->regmap, max5522_info_to_reg(chan),
+ val << chan->scan_type.shift);
+ if (rval < 0)
+ return rval;
+
+ state->dac_cache[chan->channel] = val;
+
+ return 0;
+}
+
+static const struct iio_info max5522_info = {
+ .read_raw = max5522_read_raw,
+ .write_raw = max5522_write_raw,
+};
+
+static const struct regmap_config max5522_regmap_config = {
+ .reg_bits = 4,
+ .val_bits = 12,
+ .max_register = MAX5522_MAX_ADDR,
+};
+
+static int max5522_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct max5522_state *state;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ state = iio_priv(indio_dev);
+ state->chip_info = device_get_match_data(&spi->dev);
+ if (!state->chip_info) {
+ state->chip_info =
+ (struct max5522_chip_info *)(id->driver_data);
+ if (!state->chip_info)
+ return -EINVAL;
+ }
+
+ state->vrefin_reg = devm_regulator_get(&spi->dev, "vrefin");
+ if (IS_ERR(state->vrefin_reg))
+ return dev_err_probe(&spi->dev, PTR_ERR(state->vrefin_reg),
+ "Vrefin regulator not specified\n");
+
+ ret = regulator_enable(state->vrefin_reg);
+ if (ret) {
+ return dev_err_probe(&spi->dev, ret,
+ "Failed to enable vref regulators\n");
+ }
+
+ state->regmap = devm_regmap_init_spi(spi, &max5522_regmap_config);
+
+ if (IS_ERR(state->regmap))
+ return PTR_ERR(state->regmap);
+
+ indio_dev->info = &max5522_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max5522_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max5522_channels);
+ indio_dev->name = max5522_chip_info_tbl[ID_MAX5522].name;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id max5522_ids[] = {
+ { "max5522", (kernel_ulong_t)&max5522_chip_info_tbl[ID_MAX5522] },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, max5522_ids);
+
+static const struct of_device_id max5522_of_match[] = {
+ {
+ .compatible = "maxim,max5522",
+ .data = &max5522_chip_info_tbl[ID_MAX5522],
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, max5522_of_match);
+
+static struct spi_driver max5522_spi_driver = {
+ .driver = {
+ .name = "max5522",
+ .of_match_table = max5522_of_match,
+ },
+ .probe = max5522_spi_probe,
+ .id_table = max5522_ids,
+};
+module_spi_driver(max5522_spi_driver);
+
+MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com");
+MODULE_DESCRIPTION("MAX5522 DAC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.c b/drivers/iio/imu/bno055/bno055_ser_trace.c
index 48397b66daef..ab564186d19c 100644
--- a/drivers/iio/imu/bno055/bno055_ser_trace.c
+++ b/drivers/iio/imu/bno055/bno055_ser_trace.c
@@ -1,4 +1,4 @@
-//SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0
/*
* bno055_ser Trace Support
diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c
index e692dfeeda44..53ba020fa5d0 100644
--- a/drivers/iio/imu/kmx61.c
+++ b/drivers/iio/imu/kmx61.c
@@ -649,7 +649,7 @@ static int kmx61_chip_update_thresholds(struct kmx61_data *data)
KMX61_REG_WUF_TIMER,
data->wake_duration);
if (ret < 0) {
- dev_err(&data->client->dev, "Errow writing reg_wuf_timer\n");
+ dev_err(&data->client->dev, "Error writing reg_wuf_timer\n");
return ret;
}
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index 5b6f195748fc..499fcf8875b4 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -93,7 +93,7 @@ enum st_lsm6dsx_hw_id {
.endianness = IIO_LE, \
}, \
.event_spec = &st_lsm6dsx_event, \
- .ext_info = st_lsm6dsx_accel_ext_info, \
+ .ext_info = st_lsm6dsx_ext_info, \
.num_event_specs = 1, \
}
@@ -113,6 +113,7 @@ enum st_lsm6dsx_hw_id {
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
+ .ext_info = st_lsm6dsx_ext_info, \
}
struct st_lsm6dsx_reg {
@@ -528,7 +529,7 @@ st_lsm6dsx_device_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
}
static const
-struct iio_chan_spec_ext_info __maybe_unused st_lsm6dsx_accel_ext_info[] = {
+struct iio_chan_spec_ext_info __maybe_unused st_lsm6dsx_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_lsm6dsx_get_mount_matrix),
{ }
};
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
index f2b64b4956a3..c1b444520d2a 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
@@ -704,18 +704,18 @@ static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev,
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail);
static IIO_DEVICE_ATTR(in_scale_available, 0444,
st_lsm6dsx_shub_scale_avail, NULL, 0);
-static struct attribute *st_lsm6dsx_ext_attributes[] = {
+static struct attribute *st_lsm6dsx_shub_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_scale_available.dev_attr.attr,
NULL,
};
-static const struct attribute_group st_lsm6dsx_ext_attribute_group = {
- .attrs = st_lsm6dsx_ext_attributes,
+static const struct attribute_group st_lsm6dsx_shub_attribute_group = {
+ .attrs = st_lsm6dsx_shub_attributes,
};
-static const struct iio_info st_lsm6dsx_ext_info = {
- .attrs = &st_lsm6dsx_ext_attribute_group,
+static const struct iio_info st_lsm6dsx_shub_info = {
+ .attrs = &st_lsm6dsx_shub_attribute_group,
.read_raw = st_lsm6dsx_shub_read_raw,
.write_raw = st_lsm6dsx_shub_write_raw,
.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
@@ -737,7 +737,7 @@ st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw,
return NULL;
iio_dev->modes = INDIO_DIRECT_MODE;
- iio_dev->info = &st_lsm6dsx_ext_info;
+ iio_dev->info = &st_lsm6dsx_shub_info;
sensor = iio_priv(iio_dev);
sensor->id = id;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 52e690f031cb..c117f50d0cf3 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -8,30 +8,32 @@
#define pr_fmt(fmt) "iio-core: " fmt
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/idr.h>
-#include <linux/kdev_t.h>
-#include <linux/err.h>
+#include <linux/anon_inodes.h>
+#include <linux/cdev.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/kdev_t.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/property.h>
#include <linux/sched.h>
-#include <linux/wait.h>
-#include <linux/cdev.h>
#include <linux/slab.h>
-#include <linux/anon_inodes.h>
-#include <linux/debugfs.h>
-#include <linux/mutex.h>
-#include <linux/iio/iio.h>
+#include <linux/wait.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer_impl.h>
+#include <linux/iio/events.h>
#include <linux/iio/iio-opaque.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
#include "iio_core.h"
#include "iio_core_trigger.h"
-#include <linux/iio/sysfs.h>
-#include <linux/iio/events.h>
-#include <linux/iio/buffer.h>
-#include <linux/iio/buffer_impl.h>
/* IDA to assign each registered device a unique id */
static DEFINE_IDA(iio_ida);
@@ -205,36 +207,6 @@ bool iio_buffer_enabled(struct iio_dev *indio_dev)
}
EXPORT_SYMBOL_GPL(iio_buffer_enabled);
-/**
- * iio_sysfs_match_string_with_gaps - matches given string in an array with gaps
- * @array: array of strings
- * @n: number of strings in the array
- * @str: string to match with
- *
- * Returns index of @str in the @array or -EINVAL, similar to match_string().
- * Uses sysfs_streq instead of strcmp for matching.
- *
- * This routine will look for a string in an array of strings.
- * The search will continue until the element is found or the n-th element
- * is reached, regardless of any NULL elements in the array.
- */
-static int iio_sysfs_match_string_with_gaps(const char * const *array, size_t n,
- const char *str)
-{
- const char *item;
- int index;
-
- for (index = 0; index < n; index++) {
- item = array[index];
- if (!item)
- continue;
- if (sysfs_streq(item, str))
- return index;
- }
-
- return -EINVAL;
-}
-
#if defined(CONFIG_DEBUG_FS)
/*
* There's also a CONFIG_DEBUG_FS guard in include/linux/iio/iio.h for
@@ -569,7 +541,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
if (!e->set)
return -EINVAL;
- ret = iio_sysfs_match_string_with_gaps(e->items, e->num_items, buf);
+ ret = __sysfs_match_string(e->items, e->num_items, buf);
if (ret < 0)
return ret;
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 6f23817fae6f..d74d2b5ff14c 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -39,7 +39,6 @@ obj-$(CONFIG_NOA1305) += noa1305.o
obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_PA12203001) += pa12203001.o
obj-$(CONFIG_RPR0521) += rpr0521.o
-obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
obj-$(CONFIG_SI1133) += si1133.o
obj-$(CONFIG_SI1145) += si1145.o
obj-$(CONFIG_STK3310) += stk3310.o
@@ -48,6 +47,7 @@ obj-$(CONFIG_ST_UVIS25_I2C) += st_uvis25_i2c.o
obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o
obj-$(CONFIG_TCS3414) += tcs3414.o
obj-$(CONFIG_TCS3472) += tcs3472.o
+obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
obj-$(CONFIG_TSL2583) += tsl2583.o
obj-$(CONFIG_TSL2591) += tsl2591.o
obj-$(CONFIG_TSL2772) += tsl2772.o
diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c
index 801e5a0ad496..3dadace09fe2 100644
--- a/drivers/iio/light/max44009.c
+++ b/drivers/iio/light/max44009.c
@@ -487,8 +487,7 @@ static irqreturn_t max44009_threaded_irq_handler(int irq, void *p)
return IRQ_NONE;
}
-static int max44009_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max44009_probe(struct i2c_client *client)
{
struct max44009_data *data;
struct iio_dev *indio_dev;
@@ -538,7 +537,7 @@ static struct i2c_driver max44009_driver = {
.driver = {
.name = MAX44009_DRV_NAME,
},
- .probe = max44009_probe,
+ .probe_new = max44009_probe,
.id_table = max44009_id,
};
module_i2c_driver(max44009_driver);
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index d0e42b73203a..f2f55239a072 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -11,69 +11,63 @@
* Amit Kucheria <amit.kucheria@verdurent.com>
*/
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/property.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/sched.h>
+#include <linux/math.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/err.h>
+#include <linux/property.h>
+#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include <linux/iio/events.h>
-#include <linux/platform_data/tsl2563.h>
/* Use this many bits for fraction part. */
#define ADC_FRAC_BITS 14
/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
-#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000))
+#define FRAC10K(f) (((f) * BIT(ADC_FRAC_BITS)) / (10000))
/* Bits used for fraction in calibration coefficients.*/
#define CALIB_FRAC_BITS 10
-/* 0.5 in CALIB_FRAC_BITS precision */
-#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1))
-/* Make a fraction from a number n that was multiplied with b. */
-#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b))
/* Decimal 10^(digits in sysfs presentation) */
#define CALIB_BASE_SYSFS 1000
-#define TSL2563_CMD 0x80
-#define TSL2563_CLEARINT 0x40
+#define TSL2563_CMD BIT(7)
+#define TSL2563_CLEARINT BIT(6)
#define TSL2563_REG_CTRL 0x00
#define TSL2563_REG_TIMING 0x01
-#define TSL2563_REG_LOWLOW 0x02 /* data0 low threshold, 2 bytes */
-#define TSL2563_REG_LOWHIGH 0x03
-#define TSL2563_REG_HIGHLOW 0x04 /* data0 high threshold, 2 bytes */
-#define TSL2563_REG_HIGHHIGH 0x05
+#define TSL2563_REG_LOW 0x02 /* data0 low threshold, 2 bytes */
+#define TSL2563_REG_HIGH 0x04 /* data0 high threshold, 2 bytes */
#define TSL2563_REG_INT 0x06
#define TSL2563_REG_ID 0x0a
-#define TSL2563_REG_DATA0LOW 0x0c /* broadband sensor value, 2 bytes */
-#define TSL2563_REG_DATA0HIGH 0x0d
-#define TSL2563_REG_DATA1LOW 0x0e /* infrared sensor value, 2 bytes */
-#define TSL2563_REG_DATA1HIGH 0x0f
+#define TSL2563_REG_DATA0 0x0c /* broadband sensor value, 2 bytes */
+#define TSL2563_REG_DATA1 0x0e /* infrared sensor value, 2 bytes */
#define TSL2563_CMD_POWER_ON 0x03
#define TSL2563_CMD_POWER_OFF 0x00
-#define TSL2563_CTRL_POWER_MASK 0x03
+#define TSL2563_CTRL_POWER_MASK GENMASK(1, 0)
#define TSL2563_TIMING_13MS 0x00
#define TSL2563_TIMING_100MS 0x01
#define TSL2563_TIMING_400MS 0x02
-#define TSL2563_TIMING_MASK 0x03
+#define TSL2563_TIMING_MASK GENMASK(1, 0)
#define TSL2563_TIMING_GAIN16 0x10
#define TSL2563_TIMING_GAIN1 0x00
#define TSL2563_INT_DISABLED 0x00
#define TSL2563_INT_LEVEL 0x10
-#define TSL2563_INT_PERSIST(n) ((n) & 0x0F)
+#define TSL2563_INT_MASK GENMASK(5, 4)
+#define TSL2563_INT_PERSIST(n) ((n) & GENMASK(3, 0))
struct tsl2563_gainlevel_coeff {
u8 gaintime;
@@ -161,24 +155,16 @@ static int tsl2563_configure(struct tsl2563_chip *chip)
chip->gainlevel->gaintime);
if (ret)
goto error_ret;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2563_CMD | TSL2563_REG_HIGHLOW,
- chip->high_thres & 0xFF);
- if (ret)
- goto error_ret;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2563_CMD | TSL2563_REG_HIGHHIGH,
- (chip->high_thres >> 8) & 0xFF);
+ ret = i2c_smbus_write_word_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_HIGH,
+ chip->high_thres);
if (ret)
goto error_ret;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2563_CMD | TSL2563_REG_LOWLOW,
- chip->low_thres & 0xFF);
+ ret = i2c_smbus_write_word_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_LOW,
+ chip->low_thres);
if (ret)
goto error_ret;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2563_CMD | TSL2563_REG_LOWHIGH,
- (chip->low_thres >> 8) & 0xFF);
/*
* Interrupt register is automatically written anyway if it is relevant
* so is not here.
@@ -223,6 +209,24 @@ static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
return 0;
}
+static int tsl2563_configure_irq(struct tsl2563_chip *chip, bool enable)
+{
+ int ret;
+
+ chip->intr &= ~TSL2563_INT_MASK;
+ if (enable)
+ chip->intr |= TSL2563_INT_LEVEL;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_INT,
+ chip->intr);
+ if (ret < 0)
+ return ret;
+
+ chip->int_enabled = enable;
+ return 0;
+}
+
/*
* "Normalized" ADC value is one obtained with 400ms of integration time and
* 16x gain. This function returns the number of bits of shift needed to
@@ -325,13 +329,13 @@ static int tsl2563_get_adc(struct tsl2563_chip *chip)
while (retry) {
ret = i2c_smbus_read_word_data(client,
- TSL2563_CMD | TSL2563_REG_DATA0LOW);
+ TSL2563_CMD | TSL2563_REG_DATA0);
if (ret < 0)
goto out;
adc0 = ret;
ret = i2c_smbus_read_word_data(client,
- TSL2563_CMD | TSL2563_REG_DATA1LOW);
+ TSL2563_CMD | TSL2563_REG_DATA1);
if (ret < 0)
goto out;
adc1 = ret;
@@ -352,12 +356,12 @@ out:
static inline int tsl2563_calib_to_sysfs(u32 calib)
{
- return (int) (((calib * CALIB_BASE_SYSFS) +
- CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
+ return (int)DIV_ROUND_CLOSEST(calib * CALIB_BASE_SYSFS, BIT(CALIB_FRAC_BITS));
}
static inline u32 tsl2563_calib_from_sysfs(int value)
{
+ /* Make a fraction from a number n that was multiplied with b. */
return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
}
@@ -584,20 +588,18 @@ static int tsl2563_write_thresh(struct iio_dev *indio_dev,
{
struct tsl2563_chip *chip = iio_priv(indio_dev);
int ret;
- u8 address;
+
+ mutex_lock(&chip->lock);
if (dir == IIO_EV_DIR_RISING)
- address = TSL2563_REG_HIGHLOW;
+ ret = i2c_smbus_write_word_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_HIGH, val);
else
- address = TSL2563_REG_LOWLOW;
- mutex_lock(&chip->lock);
- ret = i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | address,
- val & 0xFF);
+ ret = i2c_smbus_write_word_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_LOW, val);
if (ret)
goto error_ret;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2563_CMD | (address + 1),
- (val >> 8) & 0xFF);
+
if (dir == IIO_EV_DIR_RISING)
chip->high_thres = val;
else
@@ -634,9 +636,7 @@ static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev,
int ret = 0;
mutex_lock(&chip->lock);
- if (state && !(chip->intr & 0x30)) {
- chip->intr &= ~0x30;
- chip->intr |= 0x10;
+ if (state && !(chip->intr & TSL2563_INT_MASK)) {
/* ensure the chip is actually on */
cancel_delayed_work_sync(&chip->poweroff_work);
if (!tsl2563_get_power(chip)) {
@@ -647,18 +647,11 @@ static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev,
if (ret)
goto out;
}
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2563_CMD | TSL2563_REG_INT,
- chip->intr);
- chip->int_enabled = true;
+ ret = tsl2563_configure_irq(chip, true);
}
- if (!state && (chip->intr & 0x30)) {
- chip->intr &= ~0x30;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2563_CMD | TSL2563_REG_INT,
- chip->intr);
- chip->int_enabled = false;
+ if (!state && (chip->intr & TSL2563_INT_MASK)) {
+ ret = tsl2563_configure_irq(chip, false);
/* now the interrupt is not enabled, we can go to sleep */
schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
}
@@ -682,7 +675,7 @@ static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- return !!(ret & 0x30);
+ return !!(ret & TSL2563_INT_MASK);
}
static const struct iio_info tsl2563_info_no_irq = {
@@ -701,13 +694,14 @@ static const struct iio_info tsl2563_info = {
static int tsl2563_probe(struct i2c_client *client)
{
+ struct device *dev = &client->dev;
struct iio_dev *indio_dev;
struct tsl2563_chip *chip;
- struct tsl2563_platform_data *pdata = client->dev.platform_data;
- int err = 0;
+ unsigned long irq_flags;
u8 id = 0;
+ int err;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
@@ -717,16 +711,12 @@ static int tsl2563_probe(struct i2c_client *client)
chip->client = client;
err = tsl2563_detect(chip);
- if (err) {
- dev_err(&client->dev, "detect error %d\n", -err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "detect error\n");
err = tsl2563_read_id(chip, &id);
- if (err) {
- dev_err(&client->dev, "read id error %d\n", -err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "read id error\n");
mutex_init(&chip->lock);
@@ -738,16 +728,10 @@ static int tsl2563_probe(struct i2c_client *client)
chip->calib0 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
chip->calib1 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
- if (pdata) {
- chip->cover_comp_gain = pdata->cover_comp_gain;
- } else {
- err = device_property_read_u32(&client->dev, "amstaos,cover-comp-gain",
- &chip->cover_comp_gain);
- if (err)
- chip->cover_comp_gain = 1;
- }
+ chip->cover_comp_gain = 1;
+ device_property_read_u32(dev, "amstaos,cover-comp-gain", &chip->cover_comp_gain);
- dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f);
+ dev_info(dev, "model %d, rev. %d\n", id >> 4, id & 0x0f);
indio_dev->name = client->name;
indio_dev->channels = tsl2563_channels;
indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels);
@@ -759,23 +743,24 @@ static int tsl2563_probe(struct i2c_client *client)
indio_dev->info = &tsl2563_info_no_irq;
if (client->irq) {
- err = devm_request_threaded_irq(&client->dev, client->irq,
+ irq_flags = irq_get_trigger_type(client->irq);
+ if (irq_flags == IRQF_TRIGGER_NONE)
+ irq_flags = IRQF_TRIGGER_RISING;
+ irq_flags |= IRQF_ONESHOT;
+
+ err = devm_request_threaded_irq(dev, client->irq,
NULL,
&tsl2563_event_handler,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ irq_flags,
"tsl2563_event",
indio_dev);
- if (err) {
- dev_err(&client->dev, "irq request error %d\n", -err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "irq request error\n");
}
err = tsl2563_configure(chip);
- if (err) {
- dev_err(&client->dev, "configure error %d\n", -err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "configure error\n");
INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work);
@@ -784,7 +769,7 @@ static int tsl2563_probe(struct i2c_client *client)
err = iio_device_register(indio_dev);
if (err) {
- dev_err(&client->dev, "iio registration error %d\n", -err);
+ dev_err_probe(dev, err, "iio registration error\n");
goto fail;
}
@@ -804,15 +789,13 @@ static void tsl2563_remove(struct i2c_client *client)
if (!chip->int_enabled)
cancel_delayed_work_sync(&chip->poweroff_work);
/* Ensure that interrupts are disabled - then flush any bottom halves */
- chip->intr &= ~0x30;
- i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | TSL2563_REG_INT,
- chip->intr);
+ tsl2563_configure_irq(chip, false);
tsl2563_set_power(chip, 0);
}
static int tsl2563_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tsl2563_chip *chip = iio_priv(indio_dev);
int ret;
@@ -831,7 +814,7 @@ out:
static int tsl2563_resume(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tsl2563_chip *chip = iio_priv(indio_dev);
int ret;
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index cc1a2062e76d..6bdfce9747f9 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -60,8 +60,11 @@
#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
+#define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */
+#define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */
#define VCNL4200_PS_DATA 0x08 /* Proximity data */
#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
+#define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */
#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
#define VCNL4040_DEV_ID 0x0c /* Device ID and version */
@@ -78,6 +81,9 @@
#define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0)
#define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0)
#define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */
+#define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */
+#define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */
+#define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */
/* Bit masks for interrupt registers. */
#define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */
@@ -138,6 +144,7 @@ struct vcnl4000_data {
enum vcnl4000_device_ids id;
int rev;
int al_scale;
+ u8 ps_int; /* proximity interrupt mode */
const struct vcnl4000_chip_spec *chip_spec;
struct mutex vcnl4000_lock;
struct vcnl4200_channel vcnl4200_al;
@@ -150,11 +157,13 @@ struct vcnl4000_chip_spec {
struct iio_chan_spec const *channels;
const int num_channels;
const struct iio_info *info;
- bool irq_support;
+ const struct iio_buffer_setup_ops *buffer_setup_ops;
int (*init)(struct vcnl4000_data *data);
int (*measure_light)(struct vcnl4000_data *data, int *val);
int (*measure_proximity)(struct vcnl4000_data *data, int *val);
int (*set_power_state)(struct vcnl4000_data *data, bool on);
+ irqreturn_t (*irq_thread)(int irq, void *priv);
+ irqreturn_t (*trig_buffer_func)(int irq, void *priv);
};
static const struct i2c_device_id vcnl4000_id[] = {
@@ -254,6 +263,10 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
{
int ret;
+ /* Do not power down if interrupts are enabled */
+ if (!on && data->ps_int)
+ return 0;
+
ret = vcnl4000_write_als_enable(data, on);
if (ret < 0)
return ret;
@@ -295,6 +308,7 @@ static int vcnl4200_init(struct vcnl4000_data *data)
dev_dbg(&data->client->dev, "device id 0x%x", id);
data->rev = (ret >> 8) & 0xf;
+ data->ps_int = 0;
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
@@ -795,6 +809,64 @@ static int vcnl4010_write_event(struct iio_dev *indio_dev,
}
}
+static int vcnl4040_read_event(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)
+{
+ int ret;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = i2c_smbus_read_word_data(data->client,
+ VCNL4040_PS_THDH_LM);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ ret = i2c_smbus_read_word_data(data->client,
+ VCNL4040_PS_THDL_LM);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vcnl4040_write_event(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)
+{
+ int ret;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = i2c_smbus_write_word_data(data->client,
+ VCNL4040_PS_THDH_LM, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ ret = i2c_smbus_write_word_data(data->client,
+ VCNL4040_PS_THDL_LM, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
{
int ret;
@@ -877,6 +949,86 @@ static int vcnl4010_write_event_config(struct iio_dev *indio_dev,
}
}
+static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ int ret;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
+ if (ret < 0)
+ return ret;
+
+ data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
+
+ return (dir == IIO_EV_DIR_RISING) ?
+ FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
+ FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
+}
+
+static int vcnl4040_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)
+{
+ int ret;
+ u16 val, mask;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ mutex_lock(&data->vcnl4000_lock);
+
+ ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
+ if (ret < 0)
+ goto out;
+
+ if (dir == IIO_EV_DIR_RISING)
+ mask = VCNL4040_PS_IF_AWAY;
+ else
+ mask = VCNL4040_PS_IF_CLOSE;
+
+ val = state ? (ret | mask) : (ret & ~mask);
+
+ data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
+ ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
+
+out:
+ mutex_unlock(&data->vcnl4000_lock);
+ data->chip_spec->set_power_state(data, data->ps_int != 0);
+
+ return ret;
+}
+
+static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ if (ret & VCNL4040_PS_IF_CLOSE) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ if (ret & VCNL4040_PS_IF_AWAY) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ return IRQ_HANDLED;
+}
+
static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
uintptr_t priv,
const struct iio_chan_spec *chan,
@@ -887,6 +1039,134 @@ static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
return sprintf(buf, "%u\n", data->near_level);
}
+static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ unsigned long isr;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
+ if (ret < 0)
+ goto end;
+
+ isr = ret;
+
+ if (isr & VCNL4010_INT_THR) {
+ if (test_bit(VCNL4010_INT_THR_LOW, &isr)) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(
+ IIO_PROXIMITY,
+ 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(
+ IIO_PROXIMITY,
+ 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
+ isr & VCNL4010_INT_THR);
+ }
+
+ if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev))
+ iio_trigger_poll_chained(indio_dev->trig);
+
+end:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t vcnl4010_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ const unsigned long *active_scan_mask = indio_dev->active_scan_mask;
+ u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */
+ bool data_read = false;
+ unsigned long isr;
+ int val = 0;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
+ if (ret < 0)
+ goto end;
+
+ isr = ret;
+
+ if (test_bit(0, active_scan_mask)) {
+ if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) {
+ ret = vcnl4000_read_data(data,
+ VCNL4000_PS_RESULT_HI,
+ &val);
+ if (ret < 0)
+ goto end;
+
+ buffer[0] = val;
+ data_read = true;
+ }
+ }
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
+ isr & VCNL4010_INT_DRDY);
+ if (ret < 0)
+ goto end;
+
+ if (!data_read)
+ goto end;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ iio_get_time_ns(indio_dev));
+
+end:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ int ret;
+ int cmd;
+
+ /* Do not enable the buffer if we are already capturing events. */
+ if (vcnl4010_is_in_periodic_mode(data))
+ return -EBUSY;
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL,
+ VCNL4010_INT_PROX_EN);
+ if (ret < 0)
+ return ret;
+
+ cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN;
+ return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd);
+}
+
+static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
+}
+
+static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = {
+ .postenable = &vcnl4010_buffer_postenable,
+ .predisable = &vcnl4010_buffer_predisable,
+};
+
static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = {
{
.name = "nearlevel",
@@ -912,6 +1192,18 @@ static const struct iio_event_spec vcnl4000_event_spec[] = {
}
};
+static const struct iio_event_spec vcnl4040_event_spec[] = {
+ {
+ .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),
+ },
+};
+
static const struct iio_chan_spec vcnl4000_channels[] = {
{
.type = IIO_LIGHT,
@@ -960,6 +1252,8 @@ static const struct iio_chan_spec vcnl4040_channels[] = {
BIT(IIO_CHAN_INFO_INT_TIME),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
.ext_info = vcnl4000_ext_info,
+ .event_spec = vcnl4040_event_spec,
+ .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec),
}
};
@@ -980,6 +1274,10 @@ static const struct iio_info vcnl4010_info = {
static const struct iio_info vcnl4040_info = {
.read_raw = vcnl4000_read_raw,
.write_raw = vcnl4040_write_raw,
+ .read_event_value = vcnl4040_read_event,
+ .write_event_value = vcnl4040_write_event,
+ .read_event_config = vcnl4040_read_event_config,
+ .write_event_config = vcnl4040_write_event_config,
.read_avail = vcnl4040_read_avail,
};
@@ -993,7 +1291,6 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
.channels = vcnl4000_channels,
.num_channels = ARRAY_SIZE(vcnl4000_channels),
.info = &vcnl4000_info,
- .irq_support = false,
},
[VCNL4010] = {
.prod = "VCNL4010/4020",
@@ -1004,7 +1301,9 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
.channels = vcnl4010_channels,
.num_channels = ARRAY_SIZE(vcnl4010_channels),
.info = &vcnl4010_info,
- .irq_support = true,
+ .irq_thread = vcnl4010_irq_thread,
+ .trig_buffer_func = vcnl4010_trigger_handler,
+ .buffer_setup_ops = &vcnl4010_buffer_ops,
},
[VCNL4040] = {
.prod = "VCNL4040",
@@ -1015,7 +1314,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
.channels = vcnl4040_channels,
.num_channels = ARRAY_SIZE(vcnl4040_channels),
.info = &vcnl4040_info,
- .irq_support = false,
+ .irq_thread = vcnl4040_irq_thread,
},
[VCNL4200] = {
.prod = "VCNL4200",
@@ -1026,138 +1325,9 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
.channels = vcnl4000_channels,
.num_channels = ARRAY_SIZE(vcnl4000_channels),
.info = &vcnl4000_info,
- .irq_support = false,
},
};
-static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
-{
- struct iio_dev *indio_dev = p;
- struct vcnl4000_data *data = iio_priv(indio_dev);
- unsigned long isr;
- int ret;
-
- ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
- if (ret < 0)
- goto end;
-
- isr = ret;
-
- if (isr & VCNL4010_INT_THR) {
- if (test_bit(VCNL4010_INT_THR_LOW, &isr)) {
- iio_push_event(indio_dev,
- IIO_UNMOD_EVENT_CODE(
- IIO_PROXIMITY,
- 1,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_FALLING),
- iio_get_time_ns(indio_dev));
- }
-
- if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) {
- iio_push_event(indio_dev,
- IIO_UNMOD_EVENT_CODE(
- IIO_PROXIMITY,
- 1,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_RISING),
- iio_get_time_ns(indio_dev));
- }
-
- i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
- isr & VCNL4010_INT_THR);
- }
-
- if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev))
- iio_trigger_poll_chained(indio_dev->trig);
-
-end:
- return IRQ_HANDLED;
-}
-
-static irqreturn_t vcnl4010_trigger_handler(int irq, void *p)
-{
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct vcnl4000_data *data = iio_priv(indio_dev);
- const unsigned long *active_scan_mask = indio_dev->active_scan_mask;
- u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */
- bool data_read = false;
- unsigned long isr;
- int val = 0;
- int ret;
-
- ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
- if (ret < 0)
- goto end;
-
- isr = ret;
-
- if (test_bit(0, active_scan_mask)) {
- if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) {
- ret = vcnl4000_read_data(data,
- VCNL4000_PS_RESULT_HI,
- &val);
- if (ret < 0)
- goto end;
-
- buffer[0] = val;
- data_read = true;
- }
- }
-
- ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
- isr & VCNL4010_INT_DRDY);
- if (ret < 0)
- goto end;
-
- if (!data_read)
- goto end;
-
- iio_push_to_buffers_with_timestamp(indio_dev, buffer,
- iio_get_time_ns(indio_dev));
-
-end:
- iio_trigger_notify_done(indio_dev->trig);
- return IRQ_HANDLED;
-}
-
-static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev)
-{
- struct vcnl4000_data *data = iio_priv(indio_dev);
- int ret;
- int cmd;
-
- /* Do not enable the buffer if we are already capturing events. */
- if (vcnl4010_is_in_periodic_mode(data))
- return -EBUSY;
-
- ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL,
- VCNL4010_INT_PROX_EN);
- if (ret < 0)
- return ret;
-
- cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN;
- return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd);
-}
-
-static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev)
-{
- struct vcnl4000_data *data = iio_priv(indio_dev);
- int ret;
-
- ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
- if (ret < 0)
- return ret;
-
- return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
-}
-
-static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = {
- .postenable = &vcnl4010_buffer_postenable,
- .predisable = &vcnl4010_buffer_predisable,
-};
-
static const struct iio_trigger_ops vcnl4010_trigger_ops = {
.validate_device = iio_trigger_validate_own_device,
};
@@ -1214,22 +1384,25 @@ static int vcnl4000_probe(struct i2c_client *client)
indio_dev->name = VCNL4000_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- if (client->irq && data->chip_spec->irq_support) {
+ if (data->chip_spec->trig_buffer_func &&
+ data->chip_spec->buffer_setup_ops) {
ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
NULL,
- vcnl4010_trigger_handler,
- &vcnl4010_buffer_ops);
+ data->chip_spec->trig_buffer_func,
+ data->chip_spec->buffer_setup_ops);
if (ret < 0) {
dev_err(&client->dev,
"unable to setup iio triggered buffer\n");
return ret;
}
+ }
+ if (client->irq && data->chip_spec->irq_thread) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, vcnl4010_irq_thread,
+ NULL, data->chip_spec->irq_thread,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
- "vcnl4010_irq",
+ "vcnl4000_irq",
indio_dev);
if (ret < 0) {
dev_err(&client->dev, "irq request failed\n");
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index b91fc5e6a26e..38532d840f2a 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -119,7 +119,7 @@ config IIO_ST_MAGN_3AXIS
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics magnetometers:
- LSM303DLHC, LSM303DLM, LIS3MDL.
+ LSM303C, LSM303DLHC, LSM303DLM, LIS3MDL.
Also need to enable at least one of I2C and SPI interface drivers
below.
@@ -208,6 +208,18 @@ config SENSORS_RM3100_SPI
To compile this driver as a module, choose M here: the module
will be called rm3100-spi.
+config TI_TMAG5273
+ tristate "TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y here to add support for the TI TMAG5273 Low-Power
+ Linear 3D Hall-Effect Sensor.
+
+ This driver can also be compiled as a module.
+ To compile this driver as a module, choose M here: the module
+ will be called tmag5273.
+
config YAMAHA_YAS530
tristate "Yamaha YAS530 family of 3-Axis Magnetometers (I2C)"
depends on I2C
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index b9f45b7fafc3..b1c784ea71c8 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -29,4 +29,6 @@ obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o
obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o
+obj-$(CONFIG_TI_TMAG5273) += tmag5273.o
+
obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o
diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h
index 785b7f7b8b06..89945984d966 100644
--- a/drivers/iio/magnetometer/st_magn.h
+++ b/drivers/iio/magnetometer/st_magn.h
@@ -22,6 +22,7 @@
#define LIS2MDL_MAGN_DEV_NAME "lis2mdl"
#define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn"
#define IIS2MDC_MAGN_DEV_NAME "iis2mdc"
+#define LSM303C_MAGN_DEV_NAME "lsm303c_magn"
#ifdef CONFIG_IIO_BUFFER
int st_magn_allocate_ring(struct iio_dev *indio_dev);
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index e2fd233b3626..8faa7409d9e1 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -305,6 +305,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
.sensors_supported = {
[0] = LIS3MDL_MAGN_DEV_NAME,
[1] = LSM9DS1_MAGN_DEV_NAME,
+ [2] = LSM303C_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
.odr = {
diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c
index b4098d3b3813..cc0e0e94b129 100644
--- a/drivers/iio/magnetometer/st_magn_i2c.c
+++ b/drivers/iio/magnetometer/st_magn_i2c.c
@@ -50,6 +50,10 @@ static const struct of_device_id st_magn_of_match[] = {
.compatible = "st,iis2mdc",
.data = IIS2MDC_MAGN_DEV_NAME,
},
+ {
+ .compatible = "st,lsm303c-magn",
+ .data = LSM303C_MAGN_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_magn_of_match);
@@ -97,6 +101,7 @@ static const struct i2c_device_id st_magn_id_table[] = {
{ LIS2MDL_MAGN_DEV_NAME },
{ LSM9DS1_MAGN_DEV_NAME },
{ IIS2MDC_MAGN_DEV_NAME },
+ { LSM303C_MAGN_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c
index 6ddc4318564a..f203e1f87eec 100644
--- a/drivers/iio/magnetometer/st_magn_spi.c
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -45,6 +45,10 @@ static const struct of_device_id st_magn_of_match[] = {
.compatible = "st,iis2mdc",
.data = IIS2MDC_MAGN_DEV_NAME,
},
+ {
+ .compatible = "st,lsm303c-magn",
+ .data = LSM303C_MAGN_DEV_NAME,
+ },
{}
};
MODULE_DEVICE_TABLE(of, st_magn_of_match);
@@ -89,6 +93,7 @@ static const struct spi_device_id st_magn_id_table[] = {
{ LIS2MDL_MAGN_DEV_NAME },
{ LSM9DS1_MAGN_DEV_NAME },
{ IIS2MDC_MAGN_DEV_NAME },
+ { LSM303C_MAGN_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
diff --git a/drivers/iio/magnetometer/tmag5273.c b/drivers/iio/magnetometer/tmag5273.c
new file mode 100644
index 000000000000..28bb7efe8df8
--- /dev/null
+++ b/drivers/iio/magnetometer/tmag5273.c
@@ -0,0 +1,743 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor
+ *
+ * Copyright (C) 2022 WolfVision GmbH
+ *
+ * Author: Gerald Loacker <gerald.loacker@wolfvision.net>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define TMAG5273_DEVICE_CONFIG_1 0x00
+#define TMAG5273_DEVICE_CONFIG_2 0x01
+#define TMAG5273_SENSOR_CONFIG_1 0x02
+#define TMAG5273_SENSOR_CONFIG_2 0x03
+#define TMAG5273_X_THR_CONFIG 0x04
+#define TMAG5273_Y_THR_CONFIG 0x05
+#define TMAG5273_Z_THR_CONFIG 0x06
+#define TMAG5273_T_CONFIG 0x07
+#define TMAG5273_INT_CONFIG_1 0x08
+#define TMAG5273_MAG_GAIN_CONFIG 0x09
+#define TMAG5273_MAG_OFFSET_CONFIG_1 0x0A
+#define TMAG5273_MAG_OFFSET_CONFIG_2 0x0B
+#define TMAG5273_I2C_ADDRESS 0x0C
+#define TMAG5273_DEVICE_ID 0x0D
+#define TMAG5273_MANUFACTURER_ID_LSB 0x0E
+#define TMAG5273_MANUFACTURER_ID_MSB 0x0F
+#define TMAG5273_T_MSB_RESULT 0x10
+#define TMAG5273_T_LSB_RESULT 0x11
+#define TMAG5273_X_MSB_RESULT 0x12
+#define TMAG5273_X_LSB_RESULT 0x13
+#define TMAG5273_Y_MSB_RESULT 0x14
+#define TMAG5273_Y_LSB_RESULT 0x15
+#define TMAG5273_Z_MSB_RESULT 0x16
+#define TMAG5273_Z_LSB_RESULT 0x17
+#define TMAG5273_CONV_STATUS 0x18
+#define TMAG5273_ANGLE_RESULT_MSB 0x19
+#define TMAG5273_ANGLE_RESULT_LSB 0x1A
+#define TMAG5273_MAGNITUDE_RESULT 0x1B
+#define TMAG5273_DEVICE_STATUS 0x1C
+#define TMAG5273_MAX_REG TMAG5273_DEVICE_STATUS
+
+#define TMAG5273_AUTOSLEEP_DELAY_MS 5000
+#define TMAG5273_MAX_AVERAGE 32
+
+/*
+ * bits in the TMAG5273_MANUFACTURER_ID_LSB / MSB register
+ * 16-bit unique manufacturer ID 0x49 / 0x54 = "TI"
+ */
+#define TMAG5273_MANUFACTURER_ID 0x5449
+
+/* bits in the TMAG5273_DEVICE_CONFIG_1 register */
+#define TMAG5273_AVG_MODE_MASK GENMASK(4, 2)
+#define TMAG5273_AVG_1_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 0)
+#define TMAG5273_AVG_2_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 1)
+#define TMAG5273_AVG_4_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 2)
+#define TMAG5273_AVG_8_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 3)
+#define TMAG5273_AVG_16_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 4)
+#define TMAG5273_AVG_32_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 5)
+
+/* bits in the TMAG5273_DEVICE_CONFIG_2 register */
+#define TMAG5273_OP_MODE_MASK GENMASK(1, 0)
+#define TMAG5273_OP_MODE_STANDBY FIELD_PREP(TMAG5273_OP_MODE_MASK, 0)
+#define TMAG5273_OP_MODE_SLEEP FIELD_PREP(TMAG5273_OP_MODE_MASK, 1)
+#define TMAG5273_OP_MODE_CONT FIELD_PREP(TMAG5273_OP_MODE_MASK, 2)
+#define TMAG5273_OP_MODE_WAKEUP FIELD_PREP(TMAG5273_OP_MODE_MASK, 3)
+
+/* bits in the TMAG5273_SENSOR_CONFIG_1 register */
+#define TMAG5273_MAG_CH_EN_MASK GENMASK(7, 4)
+#define TMAG5273_MAG_CH_EN_X_Y_Z 7
+
+/* bits in the TMAG5273_SENSOR_CONFIG_2 register */
+#define TMAG5273_Z_RANGE_MASK BIT(0)
+#define TMAG5273_X_Y_RANGE_MASK BIT(1)
+#define TMAG5273_ANGLE_EN_MASK GENMASK(3, 2)
+#define TMAG5273_ANGLE_EN_OFF 0
+#define TMAG5273_ANGLE_EN_X_Y 1
+#define TMAG5273_ANGLE_EN_Y_Z 2
+#define TMAG5273_ANGLE_EN_X_Z 3
+
+/* bits in the TMAG5273_T_CONFIG register */
+#define TMAG5273_T_CH_EN BIT(0)
+
+/* bits in the TMAG5273_DEVICE_ID register */
+#define TMAG5273_VERSION_MASK GENMASK(1, 0)
+
+/* bits in the TMAG5273_CONV_STATUS register */
+#define TMAG5273_CONV_STATUS_COMPLETE BIT(0)
+
+enum tmag5273_channels {
+ TEMPERATURE = 0,
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z,
+ ANGLE,
+ MAGNITUDE,
+};
+
+enum tmag5273_scale_index {
+ MAGN_RANGE_LOW = 0,
+ MAGN_RANGE_HIGH,
+ MAGN_RANGE_NUM
+};
+
+/* state container for the TMAG5273 driver */
+struct tmag5273_data {
+ struct device *dev;
+ unsigned int devid;
+ unsigned int version;
+ char name[16];
+ unsigned int conv_avg;
+ unsigned int scale;
+ enum tmag5273_scale_index scale_index;
+ unsigned int angle_measurement;
+ struct regmap *map;
+ struct regulator *vcc;
+
+ /*
+ * Locks the sensor for exclusive use during a measurement (which
+ * involves several register transactions so the regmap lock is not
+ * enough) so that measurements get serialized in a
+ * first-come-first-serve manner.
+ */
+ struct mutex lock;
+};
+
+static const char *const tmag5273_angle_names[] = { "off", "x-y", "y-z", "x-z" };
+
+/*
+ * Averaging enables additional sampling of the sensor data to reduce the noise
+ * effect, but also increases conversion time.
+ */
+static const unsigned int tmag5273_avg_table[] = {
+ 1, 2, 4, 8, 16, 32,
+};
+
+/*
+ * Magnetic resolution in Gauss for different TMAG5273 versions.
+ * Scale[Gauss] = Range[mT] * 1000 / 2^15 * 10, (1 mT = 10 Gauss)
+ * Only version 1 and 2 are valid, version 0 and 3 are reserved.
+ */
+static const struct iio_val_int_plus_micro tmag5273_scale[][MAGN_RANGE_NUM] = {
+ { { 0, 0 }, { 0, 0 } },
+ { { 0, 12200 }, { 0, 24400 } },
+ { { 0, 40600 }, { 0, 81200 } },
+ { { 0, 0 }, { 0, 0 } },
+};
+
+static int tmag5273_get_measure(struct tmag5273_data *data, s16 *t, s16 *x,
+ s16 *y, s16 *z, u16 *angle, u16 *magnitude)
+{
+ unsigned int status, val;
+ __be16 reg_data[4];
+ int ret;
+
+ mutex_lock(&data->lock);
+
+ /*
+ * Max. conversion time is 2425 us in 32x averaging mode for all three
+ * channels. Since we are in continuous measurement mode, a measurement
+ * may already be there, so poll for completed measurement with
+ * timeout.
+ */
+ ret = regmap_read_poll_timeout(data->map, TMAG5273_CONV_STATUS, status,
+ status & TMAG5273_CONV_STATUS_COMPLETE,
+ 100, 10000);
+ if (ret) {
+ dev_err(data->dev, "timeout waiting for measurement\n");
+ goto out_unlock;
+ }
+
+ ret = regmap_bulk_read(data->map, TMAG5273_T_MSB_RESULT, reg_data,
+ sizeof(reg_data));
+ if (ret)
+ goto out_unlock;
+ *t = be16_to_cpu(reg_data[0]);
+ *x = be16_to_cpu(reg_data[1]);
+ *y = be16_to_cpu(reg_data[2]);
+ *z = be16_to_cpu(reg_data[3]);
+
+ ret = regmap_bulk_read(data->map, TMAG5273_ANGLE_RESULT_MSB,
+ &reg_data[0], sizeof(reg_data[0]));
+ if (ret)
+ goto out_unlock;
+ /*
+ * angle has 9 bits integer value and 4 bits fractional part
+ * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ * 0 0 0 a a a a a a a a a f f f f
+ */
+ *angle = be16_to_cpu(reg_data[0]);
+
+ ret = regmap_read(data->map, TMAG5273_MAGNITUDE_RESULT, &val);
+ if (ret < 0)
+ goto out_unlock;
+ *magnitude = val;
+
+out_unlock:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int tmag5273_write_osr(struct tmag5273_data *data, int val)
+{
+ int i;
+
+ if (val == data->conv_avg)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(tmag5273_avg_table); i++) {
+ if (tmag5273_avg_table[i] == val)
+ break;
+ }
+ if (i == ARRAY_SIZE(tmag5273_avg_table))
+ return -EINVAL;
+ data->conv_avg = val;
+
+ return regmap_update_bits(data->map, TMAG5273_DEVICE_CONFIG_1,
+ TMAG5273_AVG_MODE_MASK,
+ FIELD_PREP(TMAG5273_AVG_MODE_MASK, i));
+}
+
+static int tmag5273_write_scale(struct tmag5273_data *data, int scale_micro)
+{
+ u32 value;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tmag5273_scale[0]); i++) {
+ if (tmag5273_scale[data->version][i].micro == scale_micro)
+ break;
+ }
+ if (i == ARRAY_SIZE(tmag5273_scale[0]))
+ return -EINVAL;
+ data->scale_index = i;
+
+ if (data->scale_index == MAGN_RANGE_LOW)
+ value = 0;
+ else
+ value = TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK;
+
+ return regmap_update_bits(data->map, TMAG5273_SENSOR_CONFIG_2,
+ TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK, value);
+}
+
+static int tmag5273_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct tmag5273_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = tmag5273_avg_table;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(tmag5273_avg_table);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_MAGN:
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *vals = (int *)tmag5273_scale[data->version];
+ *length = ARRAY_SIZE(tmag5273_scale[data->version]) *
+ MAGN_RANGE_NUM;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tmag5273_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val,
+ int *val2, long mask)
+{
+ struct tmag5273_data *data = iio_priv(indio_dev);
+ s16 t, x, y, z;
+ u16 angle, magnitude;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ case IIO_CHAN_INFO_RAW:
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = tmag5273_get_measure(data, &t, &x, &y, &z, &angle, &magnitude);
+ if (ret)
+ return ret;
+
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ switch (chan->address) {
+ case TEMPERATURE:
+ *val = t;
+ return IIO_VAL_INT;
+ case AXIS_X:
+ *val = x;
+ return IIO_VAL_INT;
+ case AXIS_Y:
+ *val = y;
+ return IIO_VAL_INT;
+ case AXIS_Z:
+ *val = z;
+ return IIO_VAL_INT;
+ case ANGLE:
+ *val = angle;
+ return IIO_VAL_INT;
+ case MAGNITUDE:
+ *val = magnitude;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ /*
+ * Convert device specific value to millicelsius.
+ * Resolution from the sensor is 60.1 LSB/celsius and
+ * the reference value at 25 celsius is 17508 LSBs.
+ */
+ *val = 10000;
+ *val2 = 601;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_MAGN:
+ /* Magnetic resolution in uT */
+ *val = 0;
+ *val2 = tmag5273_scale[data->version]
+ [data->scale_index].micro;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ANGL:
+ /*
+ * Angle is in degrees and has four fractional bits,
+ * therefore use 1/16 * pi/180 to convert to radians.
+ */
+ *val = 1000;
+ *val2 = 916732;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = -266314;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = data->conv_avg;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tmag5273_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct tmag5273_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return tmag5273_write_osr(data, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_MAGN:
+ if (val)
+ return -EINVAL;
+ return tmag5273_write_scale(data, val2);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+#define TMAG5273_AXIS_CHANNEL(axis, index) \
+ { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+ }
+
+static const struct iio_chan_spec tmag5273_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = TEMPERATURE,
+ .scan_index = TEMPERATURE,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ TMAG5273_AXIS_CHANNEL(X, AXIS_X),
+ TMAG5273_AXIS_CHANNEL(Y, AXIS_Y),
+ TMAG5273_AXIS_CHANNEL(Z, AXIS_Z),
+ {
+ .type = IIO_ANGL,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .address = ANGLE,
+ .scan_index = ANGLE,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_DISTANCE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .address = MAGNITUDE,
+ .scan_index = MAGNITUDE,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(6),
+};
+
+static const struct iio_info tmag5273_info = {
+ .read_avail = tmag5273_read_avail,
+ .read_raw = tmag5273_read_raw,
+ .write_raw = tmag5273_write_raw,
+};
+
+static bool tmag5273_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= TMAG5273_T_MSB_RESULT && reg <= TMAG5273_MAGNITUDE_RESULT;
+}
+
+static const struct regmap_config tmag5273_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TMAG5273_MAX_REG,
+ .volatile_reg = tmag5273_volatile_reg,
+};
+
+static int tmag5273_set_operating_mode(struct tmag5273_data *data,
+ unsigned int val)
+{
+ return regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2, val);
+}
+
+static void tmag5273_read_device_property(struct tmag5273_data *data)
+{
+ struct device *dev = data->dev;
+ const char *str;
+ int ret;
+
+ data->angle_measurement = TMAG5273_ANGLE_EN_X_Y;
+
+ ret = device_property_read_string(dev, "ti,angle-measurement", &str);
+ if (ret)
+ return;
+
+ ret = match_string(tmag5273_angle_names,
+ ARRAY_SIZE(tmag5273_angle_names), str);
+ if (ret >= 0)
+ data->angle_measurement = ret;
+}
+
+static void tmag5273_wake_up(struct tmag5273_data *data)
+{
+ int val;
+
+ /* Wake up the chip by sending a dummy I2C command */
+ regmap_read(data->map, TMAG5273_DEVICE_ID, &val);
+ /*
+ * Time to go to stand-by mode from sleep mode is 50us
+ * typically, during this time no I2C access is possible.
+ */
+ usleep_range(80, 200);
+}
+
+static int tmag5273_chip_init(struct tmag5273_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_1,
+ TMAG5273_AVG_32_MODE);
+ if (ret)
+ return ret;
+ data->conv_avg = 32;
+
+ ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2,
+ TMAG5273_OP_MODE_CONT);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_1,
+ FIELD_PREP(TMAG5273_MAG_CH_EN_MASK,
+ TMAG5273_MAG_CH_EN_X_Y_Z));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_2,
+ FIELD_PREP(TMAG5273_ANGLE_EN_MASK,
+ data->angle_measurement));
+ if (ret)
+ return ret;
+ data->scale_index = MAGN_RANGE_LOW;
+
+ return regmap_write(data->map, TMAG5273_T_CONFIG, TMAG5273_T_CH_EN);
+}
+
+static int tmag5273_check_device_id(struct tmag5273_data *data)
+{
+ __le16 devid;
+ int val, ret;
+
+ ret = regmap_read(data->map, TMAG5273_DEVICE_ID, &val);
+ if (ret)
+ return dev_err_probe(data->dev, ret, "failed to power on device\n");
+ data->version = FIELD_PREP(TMAG5273_VERSION_MASK, val);
+
+ ret = regmap_bulk_read(data->map, TMAG5273_MANUFACTURER_ID_LSB, &devid,
+ sizeof(devid));
+ if (ret)
+ return dev_err_probe(data->dev, ret, "failed to read device ID\n");
+ data->devid = le16_to_cpu(devid);
+
+ switch (data->devid) {
+ case TMAG5273_MANUFACTURER_ID:
+ /*
+ * The device name matches the orderable part number. 'x' stands
+ * for A, B, C or D devices, which have different I2C addresses.
+ * Versions 1 or 2 (0 and 3 is reserved) stands for different
+ * magnetic strengths.
+ */
+ snprintf(data->name, sizeof(data->name), "tmag5273x%1u", data->version);
+ if (data->version < 1 || data->version > 2)
+ dev_warn(data->dev, "Unsupported device %s\n", data->name);
+ return 0;
+ default:
+ /*
+ * Only print warning in case of unknown device ID to allow
+ * fallback compatible in device tree.
+ */
+ dev_warn(data->dev, "Unknown device ID 0x%x\n", data->devid);
+ return 0;
+ }
+}
+
+static void tmag5273_power_down(void *data)
+{
+ tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP);
+}
+
+static int tmag5273_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct tmag5273_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+ i2c_set_clientdata(i2c, indio_dev);
+
+ data->map = devm_regmap_init_i2c(i2c, &tmag5273_regmap_config);
+ if (IS_ERR(data->map))
+ return dev_err_probe(dev, PTR_ERR(data->map),
+ "failed to allocate register map\n");
+
+ mutex_init(&data->lock);
+
+ ret = devm_regulator_get_enable(dev, "vcc");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable regulator\n");
+
+ tmag5273_wake_up(data);
+
+ ret = tmag5273_check_device_id(data);
+ if (ret)
+ return ret;
+
+ ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to power on device\n");
+
+ /*
+ * Register powerdown deferred callback which suspends the chip
+ * after module unloaded.
+ *
+ * TMAG5273 should be in SUSPEND mode in the two cases:
+ * 1) When driver is loaded, but we do not have any data or
+ * configuration requests to it (we are solving it using
+ * autosuspend feature).
+ * 2) When driver is unloaded and device is not used (devm action is
+ * used in this case).
+ */
+ ret = devm_add_action_or_reset(dev, tmag5273_power_down, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add powerdown action\n");
+
+ ret = pm_runtime_set_active(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_autosuspend_delay(dev, TMAG5273_AUTOSLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+
+ tmag5273_read_device_property(data);
+
+ ret = tmag5273_chip_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init device\n");
+
+ indio_dev->info = &tmag5273_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = data->name;
+ indio_dev->channels = tmag5273_channels;
+ indio_dev->num_channels = ARRAY_SIZE(tmag5273_channels);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "device register failed\n");
+
+ return 0;
+}
+
+static int tmag5273_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tmag5273_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP);
+ if (ret)
+ dev_err(dev, "failed to power off device (%pe)\n", ERR_PTR(ret));
+
+ return ret;
+}
+
+static int tmag5273_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tmag5273_data *data = iio_priv(indio_dev);
+ int ret;
+
+ tmag5273_wake_up(data);
+
+ ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT);
+ if (ret)
+ dev_err(dev, "failed to power on device (%pe)\n", ERR_PTR(ret));
+
+ return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(tmag5273_pm_ops,
+ tmag5273_runtime_suspend, tmag5273_runtime_resume,
+ NULL);
+
+static const struct i2c_device_id tmag5273_id[] = {
+ { "tmag5273" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, tmag5273_id);
+
+static const struct of_device_id tmag5273_of_match[] = {
+ { .compatible = "ti,tmag5273" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tmag5273_of_match);
+
+static struct i2c_driver tmag5273_driver = {
+ .driver = {
+ .name = "tmag5273",
+ .of_match_table = tmag5273_of_match,
+ .pm = pm_ptr(&tmag5273_pm_ops),
+ },
+ .probe_new = tmag5273_probe,
+ .id_table = tmag5273_id,
+};
+module_i2c_driver(tmag5273_driver);
+
+MODULE_DESCRIPTION("TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor driver");
+MODULE_AUTHOR("Gerald Loacker <gerald.loacker@wolfvision.net>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h
index 550b75b7186f..a78acd3fb18f 100644
--- a/drivers/iio/pressure/ms5611.h
+++ b/drivers/iio/pressure/ms5611.h
@@ -13,8 +13,6 @@
#include <linux/iio/iio.h>
#include <linux/mutex.h>
-struct regulator;
-
#define MS5611_RESET 0x1e
#define MS5611_READ_ADC 0x00
#define MS5611_READ_PROM_WORD 0xA0
@@ -52,11 +50,9 @@ struct ms5611_state {
int (*compensate_temp_and_pressure)(struct ms5611_state *st, s32 *temp,
s32 *pressure);
- struct regulator *vdd;
};
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
const char *name, int type);
-void ms5611_remove(struct iio_dev *indio_dev);
#endif /* _MS5611_H */
diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
index c564a1d6cafe..627497e61a63 100644
--- a/drivers/iio/pressure/ms5611_core.c
+++ b/drivers/iio/pressure/ms5611_core.c
@@ -380,40 +380,21 @@ static const struct iio_info ms5611_info = {
static int ms5611_init(struct iio_dev *indio_dev)
{
int ret;
- struct ms5611_state *st = iio_priv(indio_dev);
/* Enable attached regulator if any. */
- st->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd");
- if (IS_ERR(st->vdd))
- return PTR_ERR(st->vdd);
-
- ret = regulator_enable(st->vdd);
- if (ret) {
- dev_err(indio_dev->dev.parent,
- "failed to enable Vdd supply: %d\n", ret);
+ ret = devm_regulator_get_enable(indio_dev->dev.parent, "vdd");
+ if (ret)
return ret;
- }
ret = ms5611_reset(indio_dev);
if (ret < 0)
- goto err_regulator_disable;
+ return ret;
ret = ms5611_read_prom(indio_dev);
if (ret < 0)
- goto err_regulator_disable;
+ return ret;
return 0;
-
-err_regulator_disable:
- regulator_disable(st->vdd);
- return ret;
-}
-
-static void ms5611_fini(const struct iio_dev *indio_dev)
-{
- const struct ms5611_state *st = iio_priv(indio_dev);
-
- regulator_disable(st->vdd);
}
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
@@ -453,37 +434,23 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
if (ret < 0)
return ret;
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
ms5611_trigger_handler, NULL);
if (ret < 0) {
dev_err(dev, "iio triggered buffer setup failed\n");
- goto err_fini;
+ return ret;
}
- ret = iio_device_register(indio_dev);
+ ret = devm_iio_device_register(dev, indio_dev);
if (ret < 0) {
dev_err(dev, "unable to register iio device\n");
- goto err_buffer_cleanup;
+ return ret;
}
return 0;
-
-err_buffer_cleanup:
- iio_triggered_buffer_cleanup(indio_dev);
-err_fini:
- ms5611_fini(indio_dev);
- return ret;
}
EXPORT_SYMBOL_NS(ms5611_probe, IIO_MS5611);
-void ms5611_remove(struct iio_dev *indio_dev)
-{
- iio_device_unregister(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
- ms5611_fini(indio_dev);
-}
-EXPORT_SYMBOL_NS(ms5611_remove, IIO_MS5611);
-
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("MS5611 core driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c
index caf882497656..e3c68a3ed76a 100644
--- a/drivers/iio/pressure/ms5611_i2c.c
+++ b/drivers/iio/pressure/ms5611_i2c.c
@@ -105,11 +105,6 @@ static int ms5611_i2c_probe(struct i2c_client *client)
return ms5611_probe(indio_dev, &client->dev, id->name, id->driver_data);
}
-static void ms5611_i2c_remove(struct i2c_client *client)
-{
- ms5611_remove(i2c_get_clientdata(client));
-}
-
static const struct of_device_id ms5611_i2c_matches[] = {
{ .compatible = "meas,ms5611" },
{ .compatible = "meas,ms5607" },
@@ -131,7 +126,6 @@ static struct i2c_driver ms5611_driver = {
},
.id_table = ms5611_id,
.probe_new = ms5611_i2c_probe,
- .remove = ms5611_i2c_remove,
};
module_i2c_driver(ms5611_driver);
diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c
index a0a7205c9c3a..cc9d1f68c53c 100644
--- a/drivers/iio/pressure/ms5611_spi.c
+++ b/drivers/iio/pressure/ms5611_spi.c
@@ -107,11 +107,6 @@ static int ms5611_spi_probe(struct spi_device *spi)
spi_get_device_id(spi)->driver_data);
}
-static void ms5611_spi_remove(struct spi_device *spi)
-{
- ms5611_remove(spi_get_drvdata(spi));
-}
-
static const struct of_device_id ms5611_spi_matches[] = {
{ .compatible = "meas,ms5611" },
{ .compatible = "meas,ms5607" },
@@ -133,7 +128,6 @@ static struct spi_driver ms5611_driver = {
},
.id_table = ms5611_id,
.probe = ms5611_spi_probe,
- .remove = ms5611_spi_remove,
};
module_spi_driver(ms5611_driver);