linux/drivers/iio/adc/mcp3564.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * IIO driver for MCP356X/MCP356XR and MCP346X/MCP346XR series ADC chip family
 *
 * Copyright (C) 2022-2023 Microchip Technology Inc. and its subsidiaries
 *
 * Author: Marius Cristea <[email protected]>
 *
 * Datasheet for MCP3561, MCP3562, MCP3564 can be found here:
 * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP3561-2-4-Family-Data-Sheet-DS20006181C.pdf
 * Datasheet for MCP3561R, MCP3562R, MCP3564R can be found here:
 * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3561_2_4R-Data-Sheet-DS200006391C.pdf
 * Datasheet for MCP3461, MCP3462, MCP3464 can be found here:
 * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4-Two-Four-Eight-Channel-153.6-ksps-Low-Noise-16-Bit-Delta-Sigma-ADC-Data-Sheet-20006180D.pdf
 * Datasheet for MCP3461R, MCP3462R, MCP3464R can be found here:
 * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4R-Family-Data-Sheet-DS20006404C.pdf
 */

#include <linux/bitfield.h>
#include <linux/iopoll.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <linux/util_macros.h>

#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>

#define MCP3564_ADCDATA_REG		0x00

#define MCP3564_CONFIG0_REG		0x01
#define MCP3564_CONFIG0_ADC_MODE_MASK		GENMASK(1, 0)
/* Current Source/Sink Selection Bits for Sensor Bias */
#define MCP3564_CONFIG0_CS_SEL_MASK		GENMASK(3, 2)
/* Internal clock is selected and AMCLK is present on the analog master clock output pin */
#define MCP3564_CONFIG0_USE_INT_CLK_OUTPUT_EN	0x03
/* Internal clock is selected and no clock output is present on the CLK pin */
#define MCP3564_CONFIG0_USE_INT_CLK		0x02
/* External digital clock */
#define MCP3564_CONFIG0_USE_EXT_CLK		0x01
/* External digital clock (default) */
#define MCP3564_CONFIG0_USE_EXT_CLK_DEFAULT	0x00
#define MCP3564_CONFIG0_CLK_SEL_MASK		GENMASK(5, 4)
#define MCP3456_CONFIG0_BIT6_DEFAULT		BIT(6)
#define MCP3456_CONFIG0_VREF_MASK		BIT(7)

#define MCP3564_CONFIG1_REG		0x02
#define MCP3564_CONFIG1_OVERSPL_RATIO_MASK	GENMASK(5, 2)

#define MCP3564_CONFIG2_REG		0x03
#define MCP3564_CONFIG2_AZ_REF_MASK		BIT(1)
#define MCP3564_CONFIG2_AZ_MUX_MASK		BIT(2)

#define MCP3564_CONFIG2_HARDWARE_GAIN_MASK	GENMASK(5, 3)
#define MCP3564_DEFAULT_HARDWARE_GAIN		0x01
#define MCP3564_CONFIG2_BOOST_CURRENT_MASK	GENMASK(7, 6)

#define MCP3564_CONFIG3_REG		0x04
#define MCP3464_CONFIG3_EN_GAINCAL_MASK		BIT(0)
#define MCP3464_CONFIG3_EN_OFFCAL_MASK		BIT(1)
#define MCP3464_CONFIG3_EN_CRCCOM_MASK		BIT(2)
#define MCP3464_CONFIG3_CRC_FORMAT_MASK		BIT(3)
/*
 * ADC Output Data Format 32-bit (25-bit right justified data + Channel ID):
 *                CHID[3:0] + SGN extension (4 bits) + 24-bit ADC data.
 *        It allows overrange with the SGN extension.
 */
#define MCP3464_CONFIG3_DATA_FMT_32B_WITH_CH_ID		3
/*
 * ADC Output Data Format 32-bit (25-bit right justified data):
 *                SGN extension (8-bit) + 24-bit ADC data.
 *        It allows overrange with the SGN extension.
 */
#define MCP3464_CONFIG3_DATA_FMT_32B_SGN_EXT		2
/*
 * ADC Output Data Format 32-bit (24-bit left justified data):
 *                24-bit ADC data + 0x00 (8-bit).
 *        It does not allow overrange (ADC code locked to 0xFFFFFF or 0x800000).
 */
#define MCP3464_CONFIG3_DATA_FMT_32B_LEFT_JUSTIFIED	1
/*
 * ADC Output Data Format 24-bit (default ADC coding):
 *                24-bit ADC data.
 *        It does not allow overrange (ADC code locked to 0xFFFFFF or 0x800000).
 */
#define MCP3464_CONFIG3_DATA_FMT_24B			0
#define MCP3464_CONFIG3_DATA_FORMAT_MASK	GENMASK(5, 4)

/* Continuous Conversion mode or continuous conversion cycle in SCAN mode. */
#define MCP3464_CONFIG3_CONV_MODE_CONTINUOUS		3
/*
 * One-shot conversion or one-shot cycle in SCAN mode. It sets ADC_MODE[1:0] to ‘10’
 * (standby) at the end of the conversion or at the end of the conversion cycle in SCAN mode.
 */
#define MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY	2
/*
 * One-shot conversion or one-shot cycle in SCAN mode. It sets ADC_MODE[1:0] to ‘0x’ (ADC
 * Shutdown) at the end of the conversion or at the end of the conversion cycle in SCAN
 * mode (default).
 */
#define MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_SHUTDOWN	0
#define MCP3464_CONFIG3_CONV_MODE_MASK		GENMASK(7, 6)

#define MCP3564_IRQ_REG			0x05
#define MCP3464_EN_STP_MASK			BIT(0)
#define MCP3464_EN_FASTCMD_MASK			BIT(1)
#define MCP3464_IRQ_MODE_0_MASK			BIT(2)
#define MCP3464_IRQ_MODE_1_MASK			BIT(3)
#define MCP3564_POR_STATUS_MASK			BIT(4)
#define MCP3564_CRCCFG_STATUS_MASK		BIT(5)
#define MCP3564_DATA_READY_MASK			BIT(6)

#define MCP3564_MUX_REG			0x06
#define MCP3564_MUX_VIN_P_MASK			GENMASK(7, 4)
#define MCP3564_MUX_VIN_N_MASK			GENMASK(3, 0)
#define MCP3564_MUX_SET(x, y)			(FIELD_PREP(MCP3564_MUX_VIN_P_MASK, (x)) |	\
						FIELD_PREP(MCP3564_MUX_VIN_N_MASK, (y)))

#define MCP3564_SCAN_REG		0x07
#define MCP3564_SCAN_CH_SEL_MASK		GENMASK(15, 0)
#define MCP3564_SCAN_CH_SEL_SET(x)		FIELD_PREP(MCP3564_SCAN_CH_SEL_MASK, (x))
#define MCP3564_SCAN_DELAY_TIME_MASK		GENMASK(23, 21)
#define MCP3564_SCAN_DELAY_TIME_SET(x)		FIELD_PREP(MCP3564_SCAN_DELAY_TIME_MASK, (x))
#define MCP3564_SCAN_DEFAULT_VALUE		0

