Re: [PATCH v4 2/2] iio: adc: adding support for MCP3564 ADC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 18 Aug 2023 19:57:50 +0300
<marius.cristea@xxxxxxxxxxxxx> wrote:

> From: Marius Cristea <marius.cristea@xxxxxxxxxxxxx>
> 
> This is the iio driver for Microchip family of 153.6 ksps,
> Low-Noise 16/24-Bit Delta-Sigma ADCs with an SPI interface
> (Microchip's  MCP3461, MCP3462, MCP3464, MCP3461R, MCP3462R,
> MCP3464R, MCP3561, MCP3562, MCP3564, MCP3561R,
> MCP3562R and MCP3564R analog to digital converters).
> 
> Signed-off-by: Marius Cristea <marius.cristea@xxxxxxxxxxxxx>

Hi Marius,

A few more things came up when I was reading this version.
See inline.

Thanks,

Jonathan

> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index e07e4a3e6237..91f713af574f 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_MAX1363) += max1363.o
>  obj-$(CONFIG_MAX9611) += max9611.o
>  obj-$(CONFIG_MCP320X) += mcp320x.o
>  obj-$(CONFIG_MCP3422) += mcp3422.o
> +obj-$(CONFIG_MCP3564) += mcp3564.o
>  obj-$(CONFIG_MCP3911) += mcp3911.o
>  obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
>  obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o
> diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c
> new file mode 100644
> index 000000000000..8c5649781093
> --- /dev/null
> +++ b/drivers/iio/adc/mcp3564.c
> @@ -0,0 +1,1527 @@
> +// 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 <marius.cristea@xxxxxxxxxxxxx>
> + *
> + * 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_CONFIG2_REG		0x03
> +#define MCP3564_CONFIG2_AZ_REF_MASK		BIT(1)
> +#define MCP3564_CONFIG2_AZ_REF_SET(x)		FIELD_PREP(MCP3564_CONFIG2_AZ_REF_MASK, (x))
> +#define MCP3564_CONFIG2_AZ_MUX_MASK		BIT(2)
> +#define MCP3564_CONFIG2_AZ_MUX_SET(x)		FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, (x))
> +
> +#define MCP3564_CONFIG2_HARDWARE_GAIN_MASK	GENMASK(5, 3)
> +#define MCP3564_CONFIG2_HARDWARE_GAIN_SET(x)	FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, (x))
I don't find the GAIN_SET() macros to be more readable than FIELD_PREP used directly
as the MASK naming makes it clear what is being set.

So I would drop all these in favour of inline use of FIELD_PREP

> +#define MCP3564_DEFAULT_HARDWARE_GAIN		0x01
> +#define MCP3564_CONFIG2_BOOST_CURRENT_MASK	GENMASK(7, 6)
> +#define MCP3564_CONFIG2_BOOST_CURRENT_SET(x)	FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, (x))
> +

...

> +#define MCP3464_CONFIG3_DATA_FORMAT_MASK	GENMASK(5, 4)
> +#define MCP3564_CONFIG3_DATA_FORMAT_SET(x)	FIELD_PREP(MCP3464_CONFIG3_DATA_FORMAT_MASK, (x))
> +
> +/* 0b11 = Continuous Conversion mode or continuous conversion cycle in SCAN mode. */
> +#define MCP3464_CONFIG3_CONV_MODE_CONTINUOUS		3

Values defined - so need for them in the comment which is otherwise useful.

> +/*
> + * 0b10 = 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
> +/*
> + * 0b0x = 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 MCP3564_CMD_HW_ADDR_MASK	GENMASK(7, 6)
> +#define MCP3564_CMD_HW_ADDR_SET(x)	FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, (x))
> +#define MCP3564_CMD_ADDR_MASK		GENMASK(5, 2)
> +#define MCP3564_CMD_ADDR_SET(x)		FIELD_PREP(MCP3564_CMD_ADDR_MASK, (x))

More cases where I'd like to see the FIELD_PREP() used directly not hidden in a macro.

> +
> +/*
> + * 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}
> +};

Trivial, but I'd like brackets after the { and before the } as makes things a tiny bit
more readable to my eye.

> +/**
> + * struct mcp3564_chip_info - chip specific data
> + * @name:		device name
> + * @num_channels:	number of channels
> + * @resolution:		ADC resolution
> + * @has_vref:		does the hardware has an internal voltage reference?

have

> + */
> +struct mcp3564_chip_info {
> +	const char	*name;
> +	unsigned int	num_channels;
> +	unsigned int	resolution;
> +	bool		has_vref;
> +};
> +

