Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions Documentation/devicetree/bindings/iio/adc/adi,max22531.yaml
Original file line number Diff line number Diff line change
@@ -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 <jain.abhinav177@gmail.com>

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the "DO attempt to make bindings complete even if a driver doesn't support some features." guideline, we will also need to document the interrupt.
There should be plenty of examples of devices that can fire interrupts when readings surpass/undergo a threshold.
One device I recall having something similar is AD7091R-2/-4/-5/-8 (see Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml).
I get the impression max22531 interrupt will be a bit trickier to document since the interrupt may be triggered by conditions other than the threshold.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

max22531 and max14001 are similar in the interrupts they can provide.
I think max22531's interrupts can be documented more or less like the following.

  interrupts:
    maxItems: 5
    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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack

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>;
};
};
8 changes: 8 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -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 <jain.abhinav177@gmail.com>
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 <daniel.matyas@analog.com>
L: linux-hwmon@vger.kernel.org
Expand Down
1 change: 1 addition & 0 deletions arch/arm/boot/dts/overlays/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
41 changes: 41 additions & 0 deletions arch/arm/boot/dts/overlays/rpi-max22531-overlay.dts
Original file line number Diff line number Diff line change
@@ -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>;
};
};
10 changes: 10 additions & 0 deletions drivers/iio/adc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/iio/adc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
190 changes: 190 additions & 0 deletions drivers/iio/adc/max22531.c
Original file line number Diff line number Diff line change
@@ -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 <linux/module.h>
#include <asm/unaligned.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After porting the pacthes to the IIO tree, update this to #include <linux/unaligned.h>.

-#include <asm/unaligned.h>
+#include <linux/unaligned.h>

#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>

#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 <jain.abhinav177@gmail.com>");
MODULE_DESCRIPTION("MAX22531 ADC");
MODULE_LICENSE("GPL");