#define MCP3564_TIMER_REG		0x08
#define MCP3564_TIMER_DEFAULT_VALUE		0

#define MCP3564_OFFSETCAL_REG		0x09
#define MCP3564_DEFAULT_OFFSETCAL		0

#define MCP3564_GAINCAL_REG		0x0A
#define MCP3564_DEFAULT_GAINCAL			0x00800000

#define MCP3564_RESERVED_B_REG		0x0B

#define MCP3564_RESERVED_C_REG		0x0C
#define MCP3564_C_REG_DEFAULT			0x50
#define MCP3564R_C_REG_DEFAULT			0x30

#define MCP3564_LOCK_REG		0x0D
#define MCP3564_LOCK_WRITE_ACCESS_PASSWORD	0xA5
#define MCP3564_RESERVED_E_REG		0x0E
#define MCP3564_CRCCFG_REG		0x0F

#define MCP3564_CMD_HW_ADDR_MASK	GENMASK(7, 6)
#define MCP3564_CMD_ADDR_MASK		GENMASK(5, 2)

#define MCP3564_HW_ADDR_MASK		GENMASK(1, 0)

#define MCP3564_FASTCMD_START	0x0A
#define MCP3564_FASTCMD_RESET	0x0E

#define MCP3461_HW_ID		0x0008
#define MCP3462_HW_ID		0x0009
#define MCP3464_HW_ID		0x000B

#define MCP3561_HW_ID		0x000C
#define MCP3562_HW_ID		0x000D
#define MCP3564_HW_ID		0x000F
#define MCP3564_HW_ID_MASK	GENMASK(3, 0)

#define MCP3564R_INT_VREF_MV	2400

#define MCP3564_DATA_READY_TIMEOUT_MS	2000

#define MCP3564_MAX_PGA				8
#define MCP3564_MAX_BURNOUT_IDX			4
#define MCP3564_MAX_CHANNELS			66

enum mcp3564_ids {
	mcp3461,
	mcp3462,
	mcp3464,
	mcp3561,
	mcp3562,
	mcp3564,
	mcp3461r,
	mcp3462r,
	mcp3464r,
	mcp3561r,
	mcp3562r,
	mcp3564r,
};

enum mcp3564_delay_time {
	MCP3564_NO_DELAY,
	MCP3564_DELAY_8_DMCLK,
	MCP3564_DELAY_16_DMCLK,
	MCP3564_DELAY_32_DMCLK,
	MCP3564_DELAY_64_DMCLK,
	MCP3564_DELAY_128_DMCLK,
	MCP3564_DELAY_256_DMCLK,
	MCP3564_DELAY_512_DMCLK
};

enum mcp3564_adc_conversion_mode {
	MCP3564_ADC_MODE_DEFAULT,
	MCP3564_ADC_MODE_SHUTDOWN,
	MCP3564_ADC_MODE_STANDBY,
	MCP3564_ADC_MODE_CONVERSION
};

enum mcp3564_adc_bias_current {
	MCP3564_BOOST_CURRENT_x0_50,
	MCP3564_BOOST_CURRENT_x0_66,
	MCP3564_BOOST_CURRENT_x1_00,
	MCP3564_BOOST_CURRENT_x2_00
};

enum mcp3564_burnout {
	MCP3564_CONFIG0_CS_SEL_0_0_uA,
	MCP3564_CONFIG0_CS_SEL_0_9_uA,
	MCP3564_CONFIG0_CS_SEL_3_7_uA,
	MCP3564_CONFIG0_CS_SEL_15_uA
};

enum mcp3564_channel_names {
	MCP3564_CH0,
	MCP3564_CH1,
	MCP3564_CH2,
	MCP3564_CH3,
	MCP3564_CH4,
	MCP3564_CH5,
	MCP3564_CH6,
	MCP3564_CH7,
	MCP3564_AGND,
	MCP3564_AVDD,
	MCP3564_RESERVED, /* do not use */
	MCP3564_REFIN_POZ,
	MCP3564_REFIN_NEG,
	MCP3564_TEMP_DIODE_P,
	MCP3564_TEMP_DIODE_M,
	MCP3564_INTERNAL_VCM,
};

enum mcp3564_oversampling {
	MCP3564_OVERSAMPLING_RATIO_32,
	MCP3564_OVERSAMPLING_RATIO_64,
	MCP3564_OVERSAMPLING_RATIO_128,
	MCP3564_OVERSAMPLING_RATIO_256,
	MCP3564_OVERSAMPLING_RATIO_512,
	MCP3564_OVERSAMPLING_RATIO_1024,
	MCP3564_OVERSAMPLING_RATIO_2048,
	MCP3564_OVERSAMPLING_RATIO_4096,
	MCP3564_OVERSAMPLING_RATIO_8192,
	MCP3564_OVERSAMPLING_RATIO_16384,
	MCP3564_OVERSAMPLING_RATIO_20480,
	MCP3564_OVERSAMPLING_RATIO_24576,
	MCP3564_OVERSAMPLING_RATIO_40960,
	MCP3564_OVERSAMPLING_RATIO_49152,
	MCP3564_OVERSAMPLING_RATIO_81920,
	MCP3564_OVERSAMPLING_RATIO_98304
};

static const unsigned int mcp3564_oversampling_avail[] = {
	[MCP3564_OVERSAMPLING_RATIO_32] = 32,
	[MCP3564_OVERSAMPLING_RATIO_64] = 64,
	[MCP3564_OVERSAMPLING_RATIO_128] = 128,
	[MCP3564_OVERSAMPLING_RATIO_256] = 256,
	[MCP3564_OVERSAMPLING_RATIO_512] = 512,
	[MCP3564_OVERSAMPLING_RATIO_1024] = 1024,
	[MCP3564_OVERSAMPLING_RATIO_2048] = 2048,
	[MCP3564_OVERSAMPLING_RATIO_4096] = 4096,
	[MCP3564_OVERSAMPLING_RATIO_8192] = 8192,
	[MCP3564_OVERSAMPLING_RATIO_16384] = 16384,
	[MCP3564_OVERSAMPLING_RATIO_20480] = 20480,
	[MCP3564_OVERSAMPLING_RATIO_24576] = 24576,
	[MCP3564_OVERSAMPLING_RATIO_40960] = 40960,
	[MCP3564_OVERSAMPLING_RATIO_49152] = 49152,
	[MCP3564_OVERSAMPLING_RATIO_81920] = 81920,
	[MCP3564_OVERSAMPLING_RATIO_98304] = 98304
};

/*
 * Current Source/Sink Selection Bits for Sensor Bias (source on VIN+/sink on VIN-)
 */
