Skip to content

Commit

Permalink
staging:iio:adis: Preallocate transfer message
Browse files Browse the repository at this point in the history
Currently the driver reads out all sample registers of the device and throws
away those which it does not need. Furthermore the SPI message is constructed
each time the trigger handler is run, although it will be the same each time.
This patch preallocates and pre-constructs the SPI message in the
"update_scan_mode" callback. Only those register which are actually selected for
sampling are included in the message. The patch also gets rid of the conversion
of the sample data from big endian to the native endianness and instead marks
the channel as big endian in its scan type. This allows to directly push the
SPI transfer buffer to the IIO buffer without the need to post-process it.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
  • Loading branch information
Lars-Peter Clausen authored and Jonathan Cameron committed Nov 19, 2012
1 parent a458c55 commit aacff89
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 48 deletions.
1 change: 1 addition & 0 deletions drivers/staging/iio/accel/adis16201_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ static const struct iio_chan_spec adis16201_channels[] = {
static const struct iio_info adis16201_info = {
.read_raw = &adis16201_read_raw,
.write_raw = &adis16201_write_raw,
.update_scan_mode = adis_update_scan_mode,
.driver_module = THIS_MODULE,
};

Expand Down
1 change: 1 addition & 0 deletions drivers/staging/iio/accel/adis16203_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ static const struct iio_chan_spec adis16203_channels[] = {
static const struct iio_info adis16203_info = {
.read_raw = &adis16203_read_raw,
.write_raw = &adis16203_write_raw,
.update_scan_mode = adis_update_scan_mode,
.driver_module = THIS_MODULE,
};

Expand Down
1 change: 1 addition & 0 deletions drivers/staging/iio/accel/adis16204_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ static const struct iio_chan_spec adis16204_channels[] = {
static const struct iio_info adis16204_info = {
.read_raw = &adis16204_read_raw,
.write_raw = &adis16204_write_raw,
.update_scan_mode = adis_update_scan_mode,
.driver_module = THIS_MODULE,
};

Expand Down
1 change: 1 addition & 0 deletions drivers/staging/iio/accel/adis16209_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ static const struct iio_chan_spec adis16209_channels[] = {
static const struct iio_info adis16209_info = {
.read_raw = &adis16209_read_raw,
.write_raw = &adis16209_write_raw,
.update_scan_mode = adis_update_scan_mode,
.driver_module = THIS_MODULE,
};

Expand Down
1 change: 1 addition & 0 deletions drivers/staging/iio/accel/adis16240_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ static const struct iio_info adis16240_info = {
.attrs = &adis16240_attribute_group,
.read_raw = &adis16240_read_raw,
.write_raw = &adis16240_write_raw,
.update_scan_mode = adis_update_scan_mode,
.driver_module = THIS_MODULE,
};

Expand Down
1 change: 1 addition & 0 deletions drivers/staging/iio/gyro/adis16260_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ static const struct iio_info adis16260_info = {
.attrs = &adis16260_attribute_group,
.read_raw = &adis16260_read_raw,
.write_raw = &adis16260_write_raw,
.update_scan_mode = adis_update_scan_mode,
.driver_module = THIS_MODULE,
};

Expand Down
8 changes: 8 additions & 0 deletions drivers/staging/iio/imu/adis.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ int adis_single_conversion(struct iio_dev *indio_dev,
.sign = 'u', \
.realbits = (bits), \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
}

Expand All @@ -109,6 +110,7 @@ int adis_single_conversion(struct iio_dev *indio_dev,
.sign = 'u', \
.realbits = (bits), \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
}

Expand All @@ -125,6 +127,7 @@ int adis_single_conversion(struct iio_dev *indio_dev,
.sign = 's', \
.realbits = (bits), \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
}

Expand All @@ -150,6 +153,9 @@ void adis_cleanup_buffer_and_trigger(struct adis *adis,
int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev);
void adis_remove_trigger(struct adis *adis);

int adis_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask);

#else /* CONFIG_IIO_BUFFER */

static inline int adis_setup_buffer_and_trigger(struct adis *adis,
Expand All @@ -173,6 +179,8 @@ static inline void adis_remove_trigger(struct adis *adis)
{
}

#define adis_update_scan_mode NULL

#endif /* CONFIG_IIO_BUFFER */

#endif
114 changes: 66 additions & 48 deletions drivers/staging/iio/imu/adis_buffer.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/*
* Common library for ADIS16XXX devices
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/

#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
Expand All @@ -12,73 +21,80 @@

#include "adis.h"

#define ADIS_MAX_OUTPUTS 12

static int adis_read_buffer_data(struct adis *adis, struct iio_dev *indio_dev)
int adis_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
int n_outputs = indio_dev->num_channels;
struct spi_transfer xfers[ADIS_MAX_OUTPUTS + 1];
struct spi_message msg;
int ret;
int i;

mutex_lock(&adis->txrx_lock);

spi_message_init(&msg);

memset(xfers, 0, sizeof(xfers));
for (i = 0; i <= n_outputs; i++) {
xfers[i].bits_per_word = 8;
xfers[i].cs_change = 1;
xfers[i].len = 2;
xfers[i].delay_usecs = adis->data->read_delay;
if (i < n_outputs) {
xfers[i].tx_buf = adis->tx + 2 * i;
adis->tx[2 * i] = indio_dev->channels[i].address;
adis->tx[2 * i + 1] = 0;
}
if (i >= 1)
xfers[i].rx_buf = adis->rx + 2 * (i - 1);
spi_message_add_tail(&xfers[i], &msg);
}
struct adis *adis = iio_device_get_drvdata(indio_dev);
const struct iio_chan_spec *chan;
unsigned int scan_count;
unsigned int i, j;
__be16 *tx, *rx;

ret = spi_sync(adis->spi, &msg);
if (ret)
dev_err(&adis->spi->dev, "Failed to read data: %d", ret);
kfree(adis->xfer);
kfree(adis->buffer);

mutex_unlock(&adis->txrx_lock);
scan_count = indio_dev->scan_bytes / 2;

return ret;
adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL);
if (!adis->xfer)
return -ENOMEM;

adis->buffer = kzalloc(indio_dev->scan_bytes * 2, GFP_KERNEL);
if (!adis->buffer)
return -ENOMEM;

rx = adis->buffer;
tx = rx + indio_dev->scan_bytes;

spi_message_init(&adis->msg);

for (j = 0; j <= scan_count; j++) {
adis->xfer[j].bits_per_word = 8;
if (j != scan_count)
adis->xfer[j].cs_change = 1;
adis->xfer[j].len = 2;
adis->xfer[j].delay_usecs = adis->data->read_delay;
if (j < scan_count)
adis->xfer[j].tx_buf = &tx[j];
if (j >= 1)
adis->xfer[j].rx_buf = &rx[j - 1];
spi_message_add_tail(&adis->xfer[j], &adis->msg);
}

chan = indio_dev->channels;
for (i = 0; i < indio_dev->num_channels; i++, chan++) {
if (!test_bit(chan->scan_index, scan_mask))
continue;
*tx++ = cpu_to_be16(chan->address << 8);
}

return 0;
}
EXPORT_SYMBOL_GPL(adis_update_scan_mode);

static irqreturn_t adis_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adis *adis = iio_device_get_drvdata(indio_dev);
u16 *data;
int i = 0;
int ret;

data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
if (data == NULL) {
dev_err(&adis->spi->dev, "Failed to allocate memory.");
if (!adis->buffer)
return -ENOMEM;
}

if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)
&& adis_read_buffer_data(adis, indio_dev) >= 0)
for (; i < bitmap_weight(indio_dev->active_scan_mask,
indio_dev->masklength); i++)
data[i] = be16_to_cpup((__be16 *)&(adis->rx[i*2]));
ret = spi_sync(adis->spi, &adis->msg);
if (ret)
dev_err(&adis->spi->dev, "Failed to read data: %d", ret);

/* Guaranteed to be aligned with 8 byte boundary */
if (indio_dev->scan_timestamp)
*((s64 *)(PTR_ALIGN(data, sizeof(s64)))) = pf->timestamp;
if (indio_dev->scan_timestamp) {
void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64);
*(s64 *)b = pf->timestamp;
}

iio_push_to_buffers(indio_dev, (u8 *)data);
iio_push_to_buffers(indio_dev, adis->buffer);

iio_trigger_notify_done(indio_dev->trig);
kfree(data);

return IRQ_HANDLED;
}
Expand Down Expand Up @@ -137,6 +153,8 @@ void adis_cleanup_buffer_and_trigger(struct adis *adis,
{
if (adis->spi->irq)
adis_remove_trigger(adis);
kfree(adis->buffer);
kfree(adis->xfer);
iio_triggered_buffer_cleanup(indio_dev);
}
EXPORT_SYMBOL_GPL(adis_cleanup_buffer_and_trigger);

0 comments on commit aacff89

Please sign in to comment.