diff --git a/Documentation/devicetree/bindings/iio/adc/adi,max22531.yaml b/Documentation/devicetree/bindings/iio/adc/adi,max22531.yaml new file mode 100644 index 00000000000000..16bf4a1d0926a7 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,max22531.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2025 Abhinav Jain +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,max22531.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX22530-MAX22532 12-bit Field Isolated ADCs + +maintainers: + - Abhinav Jain + +description: + Bindings for the Analog Devices Max22530-MAX22532 Field-Side Self-Powered, + 4-Channel, 12-bit, Isolated ADCs. + + Datasheet can be found here + https://www.analog.com/media/en/technical-documentation/data-sheets/max22530-max22532.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - adi,max22530 + - adi,max22531 + - adi,max22532 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 10000000 + description: Maximum SPI frequency (Hz) + + vddl-supply: + description: + Logic power supply. + + vddpl-supply: + description: + Isolated DC-DC converter power supply. + + interrupts: + items: + - description: | + Interrupt for signaling when conversion results exceed the upper + threshold for ADC readings or fall below the lower threshold. The + interrupt source must be attached to one of COUT1 to COUT4 pins. + - description: | + Alert output that asserts low during a number of different error + conditions. The interrupt source must be attached to INT pin. + +required: + - compatible + - reg + - vddl-supply + - vddpl-supply + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + max22531: adc@0 { + compatible = "adi,max22531"; + reg = <0>; + spi-max-frequency = <5000000>; + vddl-supply = <&vddl>; + vddpl-supply = <&vddpl>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 56f521c7321d4a..aff372142db801 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13052,6 +13052,14 @@ F: Documentation/userspace-api/media/drivers/max2175.rst F: drivers/media/i2c/max2175* F: include/uapi/linux/max2175.h +MAX22531 ADC DRIVER +M: Abhinav Jain +L: linux-iio@vger.kernel.org +S: Maintained +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,max22531.yaml +F: drivers/iio/adc/max22531.c + MAX31827 TEMPERATURE SWITCH DRIVER M: Daniel Matyas L: linux-hwmon@vger.kernel.org diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile index a9fa5cc30d63fe..c6a45c699815d7 100644 --- a/arch/arm/boot/dts/overlays/Makefile +++ b/arch/arm/boot/dts/overlays/Makefile @@ -311,6 +311,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ rpi-ltc6952.dtbo \ rpi-max14830-i2c.dtbo \ rpi-max14830-spi.dtbo \ + rpi-max22531.dtbo \ rpi-max31335.dtbo \ rpi-poe.dtbo \ rpi-poe-plus.dtbo \ diff --git a/arch/arm/boot/dts/overlays/rpi-max22531-overlay.dts b/arch/arm/boot/dts/overlays/rpi-max22531-overlay.dts new file mode 100644 index 00000000000000..e734b05a3d07b1 --- /dev/null +++ b/arch/arm/boot/dts/overlays/rpi-max22531-overlay.dts @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: (GPL-2.0+) +/* + * Device Tree Overlay for MAX22531 + */ + +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2835", "brcm,bcm2711", "brcm,bcm2712"; + + vddl_1v8: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "Power Input for the Logic-Side"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + vddpl_3v3: fixedregulator@1 { + compatible = "regulator-fixed"; + regulator-name = "Power Input for the Isolated DC-DC Converter"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&spi0 { + status = "okay"; + + max22531@0 { + compatible = "adi,max22531"; + reg = <0>; /* Using CS0 on spi0 */ + spi-max-frequency = <5000000>; + vddl-supply = <&vddl_1v8>; + vddpl-supply = <&vddpl_3v3>; + }; +}; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d3d12ed064e50f..8fe7421501c917 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1126,6 +1126,16 @@ config MAX1363 To compile this driver as a module, choose M here: the module will be called max1363. +config MAX22531 + tristate "Analog Devices MAX22531 ADC Driver" + depends on SPI + help + Say yes here to build support for field-side self-powered 12-bit + isolated Maxim ADCs. (max22530, max22531, max22532). + + To compile this driver as a module, choose M here: the module will be + called max22531. + config MAX77541_ADC tristate "Analog Devices MAX77541 ADC driver" depends on MFD_MAX77541 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 6a71631c984217..510d0a3cea6baf 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -156,6 +156,7 @@ obj-$(CONFIG_MAX11205) += max11205.o obj-$(CONFIG_MAX11410) += max11410.o obj-$(CONFIG_MAX1241) += max1241.o obj-$(CONFIG_MAX1363) += max1363.o +obj-$(CONFIG_MAX22531) += max22531.o obj-$(CONFIG_MAX77541_ADC) += max77541-adc.o obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o diff --git a/drivers/iio/adc/max22531.c b/drivers/iio/adc/max22531.c new file mode 100644 index 00000000000000..c453df0a344a08 --- /dev/null +++ b/drivers/iio/adc/max22531.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MAX22531 SPI ADC Driver + * + * Copyright (C) 2025 Abhinav Jain + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max22530-max22532.pdf + */ + +#include +#include +#include +#include +#include + +#define MAX22531_REG_PROD_ID 0x00 +#define MAX22531_REG_ADC_CHAN(x) ((x) + 1) +#define MAX22531_REG_FADC_CHAN(x) ((x) + 1) + +#define MAX22531_VREF_MV 1800 +#define MAX22531_DEVICE_REV_MSK GENMASK(6, 0) +#define MAX22531_DEVICE_REV 0x01 + +#define MAX22531_REG_ADDR_MASK GENMASK(7, 2) +#define MAX22531_REG_WRITE_MASK BIT(1) + +enum max22531_id { + max22530, + max22531, + max22532, +}; + +struct max22531_chip_info { + const char *name; +}; + +static struct max22531_chip_info max22531_chip_info_tbl[] = { + [max22530] = { + .name = "max22530", + }, + [max22531] = { + .name = "max22531", + }, + [max22532] = { + .name = "max22532", + }, +}; + +struct max22531 { + struct spi_device *spi_dev; + const struct max22531_chip_info *chip_info; +}; + +#define MAX22531_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + } + +static const struct iio_chan_spec max22531_channels[] = { + MAX22531_CHANNEL(0), + MAX22531_CHANNEL(1), + MAX22531_CHANNEL(2), + MAX22531_CHANNEL(3), +}; + +static int max22531_reg_read(struct max22531 *adc, unsigned int reg, + unsigned int *readval) +{ + u8 cmd; + + cmd = FIELD_PREP(MAX22531_REG_ADDR_MASK, reg); + *readval = spi_w8r16be(adc->spi_dev, cmd); + if (*readval < 0) + return *readval; + + return 0; +} + +static int max22531_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max22531 *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = max22531_reg_read(adc, MAX22531_REG_ADC_CHAN(chan->channel), val); + if (ret) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_AVERAGE_RAW: + ret = max22531_reg_read(adc, MAX22531_REG_FADC_CHAN(chan->channel), val); + if (ret) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = MAX22531_VREF_MV; + *val2 = 12; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static const struct iio_info max22531_info = { + .read_raw = max22531_read_raw, +}; + +static int max22531_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct max22531 *adc; + unsigned int prod_id; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi_dev = spi; + adc->chip_info = spi_get_device_match_data(spi); + if (!adc->chip_info) + return dev_err_probe(&spi->dev, -EINVAL, "no chip info\n"); + + indio_dev->name = adc->chip_info->name; + indio_dev->info = &max22531_info; + indio_dev->channels = max22531_channels; + indio_dev->num_channels = ARRAY_SIZE(max22531_channels); + + ret = devm_regulator_get_enable(&spi->dev, "vddl"); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to retrieve power logic supply.\n"); + + ret = devm_regulator_get_enable(&spi->dev, "vddpl"); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to retrieve isolated DC-DC supply.\n"); + + ret = max22531_reg_read(adc, MAX22531_REG_PROD_ID, &prod_id); + if (ret || + FIELD_GET(MAX22531_DEVICE_REV_MSK, prod_id) != MAX22531_DEVICE_REV) + dev_warn(&spi->dev, "PROD_ID verification failed\n"); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id max22531_id[] = { + { "max22530", (kernel_ulong_t)&max22531_chip_info_tbl[max22530] }, + { "max22531", (kernel_ulong_t)&max22531_chip_info_tbl[max22531] }, + { "max22532", (kernel_ulong_t)&max22531_chip_info_tbl[max22532] }, + { } +}; +MODULE_DEVICE_TABLE(spi, max22531_id); + +static const struct of_device_id max22531_spi_of_id[] = { + { .compatible = "adi,max22530", + .data = &max22531_chip_info_tbl[max22530], }, + { .compatible = "adi,max22531", + .data = &max22531_chip_info_tbl[max22531], }, + { .compatible = "adi,max22532", + .data = &max22531_chip_info_tbl[max22532], }, + { } +}; +MODULE_DEVICE_TABLE(of, max22531_spi_of_id); + +static struct spi_driver max22531_driver = { + .driver = { + .name = "max22531", + .of_match_table = max22531_spi_of_id, + }, + .probe = max22531_probe, + .id_table = max22531_id, +}; +module_spi_driver(max22531_driver); + +MODULE_AUTHOR("Abhinav Jain "); +MODULE_DESCRIPTION("MAX22531 ADC"); +MODULE_LICENSE("GPL");