static const int mcp3564_burnout_avail[][2] = {
	[MCP3564_CONFIG0_CS_SEL_0_0_uA] = { 0, 0 },
	[MCP3564_CONFIG0_CS_SEL_0_9_uA] = { 0, 900 },
	[MCP3564_CONFIG0_CS_SEL_3_7_uA] = { 0, 3700 },
	[MCP3564_CONFIG0_CS_SEL_15_uA] = { 0, 15000 }
};

/*
 * BOOST[1:0]: ADC Bias Current Selection
 */
static const char * const mcp3564_boost_current_avail[] = {
	[MCP3564_BOOST_CURRENT_x0_50] = "0.5",
	[MCP3564_BOOST_CURRENT_x0_66] = "0.66",
	[MCP3564_BOOST_CURRENT_x1_00] = "1",
	[MCP3564_BOOST_CURRENT_x2_00] = "2",
};

/*
 * Calibration bias values
 */
static const int mcp3564_calib_bias[] = {
	-8388608,	/* min: -2^23		*/
	 1,		/* step: 1		*/
	 8388607	/* max:  2^23 - 1	*/
};

/*
 * Calibration scale values
 * The Gain Error Calibration register (GAINCAL) is an
 * unsigned 24-bit register that holds the digital gain error
 * calibration value, GAINCAL which could be calculated by
 * GAINCAL (V/V) = (GAINCAL[23:0])/8388608
 * The gain error calibration value range in equivalent voltage is [0; 2-2^(-23)]
 */
static const unsigned int mcp3564_calib_scale[] = {
	0,		/* min:  0		*/
	1,		/* step: 1/8388608	*/
	16777215	/* max:  2 - 2^(-23)	*/
};

/* Programmable hardware gain x1/3, x1, x2, x4, x8, x16, x32, x64 */
static const int mcp3564_hwgain_frac[] = {
	3, 10,
	1, 1,
	2, 1,
	4, 1,
	8, 1,
	16, 1,
	32, 1,
	64, 1
};

static const char *mcp3564_channel_labels[2] = {
	"burnout_current", "temperature",
};

/**
 * struct mcp3564_chip_info - chip specific data
 * @name:		device name
 * @num_channels:	number of channels
 * @resolution:		ADC resolution
 * @have_vref:		does the hardware have an internal voltage reference?
 */
struct mcp3564_chip_info {
	const char	*name;
	unsigned int	num_channels;
	unsigned int	resolution;
	bool		have_vref;
};

/**
 * struct mcp3564_state - working data for a ADC device
 * @chip_info:		chip specific data
 * @spi:		SPI device structure
 * @vref_mv:		voltage reference value in miliVolts
 * @lock:		synchronize access to driver's state members
 * @dev_addr:		hardware device address
 * @oversampling:	the index inside oversampling list of the ADC
 * @hwgain:		the index inside hardware gain list of the ADC
 * @scale_tbls:		table with precalculated scale
 * @calib_bias:		calibration bias value
 * @calib_scale:	calibration scale value
 * @current_boost_mode:	the index inside current boost list of the ADC
 * @burnout_mode:	the index inside current bias list of the ADC
 * @auto_zeroing_mux:	set if ADC auto-zeroing algorithm is enabled
 * @auto_zeroing_ref:	set if ADC auto-Zeroing Reference Buffer Setting is enabled
 * @have_vref:		does the ADC have an internal voltage reference?
 * @labels:		table with channels labels
 */
struct mcp3564_state {
	const struct mcp3564_chip_info	*chip_info;
	struct spi_device		*spi;
	unsigned short			vref_mv;
	struct mutex			lock; /* Synchronize access to driver's state members */
	u8				dev_addr;
	enum mcp3564_oversampling	oversampling;
	unsigned int			hwgain;
	unsigned int			scale_tbls[MCP3564_MAX_PGA][2];
	int				calib_bias;
	int				calib_scale;
	unsigned int			current_boost_mode;
	enum mcp3564_burnout		burnout_mode;
	bool				auto_zeroing_mux;
	bool				auto_zeroing_ref;
	bool				have_vref;
	const char			*labels[MCP3564_MAX_CHANNELS];
};

static inline u8 mcp3564_cmd_write(u8 chip_addr, u8 reg)
{
	return FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, chip_addr) |
	       FIELD_PREP(MCP3564_CMD_ADDR_MASK, reg) |
	       BIT(1);
}

static inline u8 mcp3564_cmd_read(u8 chip_addr, u8 reg)
{
	return FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, chip_addr) |
	       FIELD_PREP(MCP3564_CMD_ADDR_MASK, reg) |
	       BIT(0);
}

static int mcp3564_read_8bits(struct mcp3564_state *adc, u8 reg, u8 *val)
{
	int ret;
	u8 tx_buf;
	u8 rx_buf;

	tx_buf = mcp3564_cmd_read(adc->dev_addr, reg);

	ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf),
				  &rx_buf, sizeof(rx_buf));
	*val = rx_buf;

	return ret;
}

static int mcp3564_read_16bits(struct mcp3564_state *adc, u8 reg, u16 *val)
{
	int ret;
	u8 tx_buf;
	__be16 rx_buf;

	tx_buf = mcp3564_cmd_read(adc->dev_addr, reg);

	ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf),
				  &rx_buf, sizeof(rx_buf));
	*val = be16_to_cpu(rx_buf);

	return ret;
}

static int mcp3564_read_32bits(struct mcp3564_state *adc, u8 reg, u32 *val)
{
	int ret;
	u8 tx_buf;
	__be32 rx_buf;

	tx_buf = mcp3564_cmd_read(adc->dev_addr, reg);

	ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf),
				  &rx_buf, sizeof(rx_buf));
	*val = be32_to_cpu(rx_buf);

	return ret;
}

static int mcp3564_write_8bits(struct mcp3564_state *adc, u8 reg, u8 val)
{
	u8 tx_buf[2];

	tx_buf[0] = mcp3564_cmd_write(adc->dev_addr, reg);
	tx_buf[1] = val;

	return spi_write_then_read(adc->spi, tx_buf, sizeof(tx_buf), NULL, 0);
}

static int mcp3564_write_24bits(struct mcp3564_state *adc, u8 reg, u32 val)
{
	__be32 val_be;

	val |= (mcp3564_cmd_write(adc->dev_addr, reg) << 24);
	val_be = cpu_to_be32(val);

	return spi_write_then_read(adc->spi, &val_be, sizeof(val_be), NULL, 0);
}

static int mcp3564_fast_cmd(struct mcp3564_state *adc, u8 fast_cmd)
{
	u8 val;

	val = FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, adc->dev_addr) |
			 FIELD_PREP(MCP3564_CMD_ADDR_MASK, fast_cmd);

	return spi_write_then_read(adc->spi, &val, 1, NULL, 0);
}

static int mcp3564_update_8bits(struct mcp3564_state *adc, u8 reg, u32 mask, u8 val)
{
	u8 tmp;
	int ret;

	val &= mask;

	ret = mcp3564_read_8bits(adc, reg, &tmp);
	if (ret < 0)
		return ret;

	tmp &= ~mask;
	tmp |= val;

	return mcp3564_write_8bits(adc, reg, tmp);
}

