diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-24 12:47:33 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-24 12:47:33 -0800 |
commit | 693fed981eb9bf6e70bfda66bb872e2bb8155671 (patch) | |
tree | f37b03fde9901e75fa77d6943ee54b29f9064f53 /drivers/iio/adc | |
parent | 0601f25d1c4937c678db786961705ce56fbd6bb6 (diff) | |
parent | 6ec363fc6142226b9ab5a6528f65333d729d2b6b (diff) | |
download | lwn-693fed981eb9bf6e70bfda66bb872e2bb8155671.tar.gz lwn-693fed981eb9bf6e70bfda66bb872e2bb8155671.zip |
Merge tag 'char-misc-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc and other driver subsystem updates from Greg KH:
"Here is the large set of driver changes for char/misc drivers and
other smaller driver subsystems that flow through this git tree.
Included in here are:
- New IIO drivers and features and improvments in that subsystem
- New hwtracing drivers and additions to that subsystem
- lots of interconnect changes and new drivers as that subsystem
seems under very active development recently. This required also
merging in the icc subsystem changes through this tree.
- FPGA driver updates
- counter subsystem and driver updates
- MHI driver updates
- nvmem driver updates
- documentation updates
- Other smaller driver updates and fixes, full details in the
shortlog
All of these have been in linux-next for a while with no reported
problems"
* tag 'char-misc-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (223 commits)
scripts/tags.sh: fix incompatibility with PCRE2
firmware: coreboot: Remove GOOGLE_COREBOOT_TABLE_ACPI/OF Kconfig entries
mei: lower the log level for non-fatal failed messages
mei: bus: disallow driver match while dismantling device
misc: vmw_balloon: fix memory leak with using debugfs_lookup()
nvmem: stm32: fix OPTEE dependency
dt-bindings: nvmem: qfprom: add IPQ8074 compatible
nvmem: qcom-spmi-sdam: register at device init time
nvmem: rave-sp-eeprm: fix kernel-doc bad line warning
nvmem: stm32: detect bsec pta presence for STM32MP15x
nvmem: stm32: add OP-TEE support for STM32MP13x
nvmem: core: use nvmem_add_one_cell() in nvmem_add_cells_from_of()
nvmem: core: add nvmem_add_one_cell()
nvmem: core: drop the removal of the cells in nvmem_add_cells()
nvmem: core: move struct nvmem_cell_info to nvmem-provider.h
nvmem: core: add an index parameter to the cell
of: property: add #nvmem-cell-cells property
of: property: make #.*-cells optional for simple props
of: base: add of_parse_phandle_with_optional_args()
net: add helper eth_addr_add()
...
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r-- | drivers/iio/adc/Kconfig | 34 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 3 | ||||
-rw-r--r-- | drivers/iio/adc/ad7291.c | 2 | ||||
-rw-r--r-- | drivers/iio/adc/at91-sama5d2_adc.c | 4 | ||||
-rw-r--r-- | drivers/iio/adc/ep93xx_adc.c | 8 | ||||
-rw-r--r-- | drivers/iio/adc/imx93_adc.c | 484 | ||||
-rw-r--r-- | drivers/iio/adc/max11410.c | 3 | ||||
-rw-r--r-- | drivers/iio/adc/qcom-spmi-adc5.c | 8 | ||||
-rw-r--r-- | drivers/iio/adc/stm32-dfsdm-core.c | 99 | ||||
-rw-r--r-- | drivers/iio/adc/stm32-dfsdm.h | 60 | ||||
-rw-r--r-- | drivers/iio/adc/ti-adc128s052.c | 54 | ||||
-rw-r--r-- | drivers/iio/adc/ti-ads7924.c | 474 | ||||
-rw-r--r-- | drivers/iio/adc/ti-lmp92064.c | 332 | ||||
-rw-r--r-- | drivers/iio/adc/xilinx-ams.c | 9 |
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); |