2005-12-23 14:05:45

by Vitaly Wool

[permalink] [raw]
Subject: [PATCH] SPI: turn transfers from arrays to linked lists

Hi,

the patch inlined is changing the SPI core and its users to have transfers in the SPI message structure as linked list not as an array, for the reasons discussed recently in this list.
We decided to post is as a single patch, it's not that big to split it :)

Vitaly

Signed-off-by: Vitaly Wool <[email protected]>
Signed-off-by: Dmitry Pervushin <[email protected]>

drivers/input/touchscreen/ads7846.c | 12 ++++++++----
drivers/mtd/devices/m25p80.c | 15 +++++++--------
drivers/mtd/devices/mtd_dataflash.c | 27 +++++++++++++--------------
drivers/spi/spi.c | 6 +++---
drivers/spi/spi_bitbang.c | 13 ++++---------
include/linux/spi/spi.h | 27 ++++++++++++++++-----------
6 files changed, 51 insertions(+), 49 deletions(-)

diff -uNr --exclude='patches*' --exclude=.pc linux-2.6.orig/drivers/input/touchscreen/ads7846.c linux-2.6/drivers/input/touchscreen/ads7846.c
--- linux-2.6.orig/drivers/input/touchscreen/ads7846.c 2005-12-23 13:46:16.000000000 +0300
+++ linux-2.6/drivers/input/touchscreen/ads7846.c 2005-12-23 13:34:01.000000000 +0300
@@ -155,10 +155,13 @@
struct ser_req *req = kzalloc(sizeof *req, SLAB_KERNEL);
int status;
int sample;
+ int i;

if (!req)
return -ENOMEM;

+ INIT_LIST_HEAD(&req->msg.transfers);
+
/* activate reference, so it has time to settle; */
req->xfer[0].tx_buf = &ref_on;
req->xfer[0].len = 1;
@@ -192,8 +195,8 @@
/* group all the transfers together, so we can't interfere with
* reading touchscreen state; disable penirq while sampling
*/
- req->msg.transfers = req->xfer;
- req->msg.n_transfer = 6;
+ for (i = 0; i < 6; i++)
+ list_add_tail(&req->xfer[i].link, &req->msg.transfers);

disable_irq(spi->irq);
status = spi_sync(spi, &req->msg);
@@ -398,6 +401,7 @@
struct ads7846 *ts;
struct ads7846_platform_data *pdata = spi->dev.platform_data;
struct spi_transfer *x;
+ int i;

if (!spi->irq) {
dev_dbg(&spi->dev, "no IRQ?\n");
@@ -500,8 +504,8 @@

CS_CHANGE(x[-1]);

- ts->msg.transfers = ts->xfer;
- ts->msg.n_transfer = x - ts->xfer;
+ for (i = 0; i < x - ts->xfer; i++)
+ list_add_tail(&ts->xfer[i].link, &ts->msg.transfers);
ts->msg.complete = ads7846_rx;
ts->msg.context = ts;

diff -uNr --exclude='patches*' --exclude=.pc linux-2.6.orig/drivers/mtd/devices/m25p80.c linux-2.6/drivers/mtd/devices/m25p80.c
--- linux-2.6.orig/drivers/mtd/devices/m25p80.c 2005-12-23 13:46:16.000000000 +0300
+++ linux-2.6/drivers/mtd/devices/m25p80.c 2005-12-23 15:38:19.000000000 +0300
@@ -231,7 +231,7 @@
{
struct m25p *flash = mtd_to_m25p(mtd);
struct spi_transfer t[2];
- struct spi_message m;
+ DECLARE_SPI_MESSAGE(m);

DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %z\n", spi->dev.bus_id,
__FUNCTION__, "from", (u32) from, len);
@@ -268,12 +268,11 @@

t[0].tx_buf = flash->command;
t[0].len = sizeof(flash->command);
+ list_add_tail(&t[0].link, &m.transfers);

t[1].rx_buf = buf;
t[1].len = len;
-
- m.transfers = t;
- m.n_transfer = 2;
+ list_add_tail(&t[1].link, &m.transfers);

spi_sync(flash->spi, &m);

@@ -295,7 +294,7 @@
struct m25p *flash = mtd_to_m25p(mtd);
u32 page_offset, page_size;
struct spi_transfer t[2];
- struct spi_message m;
+ DECLARE_SPI_MESSAGE(m);

DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %z\n", spi->dev.bus_id,
__FUNCTION__, "to", (u32) to, len);
@@ -326,12 +325,12 @@
flash->command[2] = to >> 8;
flash->command[3] = to;

