iio: adc: ad7944: use spi_optimize_message()
This modifies the ad7944 driver to use spi_optimize_message() to reduce CPU usage and increase the max sample rate by avoiding repeating validation of the spi message on each transfer. Signed-off-by: David Lechner <dlechner@baylibre.com> Link: https://lore.kernel.org/r/20240328-ad7944-spi-optimize-message-v2-1-a142b2576379@baylibre.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
parent
27eea4778d
commit
6020ca4de8
@ -51,6 +51,8 @@ static const char * const ad7944_spi_modes[] = {
|
|||||||
struct ad7944_adc {
|
struct ad7944_adc {
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
enum ad7944_spi_mode spi_mode;
|
enum ad7944_spi_mode spi_mode;
|
||||||
|
struct spi_transfer xfers[3];
|
||||||
|
struct spi_message msg;
|
||||||
/* Chip-specific timing specifications. */
|
/* Chip-specific timing specifications. */
|
||||||
const struct ad7944_timing_spec *timing_spec;
|
const struct ad7944_timing_spec *timing_spec;
|
||||||
/* GPIO connected to CNV pin. */
|
/* GPIO connected to CNV pin. */
|
||||||
@ -130,6 +132,88 @@ AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 16, 0);
|
|||||||
/* fully differential */
|
/* fully differential */
|
||||||
AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1);
|
AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1);
|
||||||
|
|
||||||
|
static void ad7944_unoptimize_msg(void *msg)
|
||||||
|
{
|
||||||
|
spi_unoptimize_message(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
|
||||||
|
const struct iio_chan_spec *chan)
|
||||||
|
{
|
||||||
|
unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
|
||||||
|
: adc->timing_spec->conv_ns;
|
||||||
|
struct spi_transfer *xfers = adc->xfers;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NB: can get better performance from some SPI controllers if we use
|
||||||
|
* the same bits_per_word in every transfer.
|
||||||
|
*/
|
||||||
|
xfers[0].bits_per_word = chan->scan_type.realbits;
|
||||||
|
/*
|
||||||
|
* CS is tied to CNV and we need a low to high transition to start the
|
||||||
|
* conversion, so place CNV low for t_QUIET to prepare for this.
|
||||||
|
*/
|
||||||
|
xfers[0].delay.value = T_QUIET_NS;
|
||||||
|
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CS has to be high for full conversion time to avoid triggering the
|
||||||
|
* busy indication.
|
||||||
|
*/
|
||||||
|
xfers[1].cs_off = 1;
|
||||||
|
xfers[1].delay.value = t_conv_ns;
|
||||||
|
xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||||
|
xfers[1].bits_per_word = chan->scan_type.realbits;
|
||||||
|
|
||||||
|
/* Then we can read the data during the acquisition phase */
|
||||||
|
xfers[2].rx_buf = &adc->sample.raw;
|
||||||
|
xfers[2].len = BITS_TO_BYTES(chan->scan_type.storagebits);
|
||||||
|
xfers[2].bits_per_word = chan->scan_type.realbits;
|
||||||
|
|
||||||
|
spi_message_init_with_transfers(&adc->msg, xfers, 3);
|
||||||
|
|
||||||
|
ret = spi_optimize_message(adc->spi, &adc->msg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
|
||||||
|
const struct iio_chan_spec *chan)
|
||||||
|
{
|
||||||
|
unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
|
||||||
|
: adc->timing_spec->conv_ns;
|
||||||
|
struct spi_transfer *xfers = adc->xfers;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NB: can get better performance from some SPI controllers if we use
|
||||||
|
* the same bits_per_word in every transfer.
|
||||||
|
*/
|
||||||
|
xfers[0].bits_per_word = chan->scan_type.realbits;
|
||||||
|
/*
|
||||||
|
* CS has to be high for full conversion time to avoid triggering the
|
||||||
|
* busy indication.
|
||||||
|
*/
|
||||||
|
xfers[0].cs_off = 1;
|
||||||
|
xfers[0].delay.value = t_conv_ns;
|
||||||
|
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||||
|
|
||||||
|
xfers[1].rx_buf = &adc->sample.raw;
|
||||||
|
xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
|
||||||
|
xfers[1].bits_per_word = chan->scan_type.realbits;
|
||||||
|
|
||||||
|
spi_message_init_with_transfers(&adc->msg, xfers, 2);
|
||||||
|
|
||||||
|
ret = spi_optimize_message(adc->spi, &adc->msg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ad7944_3wire_cs_mode_conversion - Perform a 3-wire CS mode conversion and
|
* ad7944_3wire_cs_mode_conversion - Perform a 3-wire CS mode conversion and
|
||||||
* acquisition
|
* acquisition
|
||||||
@ -145,48 +229,7 @@ AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1);
|
|||||||
static int ad7944_3wire_cs_mode_conversion(struct ad7944_adc *adc,
|
static int ad7944_3wire_cs_mode_conversion(struct ad7944_adc *adc,
|
||||||
const struct iio_chan_spec *chan)
|
const struct iio_chan_spec *chan)
|
||||||
{
|
{
|
||||||
unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
|
return spi_sync(adc->spi, &adc->msg);
|
||||||
: adc->timing_spec->conv_ns;
|
|
||||||
struct spi_transfer xfers[] = {
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* NB: can get better performance from some SPI
|
|
||||||
* controllers if we use the same bits_per_word
|
|
||||||
* in every transfer.
|
|
||||||
*/
|
|
||||||
.bits_per_word = chan->scan_type.realbits,
|
|
||||||
/*
|
|
||||||
* CS is tied to CNV and we need a low to high
|
|
||||||
* transition to start the conversion, so place CNV
|
|
||||||
* low for t_QUIET to prepare for this.
|
|
||||||
*/
|
|
||||||
.delay = {
|
|
||||||
.value = T_QUIET_NS,
|
|
||||||
.unit = SPI_DELAY_UNIT_NSECS,
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.bits_per_word = chan->scan_type.realbits,
|
|
||||||
/*
|
|
||||||
* CS has to be high for full conversion time to avoid
|
|
||||||
* triggering the busy indication.
|
|
||||||
*/
|
|
||||||
.cs_off = 1,
|
|
||||||
.delay = {
|
|
||||||
.value = t_conv_ns,
|
|
||||||
.unit = SPI_DELAY_UNIT_NSECS,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/* Then we can read the data during the acquisition phase */
|
|
||||||
.rx_buf = &adc->sample.raw,
|
|
||||||
.len = BITS_TO_BYTES(chan->scan_type.storagebits),
|
|
||||||
.bits_per_word = chan->scan_type.realbits,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return spi_sync_transfer(adc->spi, xfers, ARRAY_SIZE(xfers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -200,33 +243,6 @@ static int ad7944_3wire_cs_mode_conversion(struct ad7944_adc *adc,
|
|||||||
static int ad7944_4wire_mode_conversion(struct ad7944_adc *adc,
|
static int ad7944_4wire_mode_conversion(struct ad7944_adc *adc,
|
||||||
const struct iio_chan_spec *chan)
|
const struct iio_chan_spec *chan)
|
||||||
{
|
{
|
||||||
unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
|
|
||||||
: adc->timing_spec->conv_ns;
|
|
||||||
struct spi_transfer xfers[] = {
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* NB: can get better performance from some SPI
|
|
||||||
* controllers if we use the same bits_per_word
|
|
||||||
* in every transfer.
|
|
||||||
*/
|
|
||||||
.bits_per_word = chan->scan_type.realbits,
|
|
||||||
/*
|
|
||||||
* CS has to be high for full conversion time to avoid
|
|
||||||
* triggering the busy indication.
|
|
||||||
*/
|
|
||||||
.cs_off = 1,
|
|
||||||
.delay = {
|
|
||||||
.value = t_conv_ns,
|
|
||||||
.unit = SPI_DELAY_UNIT_NSECS,
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.rx_buf = &adc->sample.raw,
|
|
||||||
.len = BITS_TO_BYTES(chan->scan_type.storagebits),
|
|
||||||
.bits_per_word = chan->scan_type.realbits,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -234,7 +250,7 @@ static int ad7944_4wire_mode_conversion(struct ad7944_adc *adc,
|
|||||||
* and acquisition process.
|
* and acquisition process.
|
||||||
*/
|
*/
|
||||||
gpiod_set_value_cansleep(adc->cnv, 1);
|
gpiod_set_value_cansleep(adc->cnv, 1);
|
||||||
ret = spi_sync_transfer(adc->spi, xfers, ARRAY_SIZE(xfers));
|
ret = spi_sync(adc->spi, &adc->msg);
|
||||||
gpiod_set_value_cansleep(adc->cnv, 0);
|
gpiod_set_value_cansleep(adc->cnv, 0);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -393,10 +409,6 @@ static int ad7944_probe(struct spi_device *spi)
|
|||||||
else
|
else
|
||||||
adc->spi_mode = ret;
|
adc->spi_mode = ret;
|
||||||
|
|
||||||
if (adc->spi_mode == AD7944_SPI_MODE_CHAIN)
|
|
||||||
return dev_err_probe(dev, -EINVAL,
|
|
||||||
"chain mode is not implemented\n");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some chips use unusual word sizes, so check now instead of waiting
|
* Some chips use unusual word sizes, so check now instead of waiting
|
||||||
* for the first xfer.
|
* for the first xfer.
|
||||||
@ -489,6 +501,23 @@ static int ad7944_probe(struct spi_device *spi)
|
|||||||
return dev_err_probe(dev, -EINVAL,
|
return dev_err_probe(dev, -EINVAL,
|
||||||
"cannot have both chain mode and always turbo\n");
|
"cannot have both chain mode and always turbo\n");
|
||||||
|
|
||||||
|
switch (adc->spi_mode) {
|
||||||
|
case AD7944_SPI_MODE_DEFAULT:
|
||||||
|
ret = ad7944_4wire_mode_init_msg(dev, adc, &chip_info->channels[0]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case AD7944_SPI_MODE_SINGLE:
|
||||||
|
ret = ad7944_3wire_cs_mode_init_msg(dev, adc, &chip_info->channels[0]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case AD7944_SPI_MODE_CHAIN:
|
||||||
|
return dev_err_probe(dev, -EINVAL, "chain mode is not implemented\n");
|
||||||
|
}
|
||||||
|
|
||||||
indio_dev->name = chip_info->name;
|
indio_dev->name = chip_info->name;
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
indio_dev->info = &ad7944_iio_info;
|
indio_dev->info = &ad7944_iio_info;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user