aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Hunt <davidx.hunt@intel.com>2014-06-23 16:07:23 +0200
committerSteve Sakoman <steve@sakoman.com>2015-06-02 14:59:05 -0700
commitf4251192d6f20c8895d2c7d23dd621ea05fbe33c (patch)
tree6d68dbfe111efb935e7448714729a6c9ad5d29e4
parent7bb1e8d184acb3e3b87a6addb9ea66660a0faf52 (diff)
downloadedison-v3.10-f4251192d6f20c8895d2c7d23dd621ea05fbe33c.tar.gz
adc: enabling buffered read support for ti-ads7955
Add support for programming of a sequence of channel reads, and a mechanism for triggering those reads to get back one packet containing all reads. Signed-off-by: David Hunt <davidx.hunt@intel.com>
-rw-r--r--arch/x86/configs/i386_edison_defconfig2
-rw-r--r--drivers/iio/adc/ti-ads7955.c230
2 files changed, 189 insertions, 43 deletions
diff --git a/arch/x86/configs/i386_edison_defconfig b/arch/x86/configs/i386_edison_defconfig
index 0099f394152..4e9a57ed1f8 100644
--- a/arch/x86/configs/i386_edison_defconfig
+++ b/arch/x86/configs/i386_edison_defconfig
@@ -2982,7 +2982,7 @@ CONFIG_STAGING=y
#
# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set
# CONFIG_IIO_GPIO_TRIGGER is not set
-# CONFIG_IIO_SYSFS_TRIGGER is not set
+CONFIG_IIO_SYSFS_TRIGGER=m
# CONFIG_IIO_SIMPLE_DUMMY is not set
# CONFIG_ZSMALLOC is not set
# CONFIG_FB_SM7XX is not set
diff --git a/drivers/iio/adc/ti-ads7955.c b/drivers/iio/adc/ti-ads7955.c
index 10791165ee6..fde230b92b2 100644
--- a/drivers/iio/adc/ti-ads7955.c
+++ b/drivers/iio/adc/ti-ads7955.c
@@ -7,6 +7,16 @@
* Licensed under the GPL-2.
*/
+/*
+ * [FIXME]
+ * Notes: This version of the ti-ads7955 driver is written with a couple of
+ * workarounds for the functionality of the SPI driver on Edison at the time
+ * of writing.
+ * Issue 1: The CS is pushed low between every frame
+ * Issue 2: spi_message_add_tail() can only be called once in the driver.
+ * Subsequent messages are ignored.
+*/
+
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -17,7 +27,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
-
+#include <linux/bitops.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
@@ -26,45 +36,63 @@
#include <linux/platform_data/ti-ads7955.h>
-#define ADS7955_EXTREF 2.5
+#define ADS7955_EXTREF true
+#define SPI_MAX_SPEED_HZ 20000000
+#define SPI_BITS_PER_WORD 16
+
+#define ADS7955_MANUAL_MODE (0x1 << 12) /* Selects Manual Mode */
+#define ADS7955_AUTO1_MODE (0x2 << 12) /* Selects Auto Mode 1 */
+#define ADS7955_AUTO2_MODE (0x3 << 12) /* Selects Auto Mode 2 */
+#define ADS7955_AUTO1_PROGRAM (0x8 << 12) /* Programming Auto Mode 1 */
+#define ADS7955_AUTO2_PROGRAM (0x9 << 12) /* Programming Auto Mode 2 */
-#define ADS7955_MANUAL_MODE (1 << 12) /* Selects Manual Mode */
-#define ADS7955_CONFIG (1 << 11) /* Enable programming bits DI06-00*/
-#define ADS7955_CHANNEL(x) ((x) << 7) /* Channel select (DI10-07) */
+#define ADS7955_CONFIG BIT(11) /* program bits DI06-00 */
+#define ADS7955_AUTO1_RESET BIT(10) /* Reset to first channel */
+#define ADS7955_CHANNEL(x) ((x & 0xf) << 7)/* Channel select (DI10-07) */
-#define ADS7955_RANGE_1 0 /* Selects 2.5V input range (Range 1)*/
-#define ADS7955_RANGE_2 (1 << 6)/* Selects 5.0V input range (Range 2)*/
+#define ADS7955_RANGE_1 0 /* Selects 2.5V input range */
+#define ADS7955_RANGE_2 BIT(6) /* Selects 5.0V input range */
-#define ADS7955_POWER_NORMAL 0 /* No Powerdown */
-#define ADS7955_POWER_DOWN (1 << 5) /* Powerdown on 16th fall-edge of SCLK*/
+#define ADS7955_POWER_NORMAL 0 /* No Powerdown */
+#define ADS7955_POWER_DOWN BIT(5) /* Powerdown on last edge */
-#define ADS7955_GET_CONVERSION 0 /* Powerdown on 16th fall-edge of SCLK*/
-#define ADS7955_GET_GPIO (1 << 4)/* Powerdown on 16th fall-edge of SCLK*/
+#define ADS7955_GET_CONVERSION 0 /* High bits have ch index*/
+#define ADS7955_GET_GPIO BIT(4) /* High bits have GPIO bits */
#define ADS7955_SET_READ (ADS7955_MANUAL_MODE | ADS7955_CONFIG | \
ADS7955_RANGE_2 | ADS7955_POWER_NORMAL | \
ADS7955_GET_CONVERSION)
-#define ADS7955_MAX_CHAN 8
-#define ADS7955_BITS 10
-#define ADS7955_STORAGE_BITS 12
-#define ADS7955_INTREF_mV 3300
+#define ADS7955_READ_AUTO1 (ADS7955_AUTO1_MODE | ADS7955_CONFIG | \
+ ADS7955_RANGE_2 | ADS7955_POWER_NORMAL | \
+ ADS7955_GET_CONVERSION)
+#define ADS7955_MAX_CHAN 8
+#define ADS7955_BITS 12
+#define ADS7955_STORAGE_BITS 16
+/*
+ * Define the Reference Voltage for the board on which this ADC is used.
+ * May change depending on jumper settings or wiring configuration.
+ */
+#define ADS7955_INTREF_mV 5000
+#define SPI_MSG_MAX_LEN 20 /* 8 channels plus timestamp */
#define RES_MASK(bits) ((1 << (bits)) - 1)
struct ads7955_state {
- struct spi_device *spi;
- struct regulator *reg;
- unsigned ext_ref;
- struct spi_transfer scan_single_xfer[3];
- struct spi_message scan_single_msg;
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned ext_ref;
+ struct spi_transfer ring_xfer[10];
+ struct spi_transfer scan_single_xfer[3];
+ struct spi_message ring_msg;
+ struct spi_message scan_single_msg;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
- __be16 rx_buf[1] ____cacheline_aligned;
- __be16 tx_buf[1];
+ __u16 rx_buf[SPI_MSG_MAX_LEN] ____cacheline_aligned;
+ __u16 tx_buf[SPI_MSG_MAX_LEN];
};
#define ADS7955_V_CHAN(index) \
@@ -78,9 +106,9 @@ struct ads7955_state {
.scan_index = index, \
.scan_type = { \
.sign = 'u', \
- .realbits = 10, \
- .storagebits = 16, \
- .endianness = IIO_BE, \
+ .realbits = ADS7955_BITS, \
+ .storagebits = ADS7955_STORAGE_BITS, \
+ .endianness = IIO_CPU, \
}, \
}
@@ -96,18 +124,124 @@ static const struct iio_chan_spec ads7955_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(8),
};
+/**
+ * ads7955_update_scan_mode() setup the spi transfer buffer for the scan mask
+ **/
+static int ads7955_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *active_scan_mask)
+{
+ struct ads7955_state *st = iio_priv(indio_dev);
+ int i, ret;
+ unsigned short channel_count;
+
+ /*
+ * For programming the auto1 mode, we need to send two words, one to
+ * specify program mode, and the other to give a bitmask of channels
+ * to be read when reading the auto sequence.
+ */
+ /*
+ * [FIXME]
+ * Workaround: Build up a custom SPI message containing all required
+ * frames (including space for expected responses), and send as one
+ * SPI messge. This is to get around the issue that the current SPI
+ * driver only supports the first 'spi_message_add_tail' call.
+ */
+ st->tx_buf[0] = ADS7955_AUTO1_PROGRAM;
+ st->tx_buf[1] = (unsigned short)*active_scan_mask;
+ st->tx_buf[2] = (ADS7955_SET_READ | ADS7955_POWER_DOWN);
+
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ return ret;
+
+ /*
+ * So now we've told the hardware about the channels we want to sample,
+ * now we set up the message sequence for when we're triggered.
+ */
+ /*
+ * [FIXME]
+ * Workaround: Build up a custom SPI message containing all required
+ * frames (including space for expected responses), and send as one
+ * SPI messge. This is to get around the issue that the current SPI
+ * driver only supports the first 'spi_message_add_tail' call.
+ */
+ channel_count = 0;
+ for (i = 0; i < ADS7955_MAX_CHAN; i++) {
+ if (test_bit(i, active_scan_mask)) {
+ if (channel_count == 0)
+ st->tx_buf[channel_count] = (ADS7955_READ_AUTO1
+ | ADS7955_AUTO1_RESET);
+ else
+ st->tx_buf[channel_count] =
+ (ADS7955_READ_AUTO1);
+ channel_count++;
+ }
+ }
+
+ /* Put in some extra tx frames to allow us to get the
+ rx frames (behind tx by two frames) */
+ st->tx_buf[channel_count++] = (ADS7955_READ_AUTO1);
+ st->tx_buf[channel_count++] = (ADS7955_READ_AUTO1 |
+ ADS7955_POWER_DOWN);
+
+ st->ring_xfer[0].tx_buf = &st->tx_buf[0];
+ st->ring_xfer[0].rx_buf = &st->rx_buf[0];
+ st->ring_xfer[0].len = channel_count * 2;
+ spi_message_init(&st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
+ return 0;
+}
+
+/**
+ * ads7955_trigger_handler() bh of trigger launched polling to ring buffer
+ **/
+static irqreturn_t ads7955_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads7955_state *st = iio_priv(indio_dev);
+ u8 *return_data = (u8 *)&(st->rx_buf[2]);
+ s64 time_ns = 0;
+ int ret;
+
+ ret = spi_sync(st->spi, &st->ring_msg);
+ if (ret)
+ return IRQ_HANDLED;
+
+ if (indio_dev->scan_timestamp) {
+ time_ns = iio_get_time_ns();
+ memcpy(return_data +
+ indio_dev->scan_bytes - sizeof(s64),
+ &time_ns, sizeof(time_ns));
+ }
+
+ iio_push_to_buffers(indio_dev, return_data);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
static int ads7955_scan_direct(struct ads7955_state *st, unsigned ch)
{
- int ret, count = 3;
+ int ret;
+ /*
+ * [FIXME]
+ * Workaround: Build up a custom SPI message containing all required
+ * frames (including space for expected responses), and send as one
+ * SPI messge. This is to get around the issue that the current SPI
+ * driver only supports the first 'spi_message_add_tail' call.
+ */
st->tx_buf[0] = (ADS7955_SET_READ | ADS7955_CHANNEL(ch));
- while (count--) {
- ret = spi_sync(st->spi, &st->scan_single_msg);
- if (ret)
- return ret;
- }
+ st->tx_buf[1] = (ADS7955_SET_READ | ADS7955_CHANNEL(ch));
+ st->tx_buf[2] = (ADS7955_SET_READ | ADS7955_CHANNEL(ch) |
+ ADS7955_POWER_DOWN);
- return st->rx_buf[0];
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ return ret;
+ return st->rx_buf[2];
}
@@ -143,12 +277,10 @@ static int ads7955_read_raw(struct iio_dev *indio_dev,
else
ret = ads7955_scan_direct(st, chan->address);
mutex_unlock(&indio_dev->mlock);
-
if (ret < 0)
return ret;
*val = ret & RES_MASK(ADS7955_BITS);
-
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
@@ -159,15 +291,13 @@ static int ads7955_read_raw(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
- case IIO_CHAN_INFO_OFFSET:
- *val = 1093 - 2732500 / ads7955_get_ref_voltage(st);
- return IIO_VAL_INT;
}
return -EINVAL;
}
static const struct iio_info ads7955_info = {
.read_raw = &ads7955_read_raw,
+ .update_scan_mode = ads7955_update_scan_mode,
.driver_module = THIS_MODULE,
};
@@ -208,24 +338,39 @@ static int ads7955_probe(struct spi_device *spi)
indio_dev->num_channels = ARRAY_SIZE(ads7955_channels);
indio_dev->info = &ads7955_info;
- /* Setup default SPI comms parameters */
- spi->bits_per_word = 16;
- spi->max_speed_hz = 20000000;
+ /*
+ * Setup default message
+ * [FIXME]
+ * Workaround: Send each frame as 16 bits to get over the fact that
+ * the current SPI hardware pulls CS low between every frame.
+ */
+ spi->bits_per_word = SPI_BITS_PER_WORD;
+ spi->max_speed_hz = SPI_MAX_SPEED_HZ;
spi_setup(spi);
st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
st->scan_single_xfer[0].rx_buf = &st->rx_buf[0];
- st->scan_single_xfer[0].len = 2;
+ st->scan_single_xfer[0].len = 6;
spi_message_init(&st->scan_single_msg);
spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg);
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &ads7955_trigger_handler, NULL);
+ if (ret) {
+ dev_warn(&indio_dev->dev,
+ "Failed to set up iio_triggered_buffer_setup\n");
+ goto error_disable_reg;
+ }
+
ret = iio_device_register(indio_dev);
if (ret)
- goto error_disable_reg;
+ goto error_cleanup_ring;
return 0;
+error_cleanup_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
error_disable_reg:
if (st->ext_ref)
regulator_disable(st->reg);
@@ -244,6 +389,7 @@ static int ads7955_remove(struct spi_device *spi)
struct ads7955_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
if (st->ext_ref) {
regulator_disable(st->reg);
regulator_put(st->reg);