static int mcp3564_set_current_boost_mode(struct iio_dev *indio_dev,
					  const struct iio_chan_spec *chan,
					  unsigned int mode)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);
	int ret;

	dev_dbg(&indio_dev->dev, "%s: %d\n", __func__, mode);

	mutex_lock(&adc->lock);
	ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_BOOST_CURRENT_MASK,
				   FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, mode));

	if (ret)
		dev_err(&indio_dev->dev, "Failed to configure CONFIG2 register\n");
	else
		adc->current_boost_mode = mode;

	mutex_unlock(&adc->lock);

	return ret;
}

static int mcp3564_get_current_boost_mode(struct iio_dev *indio_dev,
					  const struct iio_chan_spec *chan)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);

	return adc->current_boost_mode;
}

static const struct iio_enum mcp3564_current_boost_mode_enum = {
	.items = mcp3564_boost_current_avail,
	.num_items = ARRAY_SIZE(mcp3564_boost_current_avail),
	.set = mcp3564_set_current_boost_mode,
	.get = mcp3564_get_current_boost_mode,
};

static const struct iio_chan_spec_ext_info mcp3564_ext_info[] = {
	IIO_ENUM("boost_current_gain", IIO_SHARED_BY_ALL, &mcp3564_current_boost_mode_enum),
	{
		.name = "boost_current_gain_available",
		.shared = IIO_SHARED_BY_ALL,
		.read = iio_enum_available_read,
		.private = (uintptr_t)&mcp3564_current_boost_mode_enum,
	},
	{ }
};

static ssize_t mcp3564_auto_zeroing_mux_show(struct device *dev,
					     struct device_attribute *attr,
					     char *buf)
{
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct mcp3564_state *adc = iio_priv(indio_dev);

	return sysfs_emit(buf, "%d\n", adc->auto_zeroing_mux);
}

static ssize_t mcp3564_auto_zeroing_mux_store(struct device *dev,
					      struct device_attribute *attr,
					      const char *buf, size_t len)
{
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct mcp3564_state *adc = iio_priv(indio_dev);
	bool auto_zero;
	int ret;

	ret = kstrtobool(buf, &auto_zero);
	if (ret)
		return ret;

	mutex_lock(&adc->lock);
	ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_AZ_MUX_MASK,
				   FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, auto_zero));

	if (ret)
		dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n");
	else
		adc->auto_zeroing_mux = auto_zero;

	mutex_unlock(&adc->lock);

	return ret ? ret : len;
}

static ssize_t mcp3564_auto_zeroing_ref_show(struct device *dev,
					     struct device_attribute *attr,
					     char *buf)
{
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct mcp3564_state *adc = iio_priv(indio_dev);

	return sysfs_emit(buf, "%d\n", adc->auto_zeroing_ref);
}

static ssize_t mcp3564_auto_zeroing_ref_store(struct device *dev,
					      struct device_attribute *attr,
					      const char *buf, size_t len)
{
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct mcp3564_state *adc = iio_priv(indio_dev);
	bool auto_zero;
	int ret;

	ret = kstrtobool(buf, &auto_zero);
	if (ret)
		return ret;

	mutex_lock(&adc->lock);
	ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_AZ_REF_MASK,
				   FIELD_PREP(MCP3564_CONFIG2_AZ_REF_MASK, auto_zero));

	if (ret)
		dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n");
	else
		adc->auto_zeroing_ref = auto_zero;

	mutex_unlock(&adc->lock);

	return ret ? ret : len;
}

static const struct iio_chan_spec mcp3564_channel_template = {
	.type = IIO_VOLTAGE,
	.indexed = 1,
	.differential = 1,
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
	.info_mask_shared_by_all  = BIT(IIO_CHAN_INFO_SCALE)		|
				BIT(IIO_CHAN_INFO_CALIBSCALE)		|
				BIT(IIO_CHAN_INFO_CALIBBIAS)		|
				BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE)	|
				BIT(IIO_CHAN_INFO_CALIBSCALE) |
				BIT(IIO_CHAN_INFO_CALIBBIAS)		|
				BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
	.ext_info = mcp3564_ext_info,
};

static const struct iio_chan_spec mcp3564_temp_channel_template = {
	.type = IIO_TEMP,
	.channel = 0,
	.address = ((MCP3564_TEMP_DIODE_P << 4) | MCP3564_TEMP_DIODE_M),
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
	.info_mask_shared_by_all  = BIT(IIO_CHAN_INFO_SCALE)		|
			BIT(IIO_CHAN_INFO_CALIBSCALE)			|
			BIT(IIO_CHAN_INFO_CALIBBIAS)			|
			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE)	|
			BIT(IIO_CHAN_INFO_CALIBSCALE) |
			BIT(IIO_CHAN_INFO_CALIBBIAS)			|
			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
};

static const struct iio_chan_spec mcp3564_burnout_channel_template = {
	.type = IIO_CURRENT,
	.output = true,
	.channel = 0,
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
	.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
};

/*
 * Number of channels could be calculated:
 * num_channels = single_ended_input + differential_input + temperature + burnout
 * Eg. for MCP3561 (only 2 channels available: CH0 and CH1)
 * single_ended_input = (CH0 - GND), (CH1 -  GND) = 2
 * differential_input = (CH0 - CH1), (CH0 -  CH0) = 2
 * num_channels = 2 + 2 + 2
 * Generic formula is:
 * num_channels = P^R(Number_of_single_ended_channels, 2) + 2 (temperature + burnout channels)
 * P^R(Number_of_single_ended_channels, 2) is Permutations with Replacement of
 *     Number_of_single_ended_channels taken by 2
 */
static const struct mcp3564_chip_info mcp3564_chip_infos_tbl[] = {
	[mcp3461] = {
		.name = "mcp3461",
		.num_channels = 6,
		.resolution = 16,
		.have_vref = false,
	},
	[mcp3462] = {
		.name = "mcp3462",
		.num_channels = 18,
		.resolution = 16,
		.have_vref = false,
	},
	[mcp3464] = {
		.name = "mcp3464",
		.num_channels = 66,
		.resolution = 16,
		.have_vref = false,
	},
	[mcp3561] = {
		.name = "mcp3561",
		.num_channels = 6,
		.resolution = 24,
		.have_vref = false,
	},
	[mcp3562] = {
		.name = "mcp3562",
		.num_channels = 18,
		.resolution = 24,
		.have_vref = false,
	},
	[mcp3564] = {
		.name = "mcp3564",
		.num_channels = 66,
		.resolution = 24,
		.have_vref = false,
	},
	[mcp3461r] = {
		.name = "mcp3461r",
		.num_channels = 6,
		.resolution = 16,
		.have_vref = false,
	},
	[mcp3462r] = {
		.name = "mcp3462r",
		.num_channels = 18,
		.resolution = 16,
		.have_vref = true,
	},
	[mcp3464r] = {
		.name = "mcp3464r",
		.num_channels = 66,
		.resolution = 16,
		.have_vref = true,
	},
	[mcp3561r] = {
		.name = "mcp3561r",
		.num_channels = 6,
		.resolution = 24,
		.have_vref = true,
	},
	[mcp3562r] = {
		.name = "mcp3562r",
		.num_channels = 18,
		.resolution = 24,
		.have_vref = true,
	},
	[mcp3564r] = {
		.name = "mcp3564r",
		.num_channels = 66,
		.resolution = 24,
		.have_vref = true,
	},
};