+ list_add_tail(&t[0].link, &m.transfers);
+ list_add_tail(&t[1].link, &m.transfers);
+
t[0].tx_buf = flash->command;
t[0].len = sizeof(flash->command);

- m.transfers = t;
- m.n_transfer = 2;
-
/* what page do we start with? */
page_offset = to % FLASH_PAGESIZE;

diff -uNr --exclude='patches*' --exclude=.pc linux-2.6.orig/drivers/mtd/devices/mtd_dataflash.c linux-2.6/drivers/mtd/devices/mtd_dataflash.c
--- linux-2.6.orig/drivers/mtd/devices/mtd_dataflash.c 2005-12-23 13:46:16.000000000 +0300
+++ linux-2.6/drivers/mtd/devices/mtd_dataflash.c 2005-12-23 15:31:14.000000000 +0300
@@ -147,8 +147,8 @@
{
struct dataflash *priv = (struct dataflash *)mtd->priv;
struct spi_device *spi = priv->spi;
- struct spi_transfer x[1] = { { .tx_dma = 0, }, };
- struct spi_message msg;
+ struct spi_transfer x = { .tx_dma = 0, };
+ DECLARE_SPI_MESSAGE(msg);
unsigned blocksize = priv->page_size << 3;
u8 *command;

@@ -162,10 +162,9 @@
|| (instr->addr % priv->page_size) != 0)
return -EINVAL;

- x[0].tx_buf = command = priv->command;
- x[0].len = 4;
- msg.transfers = x;
- msg.n_transfer = 1;
+ x.tx_buf = command = priv->command;
+ x.len = 4;
+ list_add_tail(&x.link, &msg.transfers);