...


> +
> +static int mcp3564_write_8bits(struct mcp3564_state *adc, u8 reg, u8 val)
> +{
> +	u8 tmp;
> +	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), &tmp, 0);
as below.


> +}
> +
> +static int mcp3564_write_24bits(struct mcp3564_state *adc, u8 reg, u32 val)
> +{
> +	u8 tmp;
> +	__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), &tmp, 0);

as below.

> +}
> +
> +static int mcp3564_fast_cmd(struct mcp3564_state *adc, u8 fast_cmd)
> +{
> +	u8 val;
> +	u8 tmp;
> +
> +	val = MCP3564_CMD_HW_ADDR_SET(adc->dev_addr) | MCP3564_CMD_ADDR_SET(fast_cmd);
> +
> +	return spi_write_then_read(adc->spi, &val, 1, &tmp, 0);

No need for tmp. I'm fairly sure spi_write_then_read is fine with NULL buffers.

> +}
> +
> +static int mcp3564_update_8bits(struct mcp3564_state *adc, u8 reg, u32 mask, u8 val)
> +{
> +	u8 tmp;
> +	int ret;
> +
> +	ret = mcp3564_read_8bits(adc, reg, &tmp);
> +	if (ret < 0)
> +		return ret;
> +
> +	val &= mask;
This looks wrong - would expect this to be
        val &= ~mask; // wipe out the bits in mask.
> +	val |= tmp & ~mask;
and
	val |= tmp & mask;  //write the bitss in mask.

Is the mask the inverse ?  At first glance it doesn't seem to be.

> +	ret = mcp3564_write_8bits(adc, reg, val);
> +
> +	return ret;

return mcp...

> +}


...



> +
> +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,
> +					   MCP3564_CONFIG0_CS_SEL_SET(burnout));
As above - I'd find this clearer with FIELD_PREP here
					   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,
> +					   MCP3564_CONFIG1_OVERSPL_RATIO_SET(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);
> +		/* Update GAIN in CONFIG2[5:3] -> GAIN[2:0]*/

I'd drop this comment as this is clear from teh code that follows and comments
rot over time ;)


> +		ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG,
> +					   MCP3564_CONFIG2_HARDWARE_GAIN_MASK,
> +					   MCP3564_CONFIG2_HARDWARE_GAIN_SET(hwgain));
As above. 
> +		if (!ret)
> +			adc->hwgain = hwgain;
> +
> +		mutex_unlock(&adc->lock);
> +		return ret;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +

...

> +
> +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 fwnode_handle *child;
> +	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);

As below. My interpretation kernel style tells me that this doesn't need brackets.