static int mcp3564_read_single_value(struct iio_dev *indio_dev,
				     struct iio_chan_spec const *channel,
				     int *val)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);
	int ret;
	u8 tmp;
	int ret_read = 0;

	ret = mcp3564_write_8bits(adc, MCP3564_MUX_REG, channel->address);
	if (ret)
		return ret;

	/* Start ADC Conversion using fast command (overwrites ADC_MODE[1:0] = 11) */
	ret = mcp3564_fast_cmd(adc, MCP3564_FASTCMD_START);
	if (ret)
		return ret;

	/*
	 * Check if the conversion is ready. If not, wait a little bit, and
	 * in case of timeout exit with an error.
	 */
	ret = read_poll_timeout(mcp3564_read_8bits, ret_read,
				ret_read || !(tmp & MCP3564_DATA_READY_MASK),
				20000, MCP3564_DATA_READY_TIMEOUT_MS * 1000, true,
				adc, MCP3564_IRQ_REG, &tmp);

	/* failed to read status register */
	if (ret_read)
		return ret_read;

	if (ret)
		return ret;

	if (tmp & MCP3564_DATA_READY_MASK)
		/* failing to finish conversion */
		return -EBUSY;

	return mcp3564_read_32bits(adc, MCP3564_ADCDATA_REG, val);
}

static int mcp3564_read_avail(struct iio_dev *indio_dev,
			      struct iio_chan_spec const *channel,
			      const int **vals, int *type,
			      int *length, long mask)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		if (!channel->output)
			return -EINVAL;

		*vals = mcp3564_burnout_avail[0];
		*length = ARRAY_SIZE(mcp3564_burnout_avail) * 2;
		*type = IIO_VAL_INT_PLUS_MICRO;
		return IIO_AVAIL_LIST;
	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
		*vals = mcp3564_oversampling_avail;
		*length = ARRAY_SIZE(mcp3564_oversampling_avail);
		*type = IIO_VAL_INT;
		return IIO_AVAIL_LIST;
	case IIO_CHAN_INFO_SCALE:
		*vals = (int *)adc->scale_tbls;
		*length = ARRAY_SIZE(adc->scale_tbls) * 2;
		*type = IIO_VAL_INT_PLUS_NANO;
		return IIO_AVAIL_LIST;
	case IIO_CHAN_INFO_CALIBBIAS:
		*vals = mcp3564_calib_bias;
		*type = IIO_VAL_INT;
		return IIO_AVAIL_RANGE;
	case IIO_CHAN_INFO_CALIBSCALE:
		*vals = mcp3564_calib_scale;
		*type = IIO_VAL_INT;
		return IIO_AVAIL_RANGE;
	default:
		return -EINVAL;
	}
}

static int mcp3564_read_raw(struct iio_dev *indio_dev,
			    struct iio_chan_spec const *channel,
			    int *val, int *val2, long mask)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);
	int ret;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		if (channel->output) {
			mutex_lock(&adc->lock);
			*val = mcp3564_burnout_avail[adc->burnout_mode][0];
			*val2 = mcp3564_burnout_avail[adc->burnout_mode][1];
			mutex_unlock(&adc->lock);
			return IIO_VAL_INT_PLUS_MICRO;
		}

		ret = mcp3564_read_single_value(indio_dev, channel, val);
		if (ret)
			return -EINVAL;
		return IIO_VAL_INT;
	case IIO_CHAN_INFO_SCALE:
		mutex_lock(&adc->lock);
		*val = adc->scale_tbls[adc->hwgain][0];
		*val2 = adc->scale_tbls[adc->hwgain][1];
		mutex_unlock(&adc->lock);
		return IIO_VAL_INT_PLUS_NANO;
	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
		*val = mcp3564_oversampling_avail[adc->oversampling];
		return IIO_VAL_INT;
	case IIO_CHAN_INFO_CALIBBIAS:
		*val = adc->calib_bias;
		return IIO_VAL_INT;
	case IIO_CHAN_INFO_CALIBSCALE:
		*val = adc->calib_scale;
		return IIO_VAL_INT;
	default:
		return -EINVAL;
	}
}

static int mcp3564_write_raw_get_fmt(struct iio_dev *indio_dev,
				     struct iio_chan_spec const *chan,
				     long info)
{
	switch (info) {
	case IIO_CHAN_INFO_RAW:
		return IIO_VAL_INT_PLUS_MICRO;
	case IIO_CHAN_INFO_CALIBBIAS:
	case IIO_CHAN_INFO_CALIBSCALE:
	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
		return IIO_VAL_INT;
	case IIO_CHAN_INFO_SCALE:
		return IIO_VAL_INT_PLUS_NANO;
	default:
		return -EINVAL;
	}
}