down(&priv->lock);
while (instr->len > 0) {
@@ -231,7 +230,7 @@
{
struct dataflash *priv = (struct dataflash *)mtd->priv;
struct spi_transfer x[2] = { { .tx_dma = 0, }, };
- struct spi_message msg;
+ DECLARE_SPI_MESSAGE(msg);
unsigned int addr;
u8 *command;
int status;
@@ -258,10 +257,10 @@

x[0].tx_buf = command;
x[0].len = 8;
+ list_add_tail(&x[0].link, &msg.transfers);
x[1].rx_buf = buf;
x[1].len = len;
- msg.transfers = x;
- msg.n_transfer = 2;
+ list_add_tail(&x[1].link, &msg.transfers);

down(&priv->lock);

@@ -302,7 +301,7 @@
struct dataflash *priv = (struct dataflash *)mtd->priv;
struct spi_device *spi = priv->spi;
struct spi_transfer x[2] = { { .tx_dma = 0, }, };
- struct spi_message msg;
+ DECLARE_SPI_MESSAGE(msg);
unsigned int pageaddr, addr, offset, writelen;
size_t remaining = len;
u_char *writebuf = (u_char *) buf;
@@ -320,9 +319,10 @@
if ((to + len) > mtd->size)
return -EINVAL;

+ list_add_tail(&x[0].link, &msg.transfers);
+
x[0].tx_buf = command = priv->command;
x[0].len = 4;
- msg.transfers = x;

pageaddr = ((unsigned)to / priv->page_size);
offset = ((unsigned)to % priv->page_size);
@@ -364,7 +364,6 @@
DEBUG(MTD_DEBUG_LEVEL3, "TRANSFER: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);

- msg.n_transfer = 1;
status = spi_sync(spi, &msg);
if (status < 0)
DEBUG(MTD_DEBUG_LEVEL1, "%s: xfer %u -> %d \n",
@@ -385,8 +384,9 @@

x[1].tx_buf = writebuf;
x[1].len = writelen;
- msg.n_transfer = 2;
+ list_add_tail(&x[1].link, &msg.transfers);
status = spi_sync(spi, &msg);
+ list_del(&x[1].link);
if (status < 0)
DEBUG(MTD_DEBUG_LEVEL1, "%s: pgm %u/%u -> %d \n",
spi->dev.bus_id, addr, writelen, status);
@@ -405,7 +405,6 @@
DEBUG(MTD_DEBUG_LEVEL3, "COMPARE: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);

- msg.n_transfer = 1;
status = spi_sync(spi, &msg);
if (status < 0)
DEBUG(MTD_DEBUG_LEVEL1, "%s: compare %u -> %d \n",
diff -uNr --exclude='patches*' --exclude=.pc linux-2.6.orig/drivers/spi/spi_bitbang.c linux-2.6/drivers/spi/spi_bitbang.c
--- linux-2.6.orig/drivers/spi/spi_bitbang.c 2005-12-23 13:46:16.000000000 +0300
+++ linux-2.6/drivers/spi/spi_bitbang.c 2005-12-23 16:13:35.000000000 +0300
@@ -242,7 +242,7 @@
struct spi_message *m;
struct spi_device *spi;
unsigned nsecs;
- struct spi_transfer *t;
+ struct spi_transfer *t = NULL;
unsigned tmp;
unsigned chipselect;
int status;
@@ -259,12 +259,11 @@
nsecs = 100;

spi = m->spi;
- t = m->transfers;
tmp = 0;
chipselect = 0;
status = 0;

- for (;;t++) {
+ list_for_each_entry (t, &m->transfers, link) {
if (bitbang->shutdown) {
status = -ESHUTDOWN;
break;
@@ -295,10 +294,6 @@
if (t->delay_usecs)
udelay(t->delay_usecs);

- tmp++;
- if (tmp >= m->n_transfer)
- break;
-
chipselect = !t->cs_change;
if (chipselect)
continue;
@@ -308,8 +303,8 @@
msleep(1);
}

- tmp = m->n_transfer - 1;
- tmp = m->transfers[tmp].cs_change;
+ if (t)
+ tmp = t->cs_change;

m->status = status;
m->complete(m->context);
diff -uNr --exclude='patches*' --exclude=.pc linux-2.6.orig/drivers/spi/spi.c linux-2.6/drivers/spi/spi.c
--- linux-2.6.orig/drivers/spi/spi.c 2005-12-23 13:46:16.000000000 +0300
+++ linux-2.6/drivers/spi/spi.c 2005-12-23 15:28:58.000000000 +0300
@@ -546,7 +546,7 @@
static DECLARE_MUTEX(lock);

int status;
- struct spi_message message;
+ DECLARE_SPI_MESSAGE(message);
struct spi_transfer x[2];
u8 *local_buf;

@@ -570,13 +570,13 @@
memcpy(local_buf, txbuf, n_tx);
x[0].tx_buf = local_buf;
x[0].len = n_tx;
+ list_add_tail(&x[0].link, &message.transfers);

x[1].rx_buf = local_buf + n_tx;
x[1].len = n_rx;
+ list_add_tail(&x[1].link, &message.transfers);

/* do the i/o */
- message.transfers = x;
- message.n_transfer = ARRAY_SIZE(x);
status = spi_sync(spi, &message);
if (status == 0) {
memcpy(rxbuf, x[1].rx_buf, n_rx);
diff -uNr --exclude='patches*' --exclude=.pc linux-2.6.orig/include/linux/spi/spi.h linux-2.6/include/linux/spi/spi.h
--- linux-2.6.orig/include/linux/spi/spi.h 2005-12-23 13:46:16.000000000 +0300
+++ linux-2.6/include/linux/spi/spi.h 2005-12-23 15:34:21.000000000 +0300
@@ -316,12 +316,13 @@

unsigned cs_change:1;
u16 delay_usecs;
+
+ struct list_head link;
};

/**
* struct spi_message - one multi-segment SPI transaction
* @transfers: the segements of the transaction
- * @n_transfer: how many segments
* @spi: SPI device to which the transaction is queued
* @is_dma_mapped: if true, the caller provided both dma and cpu virtual
* addresses for each transfer buffer
@@ -339,8 +340,7 @@
* insulate against future API updates.
*/
struct spi_message {
- struct spi_transfer *transfers;
- unsigned n_transfer;
+ struct list_head transfers;

struct spi_device *spi;

@@ -371,6 +371,9 @@
void *state;
};

+#define DECLARE_SPI_MESSAGE(mmm) struct spi_message mmm = \
+ { .transfers = LIST_HEAD_INIT((mmm).transfers), }
+
/* It's fine to embed message and transaction structures in other data
* structures so long as you don't free them while they're in use.
*/
@@ -383,8 +386,12 @@
+ ntrans * sizeof(struct spi_transfer),
flags);
if (m) {
- m->transfers = (void *)(m + 1);
- m->n_transfer = ntrans;
+ int i;
+ struct spi_transfer *t = (struct spi_transfer *)(m + 1);
+
+ INIT_LIST_HEAD(&m->transfers);
+ for (i = 0; i < ntrans; i++)
+ list_add_tail(&t[i].link, &m->transfers);
}
return m;
}
@@ -472,10 +479,9 @@
.len = len,
.cs_change = 0,
};
- struct spi_message m;
+ DECLARE_SPI_MESSAGE(m);

- m.transfers = &t;
- m.n_transfer = 1;
+ list_add_tail(&t.link, &m.transfers);
return spi_sync(spi, &m);
}

@@ -497,10 +503,9 @@
.len = len,
.cs_change = 0,
};
- struct spi_message m;
+ DECLARE_SPI_MESSAGE(m);

- m.transfers = &t;
- m.n_transfer = 1;
+ list_add_tail(&t.link, &m.transfers);
return spi_sync(spi, &m);
}


2005-12-27 17:28:05

by David Brownell

[permalink] [raw]
Subject: Re: [PATCH] SPI: turn transfers from arrays to linked lists

On Friday 23 December 2005 6:06 am, Vitaly Wool wrote:
> Hi,
>
> the patch inlined is changing the SPI core and its users to have
> transfers in the SPI message structure as linked list not as an array,

It basically looks OK. I've updated it a bit, and will include an
updated version in any future updates of mine. (Right now I have
to merge your second version with my updated one ... )

So we'll be well on the way to agreeing on a single SPI framework
to evolve and integrate against.


> - req->msg.transfers = req->xfer;
> - req->msg.n_transfer = 6;
> + for (i = 0; i < 6; i++)
> + list_add_tail(&req->xfer[i].link, &req->msg.transfers);

More expensive, but not in a way that will often matter much. That
one's part of one-time init, for example. And the attached code
uses the spi_message_add_tail(transfer, message) you had mentioned.


> + DECLARE_SPI_MESSAGE(m);

The updated code has only an spi_message_init() function.


>
> - for (;;t++) {
> + list_for_each_entry (t, &m->transfers, link) {...}

...

> - tmp = m->n_transfer - 1;
> - tmp = m->transfers[tmp].cs_change;
> + if (t)
> + tmp = t->cs_change;

Bug: "t" is non-null but invalid there. (Fixed.)

2006-01-07 03:26:15

by David Brownell

[permalink] [raw]
Subject: Re: [PATCH] SPI: turn transfers from arrays to linked lists

OK, I'll be passing this version along with future versions;
appropriate for merging into -mm I think. This does change
how ever driver hooks things up, but those changes should be
easy to get right.

- Dave



This makes the SPI core and its users access transfers in the SPI message
structure as linked list not as an array, as discussed on LKML.

Signed-off-by: Vitaly Wool <[email protected]>
Signed-off-by: Dmitry Pervushin <[email protected]>


Minor updates including doc, bugfixes to the list code, and adding
spi_message_add_tail(). Plus, initialize things _before_ grabbing the
locks in some cases (in case it grows more expensive).

Signed-off-by: David Brownell <[email protected]>

---
Documentation/spi/spi-summary | 13 +++++
drivers/input/touchscreen/ads7846.c | 12 +++--
drivers/mtd/devices/m25p80.c | 50 ++++++++++----------
drivers/mtd/devices/mtd_dataflash.c | 28 +++++++----
drivers/spi/spi.c | 18 ++++---
drivers/spi/spi_bitbang.c | 65 ++++++++++++++++-----------
include/linux/spi/spi.h | 86 +++++++++++++++++++++++++-----------
7 files changed, 175 insertions(+), 97 deletions(-)


--- g26.orig/include/linux/spi/spi.h 2006-01-03 19:06:19.000000000 -0800
+++ g26/include/linux/spi/spi.h 2006-01-03 19:44:15.000000000 -0800
@@ -263,15 +263,16 @@ extern struct spi_master *spi_busnum_to_

/**
* struct spi_transfer - a read/write buffer pair
- * @tx_buf: data to be written (dma-safe address), or NULL
- * @rx_buf: data to be read (dma-safe address), or NULL
- * @tx_dma: DMA address of buffer, if spi_message.is_dma_mapped
- * @rx_dma: DMA address of buffer, if spi_message.is_dma_mapped
+ * @tx_buf: data to be written (dma-safe memory), or NULL
+ * @rx_buf: data to be read (dma-safe memory), or NULL
+ * @tx_dma: DMA address of tx_buf, if spi_message.is_dma_mapped
+ * @rx_dma: DMA address of rx_buf, if spi_message.is_dma_mapped
* @len: size of rx and tx buffers (in bytes)
* @cs_change: affects chipselect after this transfer completes
* @delay_usecs: microseconds to delay after this transfer before
* (optionally) changing the chipselect status, then starting
* the next transfer or completing this spi_message.
+ * @transfer_list: transfers are sequenced through spi_message.transfers
*
* SPI transfers always write the same number of bytes as they read.
* Protocol drivers should always provide rx_buf and/or tx_buf.
@@ -279,11 +280,16 @@ extern struct spi_master *spi_busnum_to_
* the data being transferred; that may reduce overhead, when the
* underlying driver uses dma.
*
- * All SPI transfers start with the relevant chipselect active. Drivers
- * can change behavior of the chipselect after the transfer finishes
- * (including any mandatory delay). The normal behavior is to leave it
- * selected, except for the last transfer in a message. Setting cs_change
- * allows two additional behavior options:
+ * If the transmit buffer is null, undefined data will be shifted out
+ * while filling rx_buf. If the receive buffer is null, the data
+ * shifted in will be discarded. Only "len" bytes shift out (or in).
+ * It's an error to try to shift out a partial word. (For example, by
+ * shifting out three bytes with word size of sixteen or twenty bits;
+ * the former uses two bytes per word, the latter uses four bytes.)
+ *
+ * All SPI transfers start with the relevant chipselect active. Normally
+ * it stays selected until after the last transfer in a message. Drivers
+ * can affect the chipselect signal using cs_change:
*
* (i) If the transfer isn't the last one in the message, this flag is
* used to make the chipselect briefly go inactive in the middle of the
@@ -299,7 +305,8 @@ extern struct spi_master *spi_busnum_to_
* The code that submits an spi_message (and its spi_transfers)
* to the lower layers is responsible for managing its memory.
* Zero-initialize every field you don't set up explicitly, to
- * insulate against future API updates.
+ * insulate against future API updates. After you submit a message
+ * and its transfers, ignore them until its completion callback.
*/
struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
@@ -316,12 +323,13 @@ struct spi_transfer {

unsigned cs_change:1;
u16 delay_usecs;
+
+ struct list_head transfer_list;
};

/**
* struct spi_message - one multi-segment SPI transaction
- * @transfers: the segements of the transaction
- * @n_transfer: how many segments
+ * @transfers: list of transfer segments in this transaction
* @spi: SPI device to which the transaction is queued
* @is_dma_mapped: if true, the caller provided both dma and cpu virtual
* addresses for each transfer buffer
@@ -333,14 +341,22 @@ struct spi_transfer {
* @queue: for use by whichever driver currently owns the message
* @state: for use by whichever driver currently owns the message
*
+ * An spi_message is used to execute an atomic sequence of data transfers,
+ * each represented by a struct spi_transfer. The sequence is "atomic"
+ * in the sense that no other spi_message may use that SPI bus until that
+ * sequence completes. On some systems, many such sequences can execute as
+ * as single programmed DMA transfer. On all systems, these messages are
+ * queued, and might complete after transactions to other devices. Messages
+ * sent to a given spi_device are alway executed in FIFO order.
+ *
* The code that submits an spi_message (and its spi_transfers)
* to the lower layers is responsible for managing its memory.
* Zero-initialize every field you don't set up explicitly, to
- * insulate against future API updates.
+ * insulate against future API updates. After you submit a message
+ * and its transfers, ignore them until its completion callback.
*/
struct spi_message {
- struct spi_transfer *transfers;
- unsigned n_transfer;
+ struct list_head transfers;

struct spi_device *spi;

@@ -371,6 +387,24 @@ struct spi_message {
void *state;
};

+static inline void spi_message_init(struct spi_message *m)
+{
+ memset(m, 0, sizeof *m);
+ INIT_LIST_HEAD(&m->transfers);
+}
+
+static inline void
+spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
+{
+ list_add_tail(&t->transfer_list, &m->transfers);
+}
+
+static inline void
+spi_transfer_del(struct spi_transfer *t)
+{
+ list_del(&t->transfer_list);
+}
+
/* It's fine to embed message and transaction structures in other data
* structures so long as you don't free them while they're in use.
*/
@@ -383,8 +417,12 @@ static inline struct spi_message *spi_me
+ ntrans * sizeof(struct spi_transfer),
flags);
if (m) {
- m->transfers = (void *)(m + 1);
- m->n_transfer = ntrans;
+ int i;
+ struct spi_transfer *t = (struct spi_transfer *)(m + 1);
+
+ INIT_LIST_HEAD(&m->transfers);
+ for (i = 0; i < ntrans; i++, t++)
+ spi_message_add_tail(t, m);
}
return m;
}
@@ -402,6 +440,8 @@ static inline void spi_message_free(stru
* device doesn't work with the mode 0 default. They may likewise need
* to update clock rates or word sizes from initial values. This function
* changes those settings, and must be called from a context that can sleep.
+ * The changes take effect the next time the device is selected and data
+ * is transferred to or from it.
*/
static inline int
spi_setup(struct spi_device *spi)
@@ -468,14 +508,12 @@ spi_write(struct spi_device *spi, const
{
struct spi_transfer t = {
.tx_buf = buf,
- .rx_buf = NULL,
.len = len,
- .cs_change = 0,
};
struct spi_message m;

- m.transfers = &t;
- m.n_transfer = 1;
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}

@@ -492,15 +530,13 @@ static inline int
spi_read(struct spi_device *spi, u8 *buf, size_t len)
{
struct spi_transfer t = {
- .tx_buf = NULL,
.rx_buf = buf,
.len = len,
- .cs_change = 0,
};
struct spi_message m;

- m.transfers = &t;
- m.n_transfer = 1;
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}

--- g26.orig/drivers/spi/spi.c 2006-01-03 19:06:19.000000000 -0800
+++ g26/drivers/spi/spi.c 2006-01-03 19:55:35.000000000 -0800
@@ -557,6 +557,17 @@ int spi_write_then_read(struct spi_devic
if ((n_tx + n_rx) > SPI_BUFSIZ)
return -EINVAL;

+ spi_message_init(&message);
+ memset(x, 0, sizeof x);
+ if (n_tx) {
+ x[0].len = n_tx;
+ spi_message_add_tail(&x[0], &message);
+ }
+ if (n_rx) {
+ x[1].len = n_rx;
+ spi_message_add_tail(&x[1], &message);
+ }
+
/* ... unless someone else is using the pre-allocated buffer */
if (down_trylock(&lock)) {
local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
@@ -565,18 +576,11 @@ int spi_write_then_read(struct spi_devic
} else
local_buf = buf;

- memset(x, 0, sizeof x);
-
memcpy(local_buf, txbuf, n_tx);
x[0].tx_buf = local_buf;
- x[0].len = n_tx;
-
x[1].rx_buf = local_buf + n_tx;
- x[1].len = n_rx;

/* do the i/o */
- message.transfers = x;
- message.n_transfer = ARRAY_SIZE(x);
status = spi_sync(spi, &message);
if (status == 0) {
memcpy(rxbuf, x[1].rx_buf, n_rx);
--- g26.orig/drivers/input/touchscreen/ads7846.c 2006-01-03 19:06:19.000000000 -0800
+++ g26/drivers/input/touchscreen/ads7846.c 2006-01-03 19:11:20.000000000 -0800
@@ -155,10 +155,13 @@ static int ads7846_read12_ser(struct dev
struct ser_req *req = kzalloc(sizeof *req, SLAB_KERNEL);
int status;
int sample;
+ int i;

if (!req)
return -ENOMEM;

+ INIT_LIST_HEAD(&req->msg.transfers);
+
/* activate reference, so it has time to settle; */
req->xfer[0].tx_buf = &ref_on;
req->xfer[0].len = 1;
@@ -192,8 +195,8 @@ static int ads7846_read12_ser(struct dev
/* group all the transfers together, so we can't interfere with
* reading touchscreen state; disable penirq while sampling
*/
- req->msg.transfers = req->xfer;
- req->msg.n_transfer = 6;
+ for (i = 0; i < 6; i++)
+ spi_message_add_tail(&req->xfer[i], &req->msg);

disable_irq(spi->irq);
status = spi_sync(spi, &req->msg);
@@ -398,6 +401,7 @@ static int __devinit ads7846_probe(struc
struct ads7846 *ts;
struct ads7846_platform_data *pdata = spi->dev.platform_data;
struct spi_transfer *x;
+ int i;

if (!spi->irq) {
dev_dbg(&spi->dev, "no IRQ?\n");
@@ -500,8 +504,8 @@ static int __devinit ads7846_probe(struc

CS_CHANGE(x[-1]);

- ts->msg.transfers = ts->xfer;
- ts->msg.n_transfer = x - ts->xfer;
+ for (i = 0; i < x - ts->xfer; i++)
+ spi_message_add_tail(&ts->xfer[i], &ts->msg);
ts->msg.complete = ads7846_rx;
ts->msg.context = ts;

--- g26.orig/drivers/mtd/devices/m25p80.c 2006-01-03 19:06:19.000000000 -0800
+++ g26/drivers/mtd/devices/m25p80.c 2006-01-03 19:43:09.000000000 -0800
@@ -243,6 +243,21 @@ static int m25p80_read(struct mtd_info *
if (from + len > flash->mtd.size)
return -EINVAL;

+ spi_message_init(&m);
+ memset(t, 0, (sizeof t));
+
+ t[0].tx_buf = flash->command;
+ t[0].len = sizeof(flash->command);
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].rx_buf = buf;
+ t[1].len = len;
+ spi_message_add_tail(&t[1], &m);
+
+ /* Byte count starts at zero. */
+ if (retlen)
+ *retlen = 0;
+
down(&flash->lock);

/* Wait till previous write/erase is done. */
@@ -252,8 +267,6 @@ static int m25p80_read(struct mtd_info *
return 1;
}

- memset(t, 0, (sizeof t));
-
/* NOTE: OPCODE_FAST_READ (if available) is faster... */

/* Set up the write data buffer. */
@@ -262,19 +275,6 @@ static int m25p80_read(struct mtd_info *
flash->command[2] = from >> 8;
flash->command[3] = from;

- /* Byte count starts at zero. */
- if (retlen)
- *retlen = 0;
-
- t[0].tx_buf = flash->command;
- t[0].len = sizeof(flash->command);
-
- t[1].rx_buf = buf;
- t[1].len = len;
-
- m.transfers = t;
- m.n_transfer = 2;
-
spi_sync(flash->spi, &m);

*retlen = m.actual_length - sizeof(flash->command);
@@ -310,6 +310,16 @@ static int m25p80_write(struct mtd_info
if (to + len > flash->mtd.size)
return -EINVAL;

+ spi_message_init(&m);
+ memset(t, 0, (sizeof t));
+
+ t[0].tx_buf = flash->command;
+ t[0].len = sizeof(flash->command);
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = buf;
+ spi_message_add_tail(&t[1], &m);
+
down(&flash->lock);

/* Wait until finished previous write command. */
@@ -318,26 +328,17 @@ static int m25p80_write(struct mtd_info

write_enable(flash);

- memset(t, 0, (sizeof t));
-
/* Set up the opcode in the write buffer. */
flash->command[0] = OPCODE_PP;
flash->command[1] = to >> 16;
flash->command[2] = to >> 8;
flash->command[3] = to;

- t[0].tx_buf = flash->command;
- t[0].len = sizeof(flash->command);
-
- m.transfers = t;
- m.n_transfer = 2;
-
/* what page do we start with? */
page_offset = to % FLASH_PAGESIZE;

/* do all the bytes fit onto one page? */
if (page_offset + len <= FLASH_PAGESIZE) {
- t[1].tx_buf = buf;
t[1].len = len;

spi_sync(flash->spi, &m);
@@ -349,7 +350,6 @@ static int m25p80_write(struct mtd_info
/* the size of data remaining on the first page */
page_size = FLASH_PAGESIZE - page_offset;

- t[1].tx_buf = buf;
t[1].len = page_size;
spi_sync(flash->spi, &m);

--- g26.orig/drivers/mtd/devices/mtd_dataflash.c 2006-01-03 19:06:19.000000000 -0800
+++ g26/drivers/mtd/devices/mtd_dataflash.c 2006-01-03 19:23:13.000000000 -0800
@@ -147,7 +147,7 @@ static int dataflash_erase(struct mtd_in
{
struct dataflash *priv = (struct dataflash *)mtd->priv;
struct spi_device *spi = priv->spi;
- struct spi_transfer x[1] = { { .tx_dma = 0, }, };
+ struct spi_transfer x = { .tx_dma = 0, };
struct spi_message msg;
unsigned blocksize = priv->page_size << 3;
u8 *command;
@@ -162,10 +162,11 @@ static int dataflash_erase(struct mtd_in
|| (instr->addr % priv->page_size) != 0)
return -EINVAL;

- x[0].tx_buf = command = priv->command;
- x[0].len = 4;
- msg.transfers = x;
- msg.n_transfer = 1;
+ spi_message_init(&msg);
+
+ x.tx_buf = command = priv->command;
+ x.len = 4;
+ spi_message_add_tail(&x, &msg);

down(&priv->lock);
while (instr->len > 0) {
@@ -256,12 +257,15 @@ static int dataflash_read(struct mtd_inf
DEBUG(MTD_DEBUG_LEVEL3, "READ: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);

+ spi_message_init(&msg);
+
x[0].tx_buf = command;
x[0].len = 8;
+ spi_message_add_tail(&x[0], &msg);
+
x[1].rx_buf = buf;
x[1].len = len;
- msg.transfers = x;
- msg.n_transfer = 2;
+ spi_message_add_tail(&x[1], &msg);

down(&priv->lock);

@@ -320,9 +324,11 @@ static int dataflash_write(struct mtd_in
if ((to + len) > mtd->size)
return -EINVAL;

+ spi_message_init(&msg);
+
x[0].tx_buf = command = priv->command;
x[0].len = 4;
- msg.transfers = x;
+ spi_message_add_tail(&x[0], &msg);

pageaddr = ((unsigned)to / priv->page_size);
offset = ((unsigned)to % priv->page_size);
@@ -364,7 +370,6 @@ static int dataflash_write(struct mtd_in
DEBUG(MTD_DEBUG_LEVEL3, "TRANSFER: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);

- msg.n_transfer = 1;
status = spi_sync(spi, &msg);
if (status < 0)
DEBUG(MTD_DEBUG_LEVEL1, "%s: xfer %u -> %d \n",
@@ -385,14 +390,16 @@ static int dataflash_write(struct mtd_in

x[1].tx_buf = writebuf;
x[1].len = writelen;
- msg.n_transfer = 2;
+ spi_message_add_tail(x + 1, &msg);
status = spi_sync(spi, &msg);
+ spi_transfer_del(x + 1);
if (status < 0)
DEBUG(MTD_DEBUG_LEVEL1, "%s: pgm %u/%u -> %d \n",
spi->dev.bus_id, addr, writelen, status);

(void) dataflash_waitready(priv->spi);

+
#ifdef CONFIG_DATAFLASH_WRITE_VERIFY

/* (3) Compare to Buffer1 */
@@ -405,7 +412,6 @@ static int dataflash_write(struct mtd_in
DEBUG(MTD_DEBUG_LEVEL3, "COMPARE: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);

- msg.n_transfer = 1;
status = spi_sync(spi, &msg);
if (status < 0)
DEBUG(MTD_DEBUG_LEVEL1, "%s: compare %u -> %d \n",
--- g26.orig/drivers/spi/spi_bitbang.c 2006-01-03 19:06:19.000000000 -0800
+++ g26/drivers/spi/spi_bitbang.c 2006-01-03 19:44:50.000000000 -0800
@@ -242,9 +242,9 @@ static void bitbang_work(void *_bitbang)
struct spi_message *m;
struct spi_device *spi;
unsigned nsecs;
- struct spi_transfer *t;
+ struct spi_transfer *t = NULL;
unsigned tmp;
- unsigned chipselect;
+ unsigned cs_change;
int status;

m = container_of(bitbang->queue.next, struct spi_message,
@@ -259,30 +259,44 @@ static void bitbang_work(void *_bitbang)
nsecs = 100;

spi = m->spi;
- t = m->transfers;
tmp = 0;
- chipselect = 0;
+ cs_change = 1;
status = 0;

- for (;;t++) {
+ list_for_each_entry (t, &m->transfers, transfer_list) {
if (bitbang->shutdown) {
status = -ESHUTDOWN;
break;
}

- /* set up default clock polarity, and activate chip */
- if (!chipselect) {
+ /* 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 */
- if (t->len)
+ /* 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 != t->len) {
if (status > 0)
status = -EMSGSIZE;
@@ -295,30 +309,31 @@ static void bitbang_work(void *_bitbang)
if (t->delay_usecs)
udelay(t->delay_usecs);

- tmp++;
- if (tmp >= m->n_transfer)
- break;
-
- chipselect = !t->cs_change;
- if (chipselect)
+ 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);
-
- msleep(1);
+ ndelay(nsecs);
}

- tmp = m->n_transfer - 1;
- tmp = m->transfers[tmp].cs_change;
-
m->status = status;
m->complete(m->context);

- ndelay(2 * nsecs);
- bitbang->chipselect(spi, (status == 0 && tmp)
- ? BITBANG_CS_ACTIVE
- : BITBANG_CS_INACTIVE);
- ndelay(nsecs);
+ /* 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);
+ }

spin_lock_irqsave(&bitbang->lock, flags);
}
--- g26.orig/Documentation/spi/spi-summary 2006-01-03 19:06:19.000000000 -0800
+++ g26/Documentation/spi/spi-summary 2006-01-03 19:06:19.000000000 -0800
@@ -114,6 +114,9 @@ shows up in sysfs in several locations:
/sys/devices/.../CTLR/spiB.C ... spi_device for on bus "B",
chipselect C, accessed through CTLR.

+ /sys/devices/.../CTLR/spiB.C/modalias ... identifies the driver
+ that should be used with this device (for hotplug/coldplug)
+
/sys/bus/spi/devices/spiB.C ... symlink to the physical
spiB-C device

@@ -246,6 +249,12 @@ driver is registered:

Like with other static board-specific setup, you won't unregister those.

+The widely used "card" style computers bundle memory, cpu, and little else
+onto a card that's maybe just thirty square centimeters. On such systems,
+your arch/.../mach-.../board-*.c file would primarily provide information
+about the devices on the mainboard into which such a card is plugged. That
+certainly includes SPI devices hooked up through the card connectors!
+

NON-STATIC CONFIGURATIONS

@@ -257,6 +266,10 @@ up the spi bus master, and will likely n
board info based on the board that was hotplugged. Of course, you'd later
call at least spi_unregister_device() when that board is removed.

+When Linux includes support for MMC/SD/SDIO/DataFlash cards through SPI, those
+configurations will also be dynamic. Fortunately, those devices all support
+basic device identification probes, so that support should hotplug normally.
+

How do I write an "SPI Protocol Driver"?
----------------------------------------