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...