aboutsummaryrefslogtreecommitdiff
path: root/drivers/spi/spi_bitbang.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi_bitbang.c')
-rw-r--r--drivers/spi/spi_bitbang.c86
1 files changed, 49 insertions, 37 deletions
diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c
index 44aff198eb9..f037e559326 100644
--- a/drivers/spi/spi_bitbang.c
+++ b/drivers/spi/spi_bitbang.c
@@ -146,6 +146,9 @@ int spi_bitbang_setup(struct spi_device *spi)
struct spi_bitbang_cs *cs = spi->controller_state;
struct spi_bitbang *bitbang;
+ if (!spi->max_speed_hz)
+ return -EINVAL;
+
if (!cs) {
cs = kzalloc(sizeof *cs, SLAB_KERNEL);
if (!cs)
@@ -172,13 +175,8 @@ int spi_bitbang_setup(struct spi_device *spi)
if (!cs->txrx_word)
return -EINVAL;
- if (!spi->max_speed_hz)
- spi->max_speed_hz = 500 * 1000;
-
- /* nsecs = max(50, (clock period)/2), be optimistic */
+ /* nsecs = (clock period)/2 */
cs->nsecs = (1000000000/2) / (spi->max_speed_hz);
- if (cs->nsecs < 50)
- cs->nsecs = 50;
if (cs->nsecs > MAX_UDELAY_MS * 1000)
return -EINVAL;
@@ -194,7 +192,7 @@ int spi_bitbang_setup(struct spi_device *spi)
/* deselect chip (low or high) */
spin_lock(&bitbang->lock);
if (!bitbang->busy) {
- bitbang->chipselect(spi, 0);
+ bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(cs->nsecs);
}
spin_unlock(&bitbang->lock);
@@ -244,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,
@@ -254,37 +252,49 @@ static void bitbang_work(void *_bitbang)
list_del_init(&m->queue);
spin_unlock_irqrestore(&bitbang->lock, flags);
-// FIXME this is made-up
-nsecs = 100;
+ /* 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;
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) {
- bitbang->chipselect(spi, 1);
+ /* 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 */
+ /* transfer data. the lower level code handles any
+ * new dma mappings it needs. our caller always gave
+ * us dma-safe buffers.
+ */
if (t->len) {
- /* FIXME if bitbang->use_dma, dma_map_single()
- * before the transfer, and dma_unmap_single()
- * afterwards, for either or both buffers...
+ /* 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) {
@@ -299,29 +309,31 @@ nsecs = 100;
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;
- bitbang->chipselect(spi, 0);
-
- /* REVISIT do we want the udelay here instead? */
- msleep(1);
+ /* 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);
}
- 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);
- 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);
}