diff options
-rw-r--r-- | arch/arm/mach-s3c2410/include/mach/spi-gpio.h | 1 | ||||
-rw-r--r-- | drivers/spi/spi_bitbang.c | 231 | ||||
-rw-r--r-- | drivers/spi/spi_s3c24xx_gpio.c | 1 | ||||
-rw-r--r-- | include/linux/spi/spi.h | 30 | ||||
-rw-r--r-- | include/linux/spi/spi_bitbang.h | 5 |
5 files changed, 159 insertions, 109 deletions
diff --git a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h index 2d229da0e55..2506b4ca028 100644 --- a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h +++ b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h @@ -21,6 +21,7 @@ struct s3c2410_spigpio_info { int num_chipselect; int bus_num; + int non_blocking_transfer; /* * FIXME: board_size and board_info DO NOT belong here. * These were already removed upstream... but we still rely on them diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 85e61f45121..ab40b8ddf3f 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -264,6 +264,123 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) * Drivers can provide word-at-a-time i/o primitives, or provide * transfer-at-a-time ones to leverage dma or fifo hardware. */ + +/* Synchronous non blocking 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_transfer *t; + unsigned long flags; + int cs_change = 1; + int status; + int nsecs; + int (*setup_transfer)(struct spi_device *, struct spi_transfer *); + + /* 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; + cs_change = 1; + status = 0; + setup_transfer = NULL; + + local_irq_save(flags); + 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; + if (m->complete) + m->complete(m->context); + + /* 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); + } + + local_irq_restore(flags); + + return status; +} +EXPORT_SYMBOL_GPL(spi_bitbang_transfer_sync); + static void bitbang_work(struct work_struct *work) { struct spi_bitbang *bitbang = @@ -274,120 +391,13 @@ static void bitbang_work(struct work_struct *work) bitbang->busy = 1; while (!list_empty(&bitbang->queue)) { struct spi_message *m; - 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 *); m = container_of(bitbang->queue.next, struct spi_message, queue); list_del_init(&m->queue); - spin_unlock_irqrestore(&bitbang->lock, flags); - - /* 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; - 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; - m->complete(m->context); - - /* 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); - } + spin_unlock_irqrestore(&bitbang->lock, flags); + spi_bitbang_transfer_sync(m->spi, m); spin_lock_irqsave(&bitbang->lock, flags); } bitbang->busy = 0; @@ -459,6 +469,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) if (!bitbang->master->transfer) bitbang->master->transfer = spi_bitbang_transfer; + if (!bitbang->master->transfer_sync && bitbang->non_blocking_transfer) + 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/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c index 700cdeccc61..582f20262a9 100644 --- a/drivers/spi/spi_s3c24xx_gpio.c +++ b/drivers/spi/spi_s3c24xx_gpio.c @@ -123,6 +123,7 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) sp->bitbang.master->bus_num = info->bus_num; sp->bitbang.master->num_chipselect = info->num_chipselect; sp->bitbang.chipselect = s3c2410_spigpio_chipselect; + sp->bitbang.non_blocking_transfer = info->non_blocking_transfer; sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0; sp->bitbang.txrx_word[SPI_MODE_1] = s3c2410_spigpio_txrx_mode1; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 68bb1c501d0..bf21ef63984 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -264,6 +264,13 @@ struct spi_master { int (*transfer)(struct spi_device *spi, struct spi_message *mesg); + /* + * Synchronous non blocking transfer function. Should guarantee + * data availability when it returns + */ + 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 +580,29 @@ spi_async(struct spi_device *spi, struct spi_message *message) return spi->master->transfer(spi, message); } +/** + * spi_non_blocking_transfer - Synchronous, non blocking transfer + * @spi: device with which data will be exchanged + * @message: describes the data transfers with optional completion handlers + * Context: any (irqs may be blocked, etc) + * + * Data is guaranteed to be written or read when this function returns. + * + * Note : This may not be supported by all spi masters. + */ + +static inline int +spi_non_blocking_transfer(struct spi_device *spi, struct spi_message *message) +{ + if (unlikely(!spi->master->transfer_sync)) { + dev_err(&spi->master->dev, + "non-blocking transfers not supported\n"); + return -EIO; + } + + return spi->master->transfer_sync(spi, message); +} + /*---------------------------------------------------------------------------*/ /* All these synchronous SPI transfer routines are utilities layered diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index bf8de281b4e..476a7afdc97 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -31,6 +31,9 @@ struct spi_bitbang { u8 use_dma; u8 flags; /* extra spi->mode support */ + /* Support for synchronous non blocking transfers */ + int non_blocking_transfer; + struct spi_master *master; /* setup_transfer() changes clock and/or wordsize to match settings @@ -62,6 +65,8 @@ extern void spi_bitbang_cleanup(struct spi_device *spi); extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m); extern int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t); +extern int spi_bitbang_transfer_sync(struct spi_device *spi, + struct spi_message *m); /* start or stop queue processing */ extern int spi_bitbang_start(struct spi_bitbang *spi); |