Status of SPI bitbang / accelerometers

Simon Kagstrom simon.kagstrom at gmail.com
Wed Jan 7 07:40:47 CET 2009


On Tue, 6 Jan 2009 23:20:55 -0500
"Nelson Castillo" <arhuaco at freaks-unidos.net> wrote:

> I've been reading this thread about the accelerometers and the SPI
> problems.
> 
> http://www.mail-archive.com/openmoko-kernel@lists.openmoko.org/msg06231.html
> 
> I would like check the issue.

Great! (I see lots of good work from you!)

> Is anyone working on this?
> Has anything changed since then?

No, I started looking at it, but my head went buzzing from looking into
the SPI code (layers upon layers!). I guess you are already aware of
this, but for other readers of the thread: It's not really a
functionality issue but something needed to upstream the driver.

Using the current SPI interface is too slow on the openmoko and also not
really safe since it cannot be operated in interrupt mode. Andys
implementation simply communicates with the accelerometers manually so
everything is fine there. But not for upstream, which tends to
dislike duplicated efforts.


The idea we discussed was therefore to add a synchronous SPI transfer
function, which can be called from interrupt context. It was posted
further up in your thread:

   http://www.mail-archive.com/openmoko-kernel@lists.openmoko.org/msg06227.html

the problems which got me dizzy was the layering of SPI, which I never
really got the hang of. Also, if you want to work on this, I've found
the eclipse code navigation to be really, really nice (makes it easy to
lookup all implementations of the interfaces etc). I've written a small
guide for how to set it up for the kernel here:

  http://simonkagstrom.livejournal.com/33093.html


The patch with what I got so far is below. It's not much really, the
big function is just a copy of bitbang_work, and I think you can
safely ignore it.

// Simon
From: Simon Kagstrom <simon.kagstrom at gmail.com>

Signed-off-by: Simon Kagstrom <simon.kagstrom at gmail.com>
---

 drivers/spi/spi_bitbang.c |  117 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h   |    6 ++
 2 files changed, 123 insertions(+), 0 deletions(-)

diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c
index 96cc39e..44d74e0 100644
--- a/drivers/spi/spi_bitbang.c
+++ b/drivers/spi/spi_bitbang.c
@@ -421,6 +421,121 @@ int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
 }
 EXPORT_SYMBOL_GPL(spi_bitbang_transfer);
 
+int spi_bitbang_transfer_sync(struct spi_device *spi, struct spi_message *m)
+{
+	struct spi_bitbang	*bitbang = spi_master_get_devdata(spi->master);
+	struct spi_device	*spi;
+	unsigned		nsecs;
+	struct spi_transfer	*t = NULL;
+	unsigned		tmp;
+	unsigned		cs_change;
+	int			status;
+	int			(*setup_transfer)(struct spi_device *,
+					struct spi_transfer *);
+
+	list_del_init(&m->queue);
+
+	/* FIXME this is made-up ... the correct value is known to
+	 * word-at-a-time bitbang code, and presumably chipselect()
+	 * should enforce these requirements too?
+	 */
+	nsecs = 100;
+
+	tmp = 0;
+	cs_change = 1;
+	status = 0;
+	setup_transfer = NULL;
+
+	list_for_each_entry (t, &m->transfers, transfer_list) {
+
+		/* override or restore speed and wordsize */
+		if (t->speed_hz || t->bits_per_word) {
+			setup_transfer = bitbang->setup_transfer;
+			if (!setup_transfer) {
+				status = -ENOPROTOOPT;
+				break;
+			}
+		}
+		if (setup_transfer) {
+			status = setup_transfer(spi, t);
+			if (status < 0)
+				break;
+		}
+
+		/* set up default clock polarity, and activate chip;
+		 * this implicitly updates clock and spi modes as
+		 * previously recorded for this device via setup().
+		 * (and also deselects any other chip that might be
+		 * selected ...)
+		 */
+		if (cs_change) {
+			bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
+			ndelay(nsecs);
+		}
+		cs_change = t->cs_change;
+		if (!t->tx_buf && !t->rx_buf && t->len) {
+			status = -EINVAL;
+			break;
+		}
+
+		/* transfer data.  the lower level code handles any
+		 * new dma mappings it needs. our caller always gave
+		 * us dma-safe buffers.
+		 */
+		if (t->len) {
+			/* REVISIT dma API still needs a designated
+			 * DMA_ADDR_INVALID; ~0 might be better.
+			 */
+			if (!m->is_dma_mapped)
+				t->rx_dma = t->tx_dma = 0;
+			status = bitbang->txrx_bufs(spi, t);
+		}
+		if (status > 0)
+			m->actual_length += status;
+		if (status != t->len) {
+			/* always report some kind of error */
+			if (status >= 0)
+				status = -EREMOTEIO;
+			break;
+		}
+		status = 0;
+
+		/* protocol tweaks before next transfer */
+		if (t->delay_usecs)
+			udelay(t->delay_usecs);
+
+		if (!cs_change)
+			continue;
+		if (t->transfer_list.next == &m->transfers)
+			break;
+
+		/* sometimes a short mid-message deselect of the chip
+		 * may be needed to terminate a mode or command
+		 */
+		ndelay(nsecs);
+		bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
+		ndelay(nsecs);
+	}
+
+	m->status = status;
+
+	/* restore speed and wordsize */
+	if (setup_transfer)
+		setup_transfer(spi, NULL);
+
+	/* normally deactivate chipselect ... unless no error and
+	 * cs_change has hinted that the next message will probably
+	 * be for this chip too.
+	 */
+	if (!(status == 0 && cs_change)) {
+		ndelay(nsecs);
+		bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
+		ndelay(nsecs);
+	}
+
+	return status;
+}
+
 /*----------------------------------------------------------------------*/
 
 /**
@@ -459,6 +574,8 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
 
 	if (!bitbang->master->transfer)
 		bitbang->master->transfer = spi_bitbang_transfer;
+	if (!bitbang->master->transfer_sync)
+		bitbang->master->transfer_sync = spi_bitbang_transfer_sync;
 	if (!bitbang->txrx_bufs) {
 		bitbang->use_dma = 0;
 		bitbang->txrx_bufs = spi_bitbang_bufs;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 4be01bb..6147e57 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -262,6 +262,10 @@ struct spi_master {
 	int			(*transfer)(struct spi_device *spi,
 						struct spi_message *mesg);
 
+	/* bidirectional bulk transfer, synchronous (no callback) */
+	int			(*transfer_sync)(struct spi_device *spi,
+						struct spi_message *mesg);
+
 	/* called on release() to free memory provided by spi_master */
 	void			(*cleanup)(struct spi_device *spi);
 };
@@ -573,6 +577,8 @@ spi_async(struct spi_device *spi, struct spi_message *message)
 
 /*---------------------------------------------------------------------------*/
 
+extern int spi_sync_poll(struct spi_device *spi, struct spi_message *message);
+
 /* All these synchronous SPI transfer routines are utilities layered
  * over the core async transfer primitive.  Here, "synchronous" means
  * they will sleep uninterruptibly until the async transfer completes.



More information about the openmoko-kernel mailing list