// SPDX-License-Identifier: GPL-2.0-only /* * Analog Devices AD4030 and AD4630 ADC family driver. * * Copyright 2024 Analog Devices, Inc. * Copyright 2024 BayLibre, SAS * * based on code from: * Analog Devices, Inc. * Sergiu Cuciurean <sergiu.cuciurean@analog.com> * Nuno Sa <nuno.sa@analog.com> * Marcelo Schmitt <marcelo.schmitt@analog.com> * Liviu Adace <liviu.adace@analog.com>
*/
struct ad4030_chip_info { constchar *name; constunsignedlong *available_masks; conststruct iio_chan_spec channels[AD4030_MAX_IIO_CHANNEL_NB];
u8 grade;
u8 precision_bits; /* Number of hardware channels */ int num_voltage_inputs; unsignedint tcyc_ns;
};
struct ad4030_state { struct spi_device *spi; struct regmap *regmap; conststruct ad4030_chip_info *chip; struct gpio_desc *cnv_gpio; int vref_uv; int vio_uv; int offset_avail[3]; unsignedint avg_log2; enum ad4030_out_mode mode;
/* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines.
*/
u8 tx_data[AD4030_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN); union {
u8 raw[AD4030_MAXIMUM_RX_BUFFER_SIZE]; struct {
s32 diff;
u8 common;
} single; struct {
s32 diff[2];
u8 common[2];
} dual;
} rx_data;
};
/* * For a chip with 2 hardware channel this will be used to create 2 common-mode * channels: * - voltage4 * - voltage5 * As the common-mode channels are after the differential ones, we compute the * channel number like this: * - _idx is the scan_index (the order in the output buffer) * - _ch is the hardware channel number this common-mode channel is related * - _idx - _ch gives us the number of channel in the chip * - _idx - _ch * 2 is the starting number of the common-mode channels, since * for each differential channel there is a common-mode channel * - _idx - _ch * 2 + _ch gives the channel number for this specific common-mode * channel
*/ #define AD4030_CHAN_CMO(_idx, _ch) { \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.address = (_ch), \
.channel = ((_idx) - (_ch)) * 2 + (_ch), \
.scan_index = (_idx), \
.scan_type = { \
.sign = 'u', \
.storagebits = 8, \
.realbits = 8, \
.endianness = IIO_BE, \
}, \
}
ret = ad4030_enter_config_mode(st); if (ret) return ret;
memcpy(st->tx_data, data, count);
ret = spi_sync_transfer(st->spi, &xfer, 1); if (ret) return ret;
/* * From datasheet: "After a [...] reset, no SPI commands or conversions * can be started for 750us" * After a reset we are in conversion mode, no need to exit config mode
*/ if (is_reset) {
fsleep(750); return0;
}
/* * Descramble 2 32bits numbers out of a 64bits. The bits are interleaved: * 1 bit for first number, 1 bit for the second, and so on...
*/ staticvoid ad4030_extract_interleaved(u8 *src, u32 *ch0, u32 *ch1)
{
u8 h0, h1, l0, l1;
u32 out0, out1;
u8 *out0_raw = (u8 *)&out0;
u8 *out1_raw = (u8 *)&out1;
/* Number of bytes for one differential channel */
bytes_to_read = diff_realbytes; /* Add one byte if we are using a differential + common byte mode */
bytes_to_read += (st->mode == AD4030_OUT_DATA_MD_24_DIFF_8_COM ||
st->mode == AD4030_OUT_DATA_MD_16_DIFF_8_COM) ? 1 : 0; /* Mulitiply by the number of hardware channels */
bytes_to_read *= st->chip->num_voltage_inputs;
for (i = 0; i < cnv_nb; i++) {
gpiod_set_value_cansleep(st->cnv_gpio, 1);
ndelay(AD4030_TCNVH_NS);
gpiod_set_value_cansleep(st->cnv_gpio, 0);
ndelay(st->chip->tcyc_ns);
}
ret = spi_read(st->spi, st->rx_data.raw, bytes_to_read); if (ret) return ret;
if (st->chip->num_voltage_inputs == 2)
ad4030_extract_interleaved(st->rx_data.raw,
&st->rx_data.dual.diff[0],
&st->rx_data.dual.diff[1]);
/* * If no common mode voltage channel is enabled, we can use the raw * data as is. Otherwise, we need to rearrange the data a bit to match * the natural alignment of the IIO buffer.
*/
if (st->mode != AD4030_OUT_DATA_MD_16_DIFF_8_COM &&
st->mode != AD4030_OUT_DATA_MD_24_DIFF_8_COM) return0;
if (st->chip->num_voltage_inputs == 1) {
st->rx_data.single.common = st->rx_data.raw[diff_realbytes]; return0;
}
for (i = 0; i < st->chip->num_voltage_inputs; i++)
st->rx_data.dual.common[i] =
st->rx_data.raw[diff_storagebytes * i + diff_realbytes];
return0;
}
staticint ad4030_single_conversion(struct iio_dev *indio_dev, conststruct iio_chan_spec *chan, int *val)
{ struct ad4030_state *st = iio_priv(indio_dev); int ret;
ret = ad4030_set_mode(indio_dev, BIT(chan->scan_index)); if (ret) return ret;
ret = ad4030_conversion(indio_dev); if (ret) return ret;
if (chan->differential) if (st->chip->num_voltage_inputs == 1)
*val = st->rx_data.single.diff; else
*val = st->rx_data.dual.diff[chan->address]; else if (st->chip->num_voltage_inputs == 1)
*val = st->rx_data.single.common; else
*val = st->rx_data.dual.common[chan->address];
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ids), ids); if (ret) return dev_err_probe(dev, ret, "Failed to enable regulators\n");
st->vio_uv = devm_regulator_get_enable_read_voltage(dev, "vio"); if (st->vio_uv < 0) return dev_err_probe(dev, st->vio_uv, "Failed to enable and read vio voltage\n");
st->vref_uv = devm_regulator_get_enable_read_voltage(dev, "ref"); if (st->vref_uv < 0) { if (st->vref_uv != -ENODEV) return dev_err_probe(dev, st->vref_uv, "Failed to read ref voltage\n");
/* if not using optional REF, the REFIN must be used */
st->vref_uv = devm_regulator_get_enable_read_voltage(dev, "refin"); if (st->vref_uv < 0) return dev_err_probe(dev, st->vref_uv, "Failed to read refin voltage\n");
}
reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset)) return dev_err_probe(dev, PTR_ERR(reset), "Failed to get reset GPIO\n");
if (reset) {
ndelay(50);
gpiod_set_value_cansleep(reset, 0); return0;
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM;
st = iio_priv(indio_dev);
st->spi = spi;
st->regmap = devm_regmap_init(dev, &ad4030_regmap_bus, st,
&ad4030_regmap_config); if (IS_ERR(st->regmap)) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to initialize regmap\n");
st->chip = spi_get_device_match_data(spi); if (!st->chip) return -EINVAL;
ret = ad4030_regulators_get(st); if (ret) return ret;
/* * From datasheet: "Perform a reset no sooner than 3ms after the power * supplies are valid and stable"
*/
fsleep(3000);
ret = ad4030_reset(st); if (ret) return ret;
ret = ad4030_detect_chip_info(st); if (ret) return ret;
ret = ad4030_config(st); if (ret) return ret;
st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW); if (IS_ERR(st->cnv_gpio)) return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), "Failed to get cnv gpio\n");
/* * One hardware channel is split in two software channels when using * common byte mode. Add one more channel for the timestamp.
*/
indio_dev->num_channels = 2 * st->chip->num_voltage_inputs + 1;
indio_dev->name = st->chip->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &ad4030_iio_info;
indio_dev->channels = st->chip->channels;
indio_dev->available_scan_masks = st->chip->available_masks;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
ad4030_trigger_handler,
&ad4030_buffer_setup_ops); if (ret) return dev_err_probe(dev, ret, "Failed to setup triggered buffer\n");
MODULE_AUTHOR("Esteban Blanc <eblanc@baylibre.com>");
MODULE_DESCRIPTION("Analog Devices AD4630 ADC family driver");
MODULE_LICENSE("GPL");
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.16Bemerkung:
(vorverarbeitet am 2026-06-05)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.