summaryrefslogtreecommitdiff
path: root/drivers/iio/adc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc')
-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
14 files changed, 1494 insertions, 80 deletions
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);