> +	}
> +
> +	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(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));
> +			chanspec.differential = 1;
> +		} else {
> +			ret = fwnode_property_read_u32(child, "reg", &inputs[0]);
> +
> +			chanspec.differential = 0;
> +			inputs[1] = MCP3564_AGND;
> +		}
> +		if (ret) {
> +			fwnode_handle_put(child);
> +			return ret;
> +		}
> +
> +		if (inputs[0] > MCP3564_INTERNAL_VCM ||
> +		    inputs[1] > MCP3564_INTERNAL_VCM) {
> +			fwnode_handle_put(child);
> +			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 int mcp3564_config(struct iio_dev *indio_dev)
> +{
> +	struct mcp3564_state *adc = iio_priv(indio_dev);
> +	struct device *dev = &adc->spi->dev;
> +	const struct spi_device_id *dev_id;
> +	u8 tmp_reg;
> +	u16 tmp_u16;
> +	enum mcp3564_ids ids;
> +	int ret = 0;
> +	unsigned int tmp = 0x01;
> +	bool err = true;
> +
> +	/*
> +	 * 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 = MCP3564_HW_ADDR_MASK & tmp;

FIELD_GET() as then we don't have to care if the mask goes to bit 0

> +
> +	dev_dbg(dev, "use 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->has_vref = false;
> +		break;
> +	case MCP3564R_C_REG_DEFAULT:
> +		adc->has_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->has_vref)
> +				ids = mcp3461r;
> +			else
> +				ids = mcp3461;
> +			break;
> +		case MCP3462_HW_ID:
> +			if (adc->has_vref)
> +				ids = mcp3462r;
> +			else
> +				ids = mcp3462;
> +			break;
> +		case MCP3464_HW_ID:
> +			if (adc->has_vref)
> +				ids = mcp3464r;
> +			else
> +				ids = mcp3464;
> +			break;
> +		case MCP3561_HW_ID:
> +			if (adc->has_vref)
> +				ids = mcp3561r;
> +			else
> +				ids = mcp3561;
> +			break;
> +		case MCP3562_HW_ID:
> +			if (adc->has_vref)
> +				ids = mcp3562r;
> +			else
> +				ids = mcp3562;
> +			break;
> +		case MCP3564_HW_ID:
> +			if (adc->has_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);
> +		if (!adc->chip_info) {
> +			dev_id = spi_get_device_id(adc->spi);
> +			adc->chip_info = (const struct mcp3564_chip_info *)dev_id->driver_data;
> +		}
> +
> +		adc->has_vref = adc->chip_info->has_vref;
> +	} else {
> +		adc->chip_info = &mcp3564_chip_infos_tbl[ids];
> +	}
> +
> +	dev_dbg(dev, "Found %s chip\n", adc->chip_info->name);
> +
> +	adc->vref = devm_regulator_get_optional(dev, "vref");
> +	if (IS_ERR(adc->vref)) {
> +		if (IS_ERR(adc->vref)) {
I guess this is meant to be an ENODEV check. 

I would suggest flipping the logic so the error return is out of line but the rest isn't.


		if (PTR_ERR(adc->vref) != -ENODEV)
			return dev_err_probe(...);
//side note that you are inconsistent on brackets around a single line statement with
// a line break.  None of them need brackets.


		if (!adc->has_vref) 
			return dev_err_probe(..);

		adc->vref = NULL;
		dev_dbg();
	} else {

		...
		
> +			/* Check if chip has internal vref */
> +			if (!adc->has_vref)
> +				return dev_err_probe(dev, PTR_ERR(adc->vref),
> +					     "Unknown Vref\n");
> +			adc->vref = NULL;
> +			dev_dbg(dev, "%s: Using internal Vref\n", __func__);
> +		} else {
> +			return dev_err_probe(dev, PTR_ERR(adc->vref),
> +					     "failed to get regulator\n");
> +		}
> +	} else {
> +		ret = regulator_enable(adc->vref);
> +		if (ret)
> +			return ret;
> +
> +		ret = devm_add_action_or_reset(dev, mcp3564_disable_reg,
> +					       adc->vref);
> +		if (ret)
> +			return ret;
> +
> +		dev_dbg(dev, "%s: Using External Vref\n", __func__);
> +
> +		ret = regulator_get_voltage(adc->vref);
> +		if (ret < 0) {
> +			return dev_err_probe(dev, ret,
> +					     "Failed to read vref regulator\n");

Brackets not needed.  Consistency matters more than whether you do or do not have
brackets as the multiline because of a line break case may not be well covered
by the kernel style docs.

> +		}
> +
> +		adc->vref_mv = ret / MILLI;
> +	}
> +
> +	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;
> +
> +	mdelay(1); 

Always good to have a doc reference for delays - sometimes they turn out to be a possible
cause of instability - so good to know if this is much larger than the spec requires
or rather close to whatever it says.

> +
> +	/* 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 = MCP3564_CONFIG3_CONV_MODE_SET(MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY);
> +	tmp_reg |= MCP3564_CONFIG3_DATA_FORMAT_SET(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 = MCP3564_CONFIG2_BOOST_CURRENT_SET(MCP3564_BOOST_CURRENT_x1_00);
> +	tmp_reg |= MCP3564_CONFIG2_HARDWARE_GAIN_SET(0x01);
> +	tmp_reg |= MCP3564_CONFIG2_AZ_MUX_SET(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 = MCP3564_CONFIG1_OVERSPL_RATIO_SET(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 = MCP3564_CONFIG0_ADC_MODE_MASK_SET(MCP3564_ADC_MODE_STANDBY);
> +	tmp_reg |= MCP3564_CONFIG0_CS_SEL_SET(MCP3564_CONFIG0_CS_SEL_0_0_uA);
> +	tmp_reg |= MCP3564_CONFIG0_CLK_SEL_SET(MCP3564_CONFIG0_USE_INT_CLK);
> +	tmp_reg |= MCP3456_CONFIG0_BIT6_DEFAULT;
> +
> +	if (!adc->vref) {
> +		tmp_reg |= MCP3564_CONFIG0_VREF_SET(1);
> +		adc->vref_mv = MCP3564R_INT_VREF_MV;
> +	}
> +
> +	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);
> +
> +//#define MCP3564_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)

This wants deleting...








[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux