[PATCH v1 2/2] iio: adc: Add basic support for MAX14001

Marilene Andrade Garcia posted 2 patches 1 month, 1 week ago
There is a newer version of this series
[PATCH v1 2/2] iio: adc: Add basic support for MAX14001
Posted by Marilene Andrade Garcia 1 month, 1 week ago
The MAX14001/MAX14002 are configurable, isolated 10-bit ADCs for
multi-range binary inputs. Besides the ADC readings, the MAX14001/MAX14002
offers more features, like a binary comparator, a filtered reading that
can provide the average of the last 2, 4, or 8 ADC readings, and an inrush
comparator that triggers the inrush current. There is also a fault feature
that can diagnose seven possible fault conditions. And an option to select
an external or internal ADC voltage reference.

Add basic support for MAX14001/MAX14002 with the following features:
- Raw ADC reading.
- Filtered ADC average reading with the default configuration.

Signed-off-by: Marilene Andrade Garcia <marilene.agarcia@gmail.com>
---
 MAINTAINERS                |   1 +
 drivers/iio/adc/Kconfig    |  10 ++
 drivers/iio/adc/Makefile   |   1 +
 drivers/iio/adc/max14001.c | 213 +++++++++++++++++++++++++++++++++++++
 4 files changed, 225 insertions(+)
 create mode 100644 drivers/iio/adc/max14001.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0aeab5dbd39d..ea5d6d9ba5eb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14990,6 +14990,7 @@ L:	linux-iio@vger.kernel.org
 S:	Maintained
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/adc/adi,max14001.yaml
+F:	drivers/iio/adc/max14001.c
 
 MAXIM MAX17040 FAMILY FUEL GAUGE DRIVERS
 R:	Iskren Chernev <iskren.chernev@gmail.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 6de2abad0197..12688c9780e1 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -976,6 +976,16 @@ config MAX1363
 	  To compile this driver as a module, choose M here: the module will be
 	  called max1363.
 
+config MAX14001
+       tristate "Analog Devices MAX14001/MAX14002 ADCs driver"
+       depends on SPI
+       help
+         Say yes here to build support for Analog Devices MAX14001/MAX14002
+         Configurable, Isolated 10-bit ADCs for Multi-Range Binary Inputs.
+
+         To compile this driver as a module, choose M here: the module will be
+         called max14001.
+
 config MAX34408
 	tristate "Maxim max34408/max344089 ADC driver"
 	depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 1c6ca5fd4b6d..05b930a1bce5 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_MAX11205) += max11205.o
 obj-$(CONFIG_MAX11410) += max11410.o
 obj-$(CONFIG_MAX1241) += max1241.o
 obj-$(CONFIG_MAX1363) += max1363.o
+obj-$(CONFIG_MAX14001) += max14001.o
 obj-$(CONFIG_MAX34408) += max34408.o
 obj-$(CONFIG_MAX77541_ADC) += max77541-adc.o
 obj-$(CONFIG_MAX9611) += max9611.o
diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c
new file mode 100644
index 000000000000..fb79f3b81e0c
--- /dev/null
+++ b/drivers/iio/adc/max14001.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MAX14001/MAX14002 SPI ADC driver
+ *
+ * Copyright (c) 2025 Marilene Andrade Garcia <marilene.agarcia@gmail.com>
+ *
+ * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX14001-MAX14002.pdf
+ */
+
+#include <asm/unaligned.h>
+#include <linux/bitfield.h>
+#include <linux/bitrev.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+
+/* MAX14001 registers definition */
+#define MAX14001_REG_ADC				0x00
+#define MAX14001_REG_FADC				0x01
+#define MAX14001_REG_FLAGS				0x02
+#define MAX14001_REG_FLTEN				0x03
+#define MAX14001_REG_THL				0x04
+#define MAX14001_REG_THU				0x05
+#define MAX14001_REG_INRR				0x06
+#define MAX14001_REG_INRT				0x07
+#define MAX14001_REG_INRP				0x08
+#define MAX14001_REG_CFG				0x09
+#define MAX14001_REG_ENBL				0x0A
+#define MAX14001_REG_ACT				0x0B
+#define MAX14001_REG_WEN				0x0C
+
+/* MAX14001 CONTROL values*/
+#define MAX14001_REG_WRITE				0x1
+#define MAX14001_REG_READ				0x0
+
+/* MAX14001 MASKS */
+#define MAX14001_MASK_ADDR				GENMASK(15, 11)
+#define MAX14001_MASK_WR				BIT(10)
+#define MAX14001_MASK_DATA				GENMASK(9, 0)
+
+enum max14001_chip_model {
+	max14001,
+	max14002,
+};
+
+struct max14001_chip_info {
+	const char *name;
+};
+
+struct max14001_state {
+	const struct max14001_chip_info *chip_info;
+	struct spi_device *spi;
+	int vref_mv;
+
+	__be16 rx_buffer __aligned(IIO_DMA_MINALIGN);
+	__be16 tx_buffer;
+};
+
+static struct max14001_chip_info max14001_chip_info_tbl[] = {
+	[max14001] = {
+		.name = "max14001",
+	},
+	[max14002] = {
+		.name = "max14002",
+	},
+};
+
+static int max14001_spi_read(struct max14001_state *st, u16 reg, int *val)
+{
+	struct spi_transfer xfer[] = {
+		{
+			.tx_buf = &st->tx_buffer,
+			.len = sizeof(st->tx_buffer),
+			.cs_change = 1,
+		},
+		{
+			.rx_buf = &st->rx_buffer,
+			.len = sizeof(st->rx_buffer),
+		},
+	};
+	int ret;
+
+	st->tx_buffer = FIELD_PREP(MAX14001_MASK_ADDR, reg) |
+			FIELD_PREP(MAX14001_MASK_WR, MAX14001_REG_READ);
+	st->tx_buffer = bitrev16(st->tx_buffer);
+
+	ret = spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
+	if (ret < 0)
+		return ret;
+
+	st->rx_buffer = bitrev16(be16_to_cpu(st->rx_buffer));
+	*val = FIELD_GET(MAX14001_MASK_DATA, st->rx_buffer);
+
+	return 0;
+}
+
+static int max14001_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val, int *val2, long mask)
+{
+	struct max14001_state *st = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = max14001_spi_read(st, MAX14001_REG_ADC, val);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_AVERAGE_RAW:
+		ret = max14001_spi_read(st, MAX14001_REG_FADC, val);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = st->vref_mv;
+		*val2 = 10;
+
+		return IIO_VAL_FRACTIONAL_LOG2;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info max14001_info = {
+	.read_raw = max14001_read_raw,
+};
+
+static const struct iio_chan_spec max14001_channel[] = {
+	{
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_AVERAGE_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+	}
+};
+
+static int max14001_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct max14001_state *st;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+	st->spi = spi;
+	st->chip_info = spi_get_device_match_data(spi);
+	if (!st->chip_info)
+		return dev_err_probe(dev, -ENODEV, "Failed to get match data\n");
+
+	indio_dev->name = st->chip_info->name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &max14001_info;
+	indio_dev->channels = max14001_channel;
+	indio_dev->num_channels = ARRAY_SIZE(max14001_channel);
+
+	ret = devm_regulator_get_enable(dev, "vdd");
+	if (ret)
+		return dev_err_probe(dev, ret,
+			"Failed to enable specified Vdd supply\n");
+
+	ret = devm_regulator_get_enable(dev, "vddl");
+	if (ret)
+		return dev_err_probe(dev, ret,
+			"Failed to enable specified Vddl supply\n");
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "vrefin");
+	if (ret < 0)
+		st->vref_mv = 1250000 / 1000;
+	else
+		st->vref_mv = ret / 1000;
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id max14001_id_table[] = {
+	{ "max14001", (kernel_ulong_t)&max14001_chip_info_tbl[max14001] },
+	{ "max14002", (kernel_ulong_t)&max14001_chip_info_tbl[max14002] },
+	{}
+};
+MODULE_DEVICE_TABLE(spi, max14001_id_table);
+
+static const struct of_device_id max14001_of_match[] = {
+	{ .compatible = "adi,max14001",
+	  .data = &max14001_chip_info_tbl[max14001], },
+	{ .compatible = "adi,max14002",
+	  .data = &max14001_chip_info_tbl[max14002], },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max14001_of_match);
+
+static struct spi_driver max14001_driver = {
+	.driver = {
+		.name = "max14001",
+		.of_match_table = max14001_of_match,
+	},
+	.probe = max14001_probe,
+	.id_table = max14001_id_table,
+};
+module_spi_driver(max14001_driver);
+
+MODULE_AUTHOR("Marilene Andrade Garcia <marilene.agarcia@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices MAX14001/MAX14002 ADCs driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1
Re: [PATCH v1 2/2] iio: adc: Add basic support for MAX14001
Posted by Jonathan Cameron 1 month, 1 week ago
On Thu, 21 Aug 2025 10:39:07 -0300
Marilene Andrade Garcia <marilene.agarcia@gmail.com> wrote:

> The MAX14001/MAX14002 are configurable, isolated 10-bit ADCs for
> multi-range binary inputs. Besides the ADC readings, the MAX14001/MAX14002
> offers more features, like a binary comparator, a filtered reading that
> can provide the average of the last 2, 4, or 8 ADC readings, and an inrush
> comparator that triggers the inrush current. There is also a fault feature
> that can diagnose seven possible fault conditions. And an option to select
> an external or internal ADC voltage reference.
> 
> Add basic support for MAX14001/MAX14002 with the following features:
> - Raw ADC reading.
> - Filtered ADC average reading with the default configuration.
> 
> Signed-off-by: Marilene Andrade Garcia <marilene.agarcia@gmail.com>

Given the discussion on the cover letter, perhaps this will need to be
merged with the earlier set. I'll do a quick review anyway!

Fairly minor comments inline. This is in a pretty good state for a v1.

Thanks,

Jonathan


> ---
>  MAINTAINERS                |   1 +
>  drivers/iio/adc/Kconfig    |  10 ++
>  drivers/iio/adc/Makefile   |   1 +
>  drivers/iio/adc/max14001.c | 213 +++++++++++++++++++++++++++++++++++++
>  4 files changed, 225 insertions(+)
>  create mode 100644 drivers/iio/adc/max14001.c

> diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c
> new file mode 100644
> index 000000000000..fb79f3b81e0c
> --- /dev/null
> +++ b/drivers/iio/adc/max14001.c
> @@ -0,0 +1,213 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * MAX14001/MAX14002 SPI ADC driver
> + *
> + * Copyright (c) 2025 Marilene Andrade Garcia <marilene.agarcia@gmail.com>
> + *
> + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX14001-MAX14002.pdf
> + */
> +
> +#include <asm/unaligned.h>

As per build bot this doesn't work on modern kernels.  linux/unaligned.

> +#include <linux/bitfield.h>
> +#include <linux/bitrev.h>
> +#include <linux/module.h>

various headers missing. Please follow approximate include what you use principles.
There are some headers that are obviously going to be included by another one
because they cannot stand alone, so for those you can include just the parent.
E.g. mutex.h not mutex_types.h but in most other cases all includes with definitions
that are used in this file should be here.

> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/regulator/consumer.h>
> +
> +/* MAX14001 registers definition */
> +#define MAX14001_REG_ADC				0x00
> +#define MAX14001_REG_FADC				0x01
> +#define MAX14001_REG_FLAGS				0x02
> +#define MAX14001_REG_FLTEN				0x03
> +#define MAX14001_REG_THL				0x04
> +#define MAX14001_REG_THU				0x05
> +#define MAX14001_REG_INRR				0x06
> +#define MAX14001_REG_INRT				0x07
> +#define MAX14001_REG_INRP				0x08
> +#define MAX14001_REG_CFG				0x09
> +#define MAX14001_REG_ENBL				0x0A
> +#define MAX14001_REG_ACT				0x0B
> +#define MAX14001_REG_WEN				0x0C
> +
> +/* MAX14001 CONTROL values*/

Missing space before */

They are going in the WR field below I'd rename that MASK_W
then you can just 1 and 0 with their boolean meaning and
not bother with these defines.

> +#define MAX14001_REG_WRITE				0x1
> +#define MAX14001_REG_READ				0x0
> +
> +/* MAX14001 MASKS */
The comment isn't very useful.  Masks of what?  These seems to be
SPI message related.  Also no point in prefixing comments
with MAX14001 when the naming makes that clear.


> +#define MAX14001_MASK_ADDR				GENMASK(15, 11)
> +#define MAX14001_MASK_WR				BIT(10)
> +#define MAX14001_MASK_DATA				GENMASK(9, 0)
> +
> +enum max14001_chip_model {
> +	max14001,
> +	max14002,
> +};
> +
> +struct max14001_chip_info {
> +	const char *name;
> +};
> +
> +struct max14001_state {
> +	const struct max14001_chip_info *chip_info;
> +	struct spi_device *spi;
> +	int vref_mv;
> +
> +	__be16 rx_buffer __aligned(IIO_DMA_MINALIGN);
> +	__be16 tx_buffer;

I'd add a comment on these to mention they are also bit
reversed after we've flipped the bytes to be in the right order.

> +};
> +
> +static struct max14001_chip_info max14001_chip_info_tbl[] = {
> +	[max14001] = {
> +		.name = "max14001",
> +	},
> +	[max14002] = {
> +		.name = "max14002",
> +	},
> +};
> +
> +static int max14001_spi_read(struct max14001_state *st, u16 reg, int *val)

The register map is large enough I'd consider using a custom regmap
as then we can take advantage of caching and the field manipulation
functions that we get from regmap.

> +{
> +	struct spi_transfer xfer[] = {
> +		{
> +			.tx_buf = &st->tx_buffer,
> +			.len = sizeof(st->tx_buffer),
> +			.cs_change = 1,
> +		},
> +		{
> +			.rx_buf = &st->rx_buffer,
> +			.len = sizeof(st->rx_buffer),
> +		},
> +	};
> +	int ret;
> +

No locking? Given use of shared buffers I would suggest you need
a mutex here, the bus lock won't be enough.

> +	st->tx_buffer = FIELD_PREP(MAX14001_MASK_ADDR, reg) |
> +			FIELD_PREP(MAX14001_MASK_WR, MAX14001_REG_READ);
> +	st->tx_buffer = bitrev16(st->tx_buffer);
> +
> +	ret = spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
> +	if (ret < 0)
> +		return ret;
> +
> +	st->rx_buffer = bitrev16(be16_to_cpu(st->rx_buffer));
> +	*val = FIELD_GET(MAX14001_MASK_DATA, st->rx_buffer);
> +
> +	return 0;
> +}


> +static const struct spi_device_id max14001_id_table[] = {
> +	{ "max14001", (kernel_ulong_t)&max14001_chip_info_tbl[max14001] },
> +	{ "max14002", (kernel_ulong_t)&max14001_chip_info_tbl[max14002] },
> +	{}

Trivial but for consistency
	{ }

which is the preferred style in IIO.  There isn't any general agreement
across the kernel so I picked a choice at random a while back as any choice
is better than a mix like we have here.

> +};
> +MODULE_DEVICE_TABLE(spi, max14001_id_table);
> +
> +static const struct of_device_id max14001_of_match[] = {
> +	{ .compatible = "adi,max14001",
> +	  .data = &max14001_chip_info_tbl[max14001], },
> +	{ .compatible = "adi,max14002",
> +	  .data = &max14001_chip_info_tbl[max14002], },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, max14001_of_match);
Re: [PATCH v1 2/2] iio: adc: Add basic support for MAX14001
Posted by kernel test robot 1 month, 1 week ago
Hi Marilene,

kernel test robot noticed the following build errors:

[auto build test ERROR on 7c680c4dbbb5365ad78ce661886ce1668ff40f9c]

url:    https://github.com/intel-lab-lkp/linux/commits/Marilene-Andrade-Garcia/dt-bindings-iio-adc-Add-MAX14001/20250821-225647
base:   7c680c4dbbb5365ad78ce661886ce1668ff40f9c
patch link:    https://lore.kernel.org/r/2919a00f86c1188b83446853bcb9740138d70f44.1755778212.git.marilene.agarcia%40gmail.com
patch subject: [PATCH v1 2/2] iio: adc: Add basic support for MAX14001
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20250822/202508221427.TaHJJwvG-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250822/202508221427.TaHJJwvG-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508221427.TaHJJwvG-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/iio/adc/max14001.c:10:10: fatal error: 'asm/unaligned.h' file not found
      10 | #include <asm/unaligned.h>
         |          ^~~~~~~~~~~~~~~~~
   1 error generated.


vim +10 drivers/iio/adc/max14001.c

  > 10	#include <asm/unaligned.h>
    11	#include <linux/bitfield.h>
    12	#include <linux/bitrev.h>
    13	#include <linux/module.h>
    14	#include <linux/spi/spi.h>
    15	#include <linux/iio/iio.h>
    16	#include <linux/regulator/consumer.h>
    17	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki