| 
 | 1 | +// SPDX-License-Identifier: GPL-2.0-only  | 
 | 2 | +/*  | 
 | 3 | + * MAX22531 SPI ADC Driver  | 
 | 4 | + *  | 
 | 5 | + * Copyright (C) 2025 Abhinav Jain  | 
 | 6 | + *  | 
 | 7 | + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max22530-max22532.pdf  | 
 | 8 | + */  | 
 | 9 | + | 
 | 10 | +#include <linux/units.h>  | 
 | 11 | +#include <linux/module.h>  | 
 | 12 | +#include <asm/unaligned.h>  | 
 | 13 | +#include <linux/spi/spi.h>  | 
 | 14 | +#include <linux/iio/iio.h>  | 
 | 15 | +#include <linux/regulator/consumer.h>  | 
 | 16 | + | 
 | 17 | +#define MAX22531_REG_PROD_ID		0x00  | 
 | 18 | +#define MAX22531_REG_ADC_CHAN(x)	((x) + 1)  | 
 | 19 | +#define MAX22531_REG_FADC_CHAN(x)	((x) + 1)  | 
 | 20 | + | 
 | 21 | +#define MAX22531_VREF_MV		(18 * MILLI)  | 
 | 22 | +#define MAX22531_DEVICE_REV_MSK		GENMASK(6, 0)  | 
 | 23 | +#define MAX22531_DEVICE_REV		0x01  | 
 | 24 | + | 
 | 25 | +#define MAX22531_REG_ADDR_MASK		GENMASK(7, 2)  | 
 | 26 | +#define MAX22531_REG_WRITE_MASK		BIT(1)  | 
 | 27 | + | 
 | 28 | +enum max22531_id {  | 
 | 29 | +	max22530,  | 
 | 30 | +	max22531,  | 
 | 31 | +	max22532,  | 
 | 32 | +};  | 
 | 33 | + | 
 | 34 | +struct max22531_chip_info {  | 
 | 35 | +	const char *name;  | 
 | 36 | +};  | 
 | 37 | + | 
 | 38 | +static struct max22531_chip_info max22531_chip_info_tbl[] = {  | 
 | 39 | +	[max22530] = {  | 
 | 40 | +		.name = "max22530",  | 
 | 41 | +	},  | 
 | 42 | +	[max22531] = {  | 
 | 43 | +		.name = "max22531",  | 
 | 44 | +	},  | 
 | 45 | +	[max22532] = {  | 
 | 46 | +		.name = "max22532",  | 
 | 47 | +	},  | 
 | 48 | +};  | 
 | 49 | + | 
 | 50 | +struct max22531 {  | 
 | 51 | +	struct spi_device *spi_dev;  | 
 | 52 | +	const struct max22531_chip_info *chip_info;  | 
 | 53 | +};  | 
 | 54 | + | 
 | 55 | +#define MAX22531_CHANNEL(ch)						\  | 
 | 56 | +	{								\  | 
 | 57 | +		.type = IIO_VOLTAGE,					\  | 
 | 58 | +		.indexed = 1,						\  | 
 | 59 | +		.channel = (ch),					\  | 
 | 60 | +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\  | 
 | 61 | +				BIT(IIO_CHAN_INFO_AVERAGE_RAW),         \  | 
 | 62 | +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\  | 
 | 63 | +	}  | 
 | 64 | + | 
 | 65 | +static const struct iio_chan_spec max22531_channels[] = {  | 
 | 66 | +	MAX22531_CHANNEL(0),  | 
 | 67 | +	MAX22531_CHANNEL(1),  | 
 | 68 | +	MAX22531_CHANNEL(2),  | 
 | 69 | +	MAX22531_CHANNEL(3),  | 
 | 70 | +};  | 
 | 71 | + | 
 | 72 | +static int max22531_reg_read(struct max22531 *adc, unsigned int reg,  | 
 | 73 | +			     unsigned int *readval)  | 
 | 74 | +{  | 
 | 75 | +	u8 cmd;  | 
 | 76 | + | 
 | 77 | +	cmd = FIELD_PREP(MAX22531_REG_ADDR_MASK, reg);  | 
 | 78 | +	*readval = spi_w8r16be(adc->spi_dev, cmd);  | 
 | 79 | +	if (*readval < 0)  | 
 | 80 | +		return *readval;  | 
 | 81 | + | 
 | 82 | +	return 0;  | 
 | 83 | +}  | 
 | 84 | + | 
 | 85 | +static int max22531_read_raw(struct iio_dev *indio_dev,  | 
 | 86 | +			     struct iio_chan_spec const *chan,  | 
 | 87 | +			     int *val, int *val2, long mask)  | 
 | 88 | +{  | 
 | 89 | +	struct max22531 *adc = iio_priv(indio_dev);  | 
 | 90 | +	int ret;  | 
 | 91 | + | 
 | 92 | +	switch (mask) {  | 
 | 93 | +	case IIO_CHAN_INFO_RAW:  | 
 | 94 | +		ret = max22531_reg_read(adc, MAX22531_REG_ADC_CHAN(chan->channel), val);  | 
 | 95 | +		if (ret)  | 
 | 96 | +			return ret;  | 
 | 97 | +	return IIO_VAL_INT;  | 
 | 98 | + | 
 | 99 | +	case IIO_CHAN_INFO_AVERAGE_RAW:  | 
 | 100 | +		ret = max22531_reg_read(adc, MAX22531_REG_FADC_CHAN(chan->channel), val);  | 
 | 101 | +		if (ret)  | 
 | 102 | +			return ret;  | 
 | 103 | +		return IIO_VAL_INT;  | 
 | 104 | + | 
 | 105 | +	case IIO_CHAN_INFO_SCALE:  | 
 | 106 | +		*val = MAX22531_VREF_MV;  | 
 | 107 | +		*val2 = 12;  | 
 | 108 | + | 
 | 109 | +		return IIO_VAL_FRACTIONAL_LOG2;  | 
 | 110 | + | 
 | 111 | +	default:  | 
 | 112 | +		return -EINVAL;  | 
 | 113 | +	}  | 
 | 114 | +}  | 
 | 115 | + | 
 | 116 | +static const struct iio_info max22531_info = {  | 
 | 117 | +	.read_raw = max22531_read_raw,  | 
 | 118 | +};  | 
 | 119 | + | 
 | 120 | +static int max22531_probe(struct spi_device *spi)  | 
 | 121 | +{  | 
 | 122 | +	const struct max22531_chip_info *chip_info;  | 
 | 123 | +	struct iio_dev *indio_dev;  | 
 | 124 | +	struct max22531 *adc;  | 
 | 125 | +	unsigned int prod_id;  | 
 | 126 | +	int ret;  | 
 | 127 | + | 
 | 128 | +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));  | 
 | 129 | +	if (!indio_dev)  | 
 | 130 | +		return -ENOMEM;  | 
 | 131 | + | 
 | 132 | +	adc = iio_priv(indio_dev);  | 
 | 133 | +	adc->spi_dev = spi;  | 
 | 134 | +	adc->chip_info = chip_info;  | 
 | 135 | + | 
 | 136 | +	indio_dev->name = adc->chip_info->name;  | 
 | 137 | +	indio_dev->info = &max22531_info;  | 
 | 138 | +	indio_dev->channels = max22531_channels;  | 
 | 139 | +	indio_dev->num_channels = ARRAY_SIZE(max22531_channels);  | 
 | 140 | + | 
 | 141 | +	ret = devm_regulator_get_enable(&spi->dev, "vddl");  | 
 | 142 | +	if (ret)  | 
 | 143 | +		return dev_err_probe(&spi->dev, ret,  | 
 | 144 | +		       "Failed to retrieve power logic supply.\n");  | 
 | 145 | + | 
 | 146 | +	ret = devm_regulator_get_enable(&spi->dev, "vddpl");  | 
 | 147 | +	if (ret)  | 
 | 148 | +		return dev_err_probe(&spi->dev, ret,  | 
 | 149 | +		       "Failed to retrieve isolated DC-DC supply.\n");  | 
 | 150 | + | 
 | 151 | +	ret = max22531_reg_read(adc, MAX22531_REG_PROD_ID, &prod_id);  | 
 | 152 | +	if (ret ||  | 
 | 153 | +	    FIELD_GET(MAX22531_DEVICE_REV_MSK, prod_id) != MAX22531_DEVICE_REV)  | 
 | 154 | +		dev_warn(&spi->dev, "PROD_ID verification failed\n");  | 
 | 155 | + | 
 | 156 | +	return devm_iio_device_register(&spi->dev, indio_dev);  | 
 | 157 | +}  | 
 | 158 | + | 
 | 159 | +static const struct spi_device_id max22531_id[] = {  | 
 | 160 | +	{ "max22530", (kernel_ulong_t)&max22531_chip_info_tbl[max22530] },  | 
 | 161 | +	{ "max22531", (kernel_ulong_t)&max22531_chip_info_tbl[max22531] },  | 
 | 162 | +	{ "max22532", (kernel_ulong_t)&max22531_chip_info_tbl[max22532] },  | 
 | 163 | +	{ }  | 
 | 164 | +};  | 
 | 165 | +MODULE_DEVICE_TABLE(spi, max22531_id);  | 
 | 166 | + | 
 | 167 | +static const struct of_device_id max22531_spi_of_id[] = {  | 
 | 168 | +	{ .compatible = "adi,max22530",  | 
 | 169 | +		.data = &max22531_chip_info_tbl[max22530], },  | 
 | 170 | +	{ .compatible = "adi,max22531",  | 
 | 171 | +		.data = &max22531_chip_info_tbl[max22531], },  | 
 | 172 | +	{ .compatible = "adi,max22532",  | 
 | 173 | +		.data = &max22531_chip_info_tbl[max22532], },  | 
 | 174 | +	{ }  | 
 | 175 | +};  | 
 | 176 | +MODULE_DEVICE_TABLE(of, max22531_spi_of_id);  | 
 | 177 | + | 
 | 178 | +static struct spi_driver max22531_driver = {  | 
 | 179 | +	.driver = {  | 
 | 180 | +		.name = "max22531",  | 
 | 181 | +		.of_match_table = max22531_spi_of_id,  | 
 | 182 | +	},  | 
 | 183 | +	.probe		= max22531_probe,  | 
 | 184 | +	.id_table	= max22531_id,  | 
 | 185 | +};  | 
 | 186 | +module_spi_driver(max22531_driver);  | 
 | 187 | + | 
 | 188 | +MODULE_AUTHOR("Abhinav Jain <jain.abhinav177@gmail.com>");  | 
 | 189 | +MODULE_DESCRIPTION("MAX22531 ADC");  | 
 | 190 | +MODULE_LICENSE("GPL");  | 
0 commit comments