static int mcp3564_write_raw(struct iio_dev *indio_dev,
			     struct iio_chan_spec const *channel, int val,
			     int val2, long mask)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);
	int tmp;
	unsigned int hwgain;
	enum mcp3564_burnout burnout;
	int ret = 0;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		if (!channel->output)
			return -EINVAL;

		for (burnout = 0; burnout < MCP3564_MAX_BURNOUT_IDX; burnout++)
			if (val == mcp3564_burnout_avail[burnout][0] &&
			    val2 == mcp3564_burnout_avail[burnout][1])
				break;

		if (burnout == MCP3564_MAX_BURNOUT_IDX)
			return -EINVAL;

		if (burnout == adc->burnout_mode)
			return ret;

		mutex_lock(&adc->lock);
		ret = mcp3564_update_8bits(adc, MCP3564_CONFIG0_REG,
					   MCP3564_CONFIG0_CS_SEL_MASK,
					   FIELD_PREP(MCP3564_CONFIG0_CS_SEL_MASK, burnout));

		if (ret)
			dev_err(&indio_dev->dev, "Failed to configure burnout current\n");
		else
			adc->burnout_mode = burnout;
		mutex_unlock(&adc->lock);
		return ret;
	case IIO_CHAN_INFO_CALIBBIAS:
		if (val < mcp3564_calib_bias[0] || val > mcp3564_calib_bias[2])
			return -EINVAL;

		mutex_lock(&adc->lock);
		ret = mcp3564_write_24bits(adc, MCP3564_OFFSETCAL_REG, val);
		if (!ret)
			adc->calib_bias = val;
		mutex_unlock(&adc->lock);
		return ret;
	case IIO_CHAN_INFO_CALIBSCALE:
		if (val < mcp3564_calib_scale[0] || val > mcp3564_calib_scale[2])
			return -EINVAL;

		if (adc->calib_scale == val)
			return ret;

		mutex_lock(&adc->lock);
		ret = mcp3564_write_24bits(adc, MCP3564_GAINCAL_REG, val);
		if (!ret)
			adc->calib_scale = val;
		mutex_unlock(&adc->lock);
		return ret;
	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
		if (val < 0)
			return -EINVAL;

		tmp = find_closest(val, mcp3564_oversampling_avail,
				   ARRAY_SIZE(mcp3564_oversampling_avail));

		if (adc->oversampling == tmp)
			return ret;

		mutex_lock(&adc->lock);
		ret = mcp3564_update_8bits(adc, MCP3564_CONFIG1_REG,
					   MCP3564_CONFIG1_OVERSPL_RATIO_MASK,
					   FIELD_PREP(MCP3564_CONFIG1_OVERSPL_RATIO_MASK,
						      adc->oversampling));
		if (!ret)
			adc->oversampling = tmp;
		mutex_unlock(&adc->lock);
		return ret;
	case IIO_CHAN_INFO_SCALE:
		for (hwgain = 0; hwgain < MCP3564_MAX_PGA; hwgain++)
			if (val == adc->scale_tbls[hwgain][0] &&
			    val2 == adc->scale_tbls[hwgain][1])
				break;

		if (hwgain == MCP3564_MAX_PGA)
			return -EINVAL;

		if (hwgain == adc->hwgain)
			return ret;

		mutex_lock(&adc->lock);
		ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG,
					   MCP3564_CONFIG2_HARDWARE_GAIN_MASK,
					   FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, hwgain));
		if (!ret)
			adc->hwgain = hwgain;

		mutex_unlock(&adc->lock);
		return ret;
	default:
		return -EINVAL;
	}
}

static int mcp3564_read_label(struct iio_dev *indio_dev,
			      struct iio_chan_spec const *chan, char *label)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);

	return sprintf(label, "%s\n", adc->labels[chan->scan_index]);
}

static int mcp3564_parse_fw_children(struct iio_dev *indio_dev)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);
	struct device *dev = &adc->spi->dev;
	struct iio_chan_spec *channels;
	struct iio_chan_spec chanspec = mcp3564_channel_template;
	struct iio_chan_spec temp_chanspec = mcp3564_temp_channel_template;
	struct iio_chan_spec burnout_chanspec = mcp3564_burnout_channel_template;
	int chan_idx = 0;
	unsigned int num_ch;
	u32 inputs[2];
	const char *node_name;
	const char *label;
	int ret;

	num_ch = device_get_child_node_count(dev);
	if (num_ch == 0)
		return dev_err_probe(&indio_dev->dev, -ENODEV,
				     "FW has no channels defined\n");

	/* Reserve space for burnout and temperature channel */
	num_ch += 2;

	if (num_ch > adc->chip_info->num_channels)
		return dev_err_probe(dev, -EINVAL, "Too many channels %d > %d\n",
				     num_ch, adc->chip_info->num_channels);

	channels = devm_kcalloc(dev, num_ch, sizeof(*channels), GFP_KERNEL);
	if (!channels)
		return dev_err_probe(dev, -ENOMEM, "Can't allocate memory\n");

	device_for_each_child_node_scoped(dev, child) {
		node_name = fwnode_get_name(child);

		if (fwnode_property_present(child, "diff-channels")) {
			ret = fwnode_property_read_u32_array(child,
							     "diff-channels",
							     inputs,
							     ARRAY_SIZE(inputs));
			if (ret)
				return ret;

			chanspec.differential = 1;
		} else {
			ret = fwnode_property_read_u32(child, "reg", &inputs[0]);
			if (ret)
				return ret;

			chanspec.differential = 0;
			inputs[1] = MCP3564_AGND;
		}

		if (inputs[0] > MCP3564_INTERNAL_VCM ||
		    inputs[1] > MCP3564_INTERNAL_VCM)
			return dev_err_probe(&indio_dev->dev, -EINVAL,
					     "Channel index > %d, for %s\n",
					     MCP3564_INTERNAL_VCM + 1,
					     node_name);

		chanspec.address = (inputs[0] << 4) | inputs[1];
		chanspec.channel = inputs[0];
		chanspec.channel2 = inputs[1];
		chanspec.scan_index = chan_idx;

		if (fwnode_property_present(child, "label")) {
			fwnode_property_read_string(child, "label", &label);
			adc->labels[chan_idx] = label;
		}

		channels[chan_idx] = chanspec;
		chan_idx++;
	}

	/* Add burnout current channel */
	burnout_chanspec.scan_index = chan_idx;
	channels[chan_idx] = burnout_chanspec;
	adc->labels[chan_idx] = mcp3564_channel_labels[0];
	chanspec.scan_index = chan_idx;
	chan_idx++;

	/* Add temperature channel */
	temp_chanspec.scan_index = chan_idx;
	channels[chan_idx] = temp_chanspec;
	adc->labels[chan_idx] = mcp3564_channel_labels[1];
	chan_idx++;

	indio_dev->num_channels = chan_idx;
	indio_dev->channels = channels;

	return 0;
}

static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc)
{
	unsigned int pow = adc->chip_info->resolution - 1;
	int ref;
	unsigned int i;
	int tmp0;
	u64 tmp1;

	for (i = 0; i < MCP3564_MAX_PGA; i++) {
		ref = adc->vref_mv;
		tmp1 = ((u64)ref * NANO) >> pow;
		div_u64_rem(tmp1, NANO, &tmp0);

		tmp1 = tmp1 * mcp3564_hwgain_frac[(2 * i) + 1];
		tmp0 = (int)div_u64(tmp1, mcp3564_hwgain_frac[2 * i]);

		adc->scale_tbls[i][1] = tmp0;
	}
}

static int mcp3564_config(struct iio_dev *indio_dev, bool *use_internal_vref_attr)
{
	struct mcp3564_state *adc = iio_priv(indio_dev);
	struct device *dev = &adc->spi->dev;
	u8 tmp_reg;
	u16 tmp_u16;
	enum mcp3564_ids ids;
	int ret = 0;
	unsigned int tmp = 0x01;
	bool internal_vref;
	bool err = false;

	/*
	 * The address is set on a per-device basis by fuses in the factory,
	 * configured on request. If not requested, the fuses are set for 0x1.
	 * The device address is part of the device markings to avoid
	 * potential confusion. This address is coded on two bits, so four possible
	 * addresses are available when multiple devices are present on the same
	 * SPI bus with only one Chip Select line for all devices.
	 */
	device_property_read_u32(dev, "microchip,hw-device-address", &tmp);

	if (tmp > 3) {
		dev_err_probe(dev, tmp,
			      "invalid device address. Must be in range 0-3.\n");
		return -EINVAL;
	}

	adc->dev_addr = FIELD_GET(MCP3564_HW_ADDR_MASK, tmp);

	dev_dbg(dev, "use HW device address %i\n", adc->dev_addr);

	ret = mcp3564_read_8bits(adc, MCP3564_RESERVED_C_REG, &tmp_reg);
	if (ret < 0)
		return ret;

	switch (tmp_reg) {
	case MCP3564_C_REG_DEFAULT:
		adc->have_vref = false;
		break;
	case MCP3564R_C_REG_DEFAULT:
		adc->have_vref = true;
		break;
	default:
		dev_info(dev, "Unknown chip found: %d\n", tmp_reg);
		err = true;
	}

	if (!err) {
		ret = mcp3564_read_16bits(adc, MCP3564_RESERVED_E_REG, &tmp_u16);
		if (ret < 0)
			return ret;

		switch (tmp_u16 & MCP3564_HW_ID_MASK) {
		case MCP3461_HW_ID:
			if (adc->have_vref)
				ids = mcp3461r;
			else
				ids = mcp3461;
			break;
		case MCP3462_HW_ID:
			if (adc->have_vref)
				ids = mcp3462r;
			else
				ids = mcp3462;
			break;
		case MCP3464_HW_ID:
			if (adc->have_vref)
				ids = mcp3464r;
			else
				ids = mcp3464;
			break;
		case MCP3561_HW_ID:
			if (adc->have_vref)
				ids = mcp3561r;
			else
				ids = mcp3561;
			break;
		case MCP3562_HW_ID:
			if (adc->have_vref)
				ids = mcp3562r;
			else
				ids = mcp3562;
			break;
		case MCP3564_HW_ID:
			if (adc->have_vref)
				ids = mcp3564r;
			else
				ids = mcp3564;
			break;
		default:
			dev_info(dev, "Unknown chip found: %d\n", tmp_u16);
			err = true;
		}
	}

	if (err) {
		/*
		 * If failed to identify the hardware based on internal registers,
		 * try using fallback compatible in device tree to deal with some newer part number.
		 */
		adc->chip_info = spi_get_device_match_data(adc->spi);
		adc->have_vref = adc->chip_info->have_vref;
	} else {
		adc->chip_info = &mcp3564_chip_infos_tbl[ids];
	}

	dev_dbg(dev, "Found %s chip\n", adc->chip_info->name);

	ret = devm_regulator_get_enable_read_voltage(dev, "vref");
	if (ret < 0 && ret != -ENODEV)
		return dev_err_probe(dev, ret, "Failed to get vref voltage\n");

	internal_vref = ret == -ENODEV;
	adc->vref_mv = internal_vref ? MCP3564R_INT_VREF_MV : ret / MILLI;
	*use_internal_vref_attr = internal_vref;

	if (internal_vref) {
		/* Check if chip has internal vref */
		if (!adc->have_vref)
			return dev_err_probe(dev, -ENODEV, "Unknown Vref\n");

		dev_dbg(dev, "%s: Using internal Vref\n", __func__);
	} else {
		dev_dbg(dev, "%s: Using External Vref\n", __func__);
	}

	ret = mcp3564_parse_fw_children(indio_dev);
	if (ret)
		return ret;

	/*
	 * Command sequence that ensures a recovery with the desired settings
	 * in any cases of loss-of-power scenario (Full Chip Reset):
	 *  - Write LOCK register to 0xA5
	 *  - Write IRQ register to 0x03
	 *  - Send "Device Full Reset" fast command
	 *  - Wait 1ms for "Full Reset" to complete
	 */
	ret = mcp3564_write_8bits(adc, MCP3564_LOCK_REG, MCP3564_LOCK_WRITE_ACCESS_PASSWORD);
	if (ret)
		return ret;

	ret = mcp3564_write_8bits(adc, MCP3564_IRQ_REG, 0x03);
	if (ret)
		return ret;

	ret = mcp3564_fast_cmd(adc, MCP3564_FASTCMD_RESET);
	if (ret)
		return ret;

	/*
	 * After Full reset wait some time to be able to fully reset the part and place
	 * it back in a default configuration.
	 * From datasheet: POR (Power On Reset Time) is ~1us
	 * 1ms should be enough.
	 */
	mdelay(1);

	/* set a gain of 1x for GAINCAL */
	ret = mcp3564_write_24bits(adc, MCP3564_GAINCAL_REG, MCP3564_DEFAULT_GAINCAL);
	if (ret)
		return ret;

	adc->calib_scale = MCP3564_DEFAULT_GAINCAL;

	ret = mcp3564_write_24bits(adc, MCP3564_OFFSETCAL_REG, MCP3564_DEFAULT_OFFSETCAL);
	if (ret)
		return ret;

	ret = mcp3564_write_24bits(adc, MCP3564_TIMER_REG, MCP3564_TIMER_DEFAULT_VALUE);
	if (ret)
		return ret;

	ret = mcp3564_write_24bits(adc, MCP3564_SCAN_REG,
				   MCP3564_SCAN_DELAY_TIME_SET(MCP3564_NO_DELAY) |
				   MCP3564_SCAN_CH_SEL_SET(MCP3564_SCAN_DEFAULT_VALUE));
	if (ret)
		return ret;

	ret = mcp3564_write_8bits(adc, MCP3564_MUX_REG, MCP3564_MUX_SET(MCP3564_CH0, MCP3564_CH1));
	if (ret)
		return ret;

	ret = mcp3564_write_8bits(adc, MCP3564_IRQ_REG,
				  FIELD_PREP(MCP3464_EN_FASTCMD_MASK, 1) |
				  FIELD_PREP(MCP3464_EN_STP_MASK, 1));
	if (ret)
		return ret;

	tmp_reg = FIELD_PREP(MCP3464_CONFIG3_CONV_MODE_MASK,
			     MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY);
	tmp_reg |= FIELD_PREP(MCP3464_CONFIG3_DATA_FORMAT_MASK,
			      MCP3464_CONFIG3_DATA_FMT_32B_SGN_EXT);
	tmp_reg |= MCP3464_CONFIG3_EN_OFFCAL_MASK;
	tmp_reg |= MCP3464_CONFIG3_EN_GAINCAL_MASK;

	ret = mcp3564_write_8bits(adc, MCP3564_CONFIG3_REG, tmp_reg);
	if (ret)
		return ret;

	tmp_reg = FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, MCP3564_BOOST_CURRENT_x1_00);
	tmp_reg |= FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, 0x01);
	tmp_reg |= FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, 1);

	ret = mcp3564_write_8bits(adc, MCP3564_CONFIG2_REG, tmp_reg);
	if (ret)
		return ret;

	adc->hwgain = 0x01;
	adc->auto_zeroing_mux = true;
	adc->auto_zeroing_ref = false;
	adc->current_boost_mode = MCP3564_BOOST_CURRENT_x1_00;

	tmp_reg = FIELD_PREP(MCP3564_CONFIG1_OVERSPL_RATIO_MASK, MCP3564_OVERSAMPLING_RATIO_98304);

	ret = mcp3564_write_8bits(adc, MCP3564_CONFIG1_REG, tmp_reg);
	if (ret)
		return ret;

	adc->oversampling = MCP3564_OVERSAMPLING_RATIO_98304;

	tmp_reg = FIELD_PREP(MCP3564_CONFIG0_ADC_MODE_MASK, MCP3564_ADC_MODE_STANDBY);
	tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CS_SEL_MASK, MCP3564_CONFIG0_CS_SEL_0_0_uA);
	tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CLK_SEL_MASK, MCP3564_CONFIG0_USE_INT_CLK);
	tmp_reg |= MCP3456_CONFIG0_BIT6_DEFAULT;

	if (internal_vref)
		tmp_reg |= FIELD_PREP(MCP3456_CONFIG0_VREF_MASK, 1);

	ret = mcp3564_write_8bits(adc, MCP3564_CONFIG0_REG, tmp_reg);

	adc->burnout_mode = MCP3564_CONFIG0_CS_SEL_0_0_uA;

	return ret;
}

static IIO_DEVICE_ATTR(auto_zeroing_ref_enable, 0644,
		       mcp3564_auto_zeroing_ref_show,
		       mcp3564_auto_zeroing_ref_store, 0);

static IIO_DEVICE_ATTR(auto_zeroing_mux_enable, 0644,
		       mcp3564_auto_zeroing_mux_show,
		       mcp3564_auto_zeroing_mux_store, 0);

static struct attribute *mcp3564_attributes[] = {
	&iio_dev_attr_auto_zeroing_mux_enable.dev_attr.attr,
	NULL
};

static struct attribute *mcp3564r_attributes[] = {
	&iio_dev_attr_auto_zeroing_mux_enable.dev_attr.attr,
	&iio_dev_attr_auto_zeroing_ref_enable.dev_attr.attr,
	NULL
};

static struct attribute_group mcp3564_attribute_group = {
	.attrs = mcp3564_attributes,
};

static struct attribute_group mcp3564r_attribute_group = {
	.attrs = mcp3564r_attributes,
};

static const struct iio_info mcp3564_info = {
	.read_raw = mcp3564_read_raw,
	.read_avail = mcp3564_read_avail,
	.write_raw = mcp3564_write_raw,
	.write_raw_get_fmt = mcp3564_write_raw_get_fmt,
	.read_label = mcp3564_read_label,
	.attrs = &mcp3564_attribute_group,
};

static const struct iio_info mcp3564r_info = {
	.read_raw = mcp3564_read_raw,
	.read_avail = mcp3564_read_avail,
	.write_raw = mcp3564_write_raw,
	.write_raw_get_fmt = mcp3564_write_raw_get_fmt,
	.read_label = mcp3564_read_label,
	.attrs = &mcp3564r_attribute_group,
};

static int mcp3564_probe(struct spi_device *spi)
{
	int ret;
	struct iio_dev *indio_dev;
	struct mcp3564_state *adc;
	bool use_internal_vref_attr;

	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
	if (!indio_dev)
		return -ENOMEM;

	adc = iio_priv(indio_dev);
	adc->spi = spi;

	dev_dbg(&spi->dev, "%s: probe(spi = 0x%p)\n", __func__, spi);

	/*
	 * Do any chip specific initialization, e.g:
	 * read/write some registers
	 * enable/disable certain channels
	 * change the sampling rate to the requested value
	 */
	ret = mcp3564_config(indio_dev, &use_internal_vref_attr);
	if (ret)
		return dev_err_probe(&spi->dev, ret,
				     "Can't configure MCP356X device\n");

	dev_dbg(&spi->dev, "%s: Vref (mV): %d\n", __func__, adc->vref_mv);

	mcp3564_fill_scale_tbls(adc);

	indio_dev->name = adc->chip_info->name;
	indio_dev->modes = INDIO_DIRECT_MODE;

	if (use_internal_vref_attr)
		indio_dev->info = &mcp3564r_info;
	else
		indio_dev->info = &mcp3564_info;

	mutex_init(&adc->lock);

	ret = devm_iio_device_register(&spi->dev, indio_dev);
	if (ret)
		return dev_err_probe(&spi->dev, ret,
				     "Can't register IIO device\n");

	return 0;
}

static const struct of_device_id mcp3564_dt_ids[] = {
	{ .compatible = "microchip,mcp3461", .data = &mcp3564_chip_infos_tbl[mcp3461] },
	{ .compatible = "microchip,mcp3462", .data = &mcp3564_chip_infos_tbl[mcp3462] },
	{ .compatible = "microchip,mcp3464", .data = &mcp3564_chip_infos_tbl[mcp3464] },
	{ .compatible = "microchip,mcp3561", .data = &mcp3564_chip_infos_tbl[mcp3561] },
	{ .compatible = "microchip,mcp3562", .data = &mcp3564_chip_infos_tbl[mcp3562] },
	{ .compatible = "microchip,mcp3564", .data = &mcp3564_chip_infos_tbl[mcp3564] },
	{ .compatible = "microchip,mcp3461r", .data = &mcp3564_chip_infos_tbl[mcp3461r] },
	{ .compatible = "microchip,mcp3462r", .data = &mcp3564_chip_infos_tbl[mcp3462r] },
	{ .compatible = "microchip,mcp3464r", .data = &mcp3564_chip_infos_tbl[mcp3464r] },
	{ .compatible = "microchip,mcp3561r", .data = &mcp3564_chip_infos_tbl[mcp3561r] },
	{ .compatible = "microchip,mcp3562r", .data = &mcp3564_chip_infos_tbl[mcp3562r] },
	{ .compatible = "microchip,mcp3564r", .data = &mcp3564_chip_infos_tbl[mcp3564r] },
	{ }
};
MODULE_DEVICE_TABLE(of, mcp3564_dt_ids);

static const struct spi_device_id mcp3564_id[] = {
	{ "mcp3461", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] },
	{ "mcp3462", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] },
	{ "mcp3464", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] },
	{ "mcp3561", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] },
	{ "mcp3562", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] },
	{ "mcp3564", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] },
	{ "mcp3461r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] },
	{ "mcp3462r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] },
	{ "mcp3464r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] },
	{ "mcp3561r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] },
	{ "mcp3562r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] },
	{ "mcp3564r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] },
	{ }
};
MODULE_DEVICE_TABLE(spi, mcp3564_id);

static struct spi_driver mcp3564_driver = {
	.driver = {
		.name = "mcp3564",
		.of_match_table = mcp3564_dt_ids,
	},
	.probe = mcp3564_probe,
	.id_table = mcp3564_id,
};

module_spi_driver(mcp3564_driver);

MODULE_AUTHOR("Marius Cristea <[email protected]>");
MODULE_DESCRIPTION("Microchip MCP346x/MCP346xR and MCP356x/MCP356xR ADCs");
MODULE_LICENSE("GPL v2");