diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi-altera.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-ath79.c | 14 | ||||
-rw-r--r-- | drivers/spi/spi-atmel.c | 778 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-dspi.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-imx.c | 27 | ||||
-rw-r--r-- | drivers/spi/spi-pxa2xx.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-sh-hspi.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-sh-msiof.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi.c | 74 |
10 files changed, 357 insertions, 548 deletions
diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 595b62cb545d..5d7deaf62867 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -220,8 +220,6 @@ static int altera_spi_probe(struct platform_device *pdev) /* setup the state for the bitbang driver */ hw->bitbang.master = master; - if (!hw->bitbang.master) - return err; hw->bitbang.chipselect = altera_spi_chipsel; hw->bitbang.txrx_bufs = altera_spi_txrx; diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 821bf7ac218d..31534b51715a 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -243,21 +243,21 @@ static int ath79_spi_probe(struct platform_device *pdev) goto err_put_master; } - sp->base = ioremap(r->start, resource_size(r)); + sp->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); if (!sp->base) { ret = -ENXIO; goto err_put_master; } - sp->clk = clk_get(&pdev->dev, "ahb"); + sp->clk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sp->clk)) { ret = PTR_ERR(sp->clk); - goto err_unmap; + goto err_put_master; } ret = clk_enable(sp->clk); if (ret) - goto err_clk_put; + goto err_put_master; rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ); if (!rate) { @@ -280,10 +280,6 @@ err_disable: ath79_spi_disable(sp); err_clk_disable: clk_disable(sp->clk); -err_clk_put: - clk_put(sp->clk); -err_unmap: - iounmap(sp->base); err_put_master: spi_master_put(sp->bitbang.master); @@ -297,8 +293,6 @@ static int ath79_spi_remove(struct platform_device *pdev) spi_bitbang_stop(&sp->bitbang); ath79_spi_disable(sp); clk_disable(sp->clk); - clk_put(sp->clk); - iounmap(sp->base); spi_master_put(sp->bitbang.master); return 0; diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 273db0beb2b8..b0842f751016 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -189,6 +189,8 @@ */ #define DMA_MIN_BYTES 16 +#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) + struct atmel_spi_dma { struct dma_chan *chan_rx; struct dma_chan *chan_tx; @@ -220,17 +222,13 @@ struct atmel_spi { int irq; struct clk *clk; struct platform_device *pdev; - struct spi_device *stay; - u8 stopping; - struct list_head queue; - struct tasklet_struct tasklet; struct spi_transfer *current_transfer; unsigned long current_remaining_bytes; - struct spi_transfer *next_transfer; - unsigned long next_remaining_bytes; int done_status; + struct completion xfer_completion; + /* scratch buffer */ void *buffer; dma_addr_t buffer_dma; @@ -241,6 +239,9 @@ struct atmel_spi { bool use_pdc; /* dmaengine data */ struct atmel_spi_dma dma; + + bool keep_cs; + bool cs_active; }; /* Controller-specific per-slave state */ @@ -376,17 +377,6 @@ static inline bool atmel_spi_use_dma(struct atmel_spi *as, return as->use_dma && xfer->len >= DMA_MIN_BYTES; } -static inline int atmel_spi_xfer_is_last(struct spi_message *msg, - struct spi_transfer *xfer) -{ - return msg->transfers.prev == &xfer->transfer_list; -} - -static inline int atmel_spi_xfer_can_be_chained(struct spi_transfer *xfer) -{ - return xfer->delay_usecs == 0 && !xfer->cs_change; -} - static int atmel_spi_dma_slave_config(struct atmel_spi *as, struct dma_slave_config *slave_config, u8 bits_per_word) @@ -513,23 +503,20 @@ static void dma_callback(void *data) struct spi_master *master = data; struct atmel_spi *as = spi_master_get_devdata(master); - /* trigger SPI tasklet */ - tasklet_schedule(&as->tasklet); + complete(&as->xfer_completion); } /* * Next transfer using PIO. - * lock is held, spi tasklet is blocked */ static void atmel_spi_next_xfer_pio(struct spi_master *master, struct spi_transfer *xfer) { struct atmel_spi *as = spi_master_get_devdata(master); + unsigned long xfer_pos = xfer->len - as->current_remaining_bytes; dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_pio\n"); - as->current_remaining_bytes = xfer->len; - /* Make sure data is not remaining in RDR */ spi_readl(as, RDR); while (spi_readl(as, SR) & SPI_BIT(RDRF)) { @@ -537,13 +524,14 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master, cpu_relax(); } - if (xfer->tx_buf) + if (xfer->tx_buf) { if (xfer->bits_per_word > 8) - spi_writel(as, TDR, *(u16 *)(xfer->tx_buf)); + spi_writel(as, TDR, *(u16 *)(xfer->tx_buf + xfer_pos)); else - spi_writel(as, TDR, *(u8 *)(xfer->tx_buf)); - else + spi_writel(as, TDR, *(u8 *)(xfer->tx_buf + xfer_pos)); + } else { spi_writel(as, TDR, 0); + } dev_dbg(master->dev.parent, " start pio xfer %p: len %u tx %p rx %p bitpw %d\n", @@ -556,7 +544,6 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master, /* * Submit next transfer for DMA. - * lock is held, spi tasklet is blocked */ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, struct spi_transfer *xfer, @@ -694,74 +681,90 @@ static void atmel_spi_next_xfer_data(struct spi_master *master, *plen = len; } +static int atmel_spi_set_xfer_speed(struct atmel_spi *as, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + u32 scbr, csr; + unsigned long bus_hz; + + /* v1 chips start out at half the peripheral bus speed. */ + bus_hz = clk_get_rate(as->clk); + if (!atmel_spi_is_v2(as)) + bus_hz /= 2; + + /* + * Calculate the lowest divider that satisfies the + * constraint, assuming div32/fdiv/mbz == 0. + */ + if (xfer->speed_hz) + scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz); + else + /* + * This can happend if max_speed is null. + * In this case, we set the lowest possible speed + */ + scbr = 0xff; + + /* + * If the resulting divider doesn't fit into the + * register bitfield, we can't satisfy the constraint. + */ + if (scbr >= (1 << SPI_SCBR_SIZE)) { + dev_err(&spi->dev, + "setup: %d Hz too slow, scbr %u; min %ld Hz\n", + xfer->speed_hz, scbr, bus_hz/255); + return -EINVAL; + } + if (scbr == 0) { + dev_err(&spi->dev, + "setup: %d Hz too high, scbr %u; max %ld Hz\n", + xfer->speed_hz, scbr, bus_hz); + return -EINVAL; + } + csr = spi_readl(as, CSR0 + 4 * spi->chip_select); + csr = SPI_BFINS(SCBR, scbr, csr); + spi_writel(as, CSR0 + 4 * spi->chip_select, csr); + + return 0; +} + /* * Submit next transfer for PDC. * lock is held, spi irq is blocked */ static void atmel_spi_pdc_next_xfer(struct spi_master *master, - struct spi_message *msg) + struct spi_message *msg, + struct spi_transfer *xfer) { struct atmel_spi *as = spi_master_get_devdata(master); - struct spi_transfer *xfer; - u32 len, remaining; - u32 ieval; + u32 len; dma_addr_t tx_dma, rx_dma; - if (!as->current_transfer) - xfer = list_entry(msg->transfers.next, - struct spi_transfer, transfer_list); - else if (!as->next_transfer) - xfer = list_entry(as->current_transfer->transfer_list.next, - struct spi_transfer, transfer_list); - else - xfer = NULL; - - if (xfer) { - spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); - - len = xfer->len; - atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); - remaining = xfer->len - len; - - spi_writel(as, RPR, rx_dma); - spi_writel(as, TPR, tx_dma); - - if (msg->spi->bits_per_word > 8) - len >>= 1; - spi_writel(as, RCR, len); - spi_writel(as, TCR, len); - - dev_dbg(&msg->spi->dev, - " start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", - xfer, xfer->len, xfer->tx_buf, - (unsigned long long)xfer->tx_dma, xfer->rx_buf, - (unsigned long long)xfer->rx_dma); - } else { - xfer = as->next_transfer; - remaining = as->next_remaining_bytes; - } + spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); - as->current_transfer = xfer; - as->current_remaining_bytes = remaining; + len = as->current_remaining_bytes; + atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); + as->current_remaining_bytes -= len; - if (remaining > 0) - len = remaining; - else if (!atmel_spi_xfer_is_last(msg, xfer) - && atmel_spi_xfer_can_be_chained(xfer)) { - xfer = list_entry(xfer->transfer_list.next, - struct spi_transfer, transfer_list); - len = xfer->len; - } else - xfer = NULL; + spi_writel(as, RPR, rx_dma); + spi_writel(as, TPR, tx_dma); - as->next_transfer = xfer; + if (msg->spi->bits_per_word > 8) + len >>= 1; + spi_writel(as, RCR, len); + spi_writel(as, TCR, len); - if (xfer) { - u32 total; + dev_dbg(&msg->spi->dev, + " start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", + xfer, xfer->len, xfer->tx_buf, + (unsigned long long)xfer->tx_dma, xfer->rx_buf, + (unsigned long long)xfer->rx_dma); - total = len; + if (as->current_remaining_bytes) { + len = as->current_remaining_bytes; atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); - as->next_remaining_bytes = total - len; + as->current_remaining_bytes -= len; spi_writel(as, RNPR, rx_dma); spi_writel(as, TNPR, tx_dma); @@ -776,11 +779,6 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, xfer, xfer->len, xfer->tx_buf, (unsigned long long)xfer->tx_dma, xfer->rx_buf, (unsigned long long)xfer->rx_dma); - ieval = SPI_BIT(ENDRX) | SPI_BIT(OVRES); - } else { - spi_writel(as, RNCR, 0); - spi_writel(as, TNCR, 0); - ieval = SPI_BIT(RXBUFF) | SPI_BIT(ENDRX) | SPI_BIT(OVRES); } /* REVISIT: We're waiting for ENDRX before we start the next @@ -793,83 +791,11 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, * * It should be doable, though. Just not now... */ - spi_writel(as, IER, ieval); + spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES)); spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); } /* - * Choose way to submit next transfer and start it. - * lock is held, spi tasklet is blocked - */ -static void atmel_spi_dma_next_xfer(struct spi_master *master, - struct spi_message *msg) -{ - struct atmel_spi *as = spi_master_get_devdata(master); - struct spi_transfer *xfer; - u32 remaining, len; - - remaining = as->current_remaining_bytes; - if (remaining) { - xfer = as->current_transfer; - len = remaining; - } else { - if (!as->current_transfer) - xfer = list_entry(msg->transfers.next, - struct spi_transfer, transfer_list); - else - xfer = list_entry( - as->current_transfer->transfer_list.next, - struct spi_transfer, transfer_list); - - as->current_transfer = xfer; - len = xfer->len; - } - - if (atmel_spi_use_dma(as, xfer)) { - u32 total = len; - if (!atmel_spi_next_xfer_dma_submit(master, xfer, &len)) { - as->current_remaining_bytes = total - len; - return; - } else { - dev_err(&msg->spi->dev, "unable to use DMA, fallback to PIO\n"); - } - } - - /* use PIO if error appened using DMA */ - atmel_spi_next_xfer_pio(master, xfer); -} - -static void atmel_spi_next_message(struct spi_master *master) -{ - struct atmel_spi *as = spi_master_get_devdata(master); - struct spi_message *msg; - struct spi_device *spi; - - BUG_ON(as->current_transfer); - - msg = list_entry(as->queue.next, struct spi_message, queue); - spi = msg->spi; - - dev_dbg(master->dev.parent, "start message %p for %s\n", - msg, dev_name(&spi->dev)); - - /* select chip if it's not still active */ - if (as->stay) { - if (as->stay != spi) { - cs_deactivate(as, as->stay); - cs_activate(as, spi); - } - as->stay = NULL; - } else - cs_activate(as, spi); - - if (as->use_pdc) - atmel_spi_pdc_next_xfer(master, msg); - else - atmel_spi_dma_next_xfer(master, msg); -} - -/* * For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma: * - The buffer is either valid for CPU access, else NULL * - If the buffer is valid, so is its DMA address @@ -924,41 +850,7 @@ static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as) spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); } -static void -atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, - struct spi_message *msg, int stay) -{ - if (!stay || as->done_status < 0) - cs_deactivate(as, msg->spi); - else - as->stay = msg->spi; - - list_del(&msg->queue); - msg->status = as->done_status; - - dev_dbg(master->dev.parent, - "xfer complete: %u bytes transferred\n", - msg->actual_length); - - atmel_spi_unlock(as); - msg->complete(msg->context); - atmel_spi_lock(as); - - as->current_transfer = NULL; - as->next_transfer = NULL; - as->done_status = 0; - - /* continue if needed */ - if (list_empty(&as->queue) || as->stopping) { - if (as->use_pdc) - atmel_spi_disable_pdc_transfer(as); - } else { - atmel_spi_next_message(master); - } -} - /* Called from IRQ - * lock is held * * Must update "current_remaining_bytes" to keep track of data * to transfer. @@ -966,9 +858,7 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, static void atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) { - u8 *txp; u8 *rxp; - u16 *txp16; u16 *rxp16; unsigned long xfer_pos = xfer->len - as->current_remaining_bytes; @@ -990,96 +880,12 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) } else { as->current_remaining_bytes--; } - - if (as->current_remaining_bytes) { - if (xfer->tx_buf) { - if (xfer->bits_per_word > 8) { - txp16 = (u16 *)(((u8 *)xfer->tx_buf) - + xfer_pos + 2); - spi_writel(as, TDR, *txp16); - } else { - txp = ((u8 *)xfer->tx_buf) + xfer_pos + 1; - spi_writel(as, TDR, *txp); - } - } else { - spi_writel(as, TDR, 0); - } - } -} - -/* Tasklet - * Called from DMA callback + pio transfer and overrun IRQ. - */ -static void atmel_spi_tasklet_func(unsigned long data) -{ - struct spi_master *master = (struct spi_master *)data; - struct atmel_spi *as = spi_master_get_devdata(master); - struct spi_message *msg; - struct spi_transfer *xfer; - - dev_vdbg(master->dev.parent, "atmel_spi_tasklet_func\n"); - - atmel_spi_lock(as); - - xfer = as->current_transfer; - - if (xfer == NULL) - /* already been there */ - goto tasklet_out; - - msg = list_entry(as->queue.next, struct spi_message, queue); - - if (as->current_remaining_bytes == 0) { - if (as->done_status < 0) { - /* error happened (overrun) */ - if (atmel_spi_use_dma(as, xfer)) - atmel_spi_stop_dma(as); - } else { - /* only update length if no error */ - msg->actual_length += xfer->len; - } - - if (atmel_spi_use_dma(as, xfer)) - if (!msg->is_dma_mapped) - atmel_spi_dma_unmap_xfer(master, xfer); - - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); - - if (atmel_spi_xfer_is_last(msg, xfer) || as->done_status < 0) { - /* report completed (or erroneous) message */ - atmel_spi_msg_done(master, as, msg, xfer->cs_change); - } else { - if (xfer->cs_change) { - cs_deactivate(as, msg->spi); - udelay(1); - cs_activate(as, msg->spi); - } - - /* - * Not done yet. Submit the next transfer. - * - * FIXME handle protocol options for xfer - */ - atmel_spi_dma_next_xfer(master, msg); - } - } else { - /* - * Keep going, we still have data to send in - * the current transfer. - */ - atmel_spi_dma_next_xfer(master, msg); - } - -tasklet_out: - atmel_spi_unlock(as); } /* Interrupt * * No need for locking in this Interrupt handler: done_status is the - * only information modified. What we need is the update of this field - * before tasklet runs. This is ensured by using barrier. + * only information modified. */ static irqreturn_t atmel_spi_pio_interrupt(int irq, void *dev_id) @@ -1107,8 +913,6 @@ atmel_spi_pio_interrupt(int irq, void *dev_id) * * We will also not process any remaning transfers in * the message. - * - * All actions are done in tasklet with done_status indication */ as->done_status = -EIO; smp_wmb(); @@ -1116,7 +920,7 @@ atmel_spi_pio_interrupt(int irq, void *dev_id) /* Clear any overrun happening while cleaning up */ spi_readl(as, SR); - tasklet_schedule(&as->tasklet); + complete(&as->xfer_completion); } else if (pending & SPI_BIT(RDRF)) { atmel_spi_lock(as); @@ -1125,11 +929,10 @@ atmel_spi_pio_interrupt(int irq, void *dev_id) ret = IRQ_HANDLED; xfer = as->current_transfer; atmel_spi_pump_pio_data(as, xfer); - if (!as->current_remaining_bytes) { - /* no more data to xfer, kick tasklet */ + if (!as->current_remaining_bytes) spi_writel(as, IDR, pending); - tasklet_schedule(&as->tasklet); - } + + complete(&as->xfer_completion); } atmel_spi_unlock(as); @@ -1147,116 +950,35 @@ atmel_spi_pdc_interrupt(int irq, void *dev_id) { struct spi_master *master = dev_id; struct atmel_spi *as = spi_master_get_devdata(master); - struct spi_message *msg; - struct spi_transfer *xfer; u32 status, pending, imr; int ret = IRQ_NONE; - atmel_spi_lock(as); - - xfer = as->current_transfer; - msg = list_entry(as->queue.next, struct spi_message, queue); - imr = spi_readl(as, IMR); status = spi_readl(as, SR); pending = status & imr; if (pending & SPI_BIT(OVRES)) { - int timeout; ret = IRQ_HANDLED; spi_writel(as, IDR, (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX) | SPI_BIT(OVRES))); - /* - * When we get an overrun, we disregard the current - * transfer. Data will not be copied back from any - * bounce buffer and msg->actual_len will not be - * updated with the last xfer. - * - * We will also not process any remaning transfers in - * the message. - * - * First, stop the transfer and unmap the DMA buffers. - */ - spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); - if (!msg->is_dma_mapped) - atmel_spi_dma_unmap_xfer(master, xfer); - - /* REVISIT: udelay in irq is unfriendly */ - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); - - dev_warn(master->dev.parent, "overrun (%u/%u remaining)\n", - spi_readl(as, TCR), spi_readl(as, RCR)); - - /* - * Clean up DMA registers and make sure the data - * registers are empty. - */ - spi_writel(as, RNCR, 0); - spi_writel(as, TNCR, 0); - spi_writel(as, RCR, 0); - spi_writel(as, TCR, 0); - for (timeout = 1000; timeout; timeout--) - if (spi_readl(as, SR) & SPI_BIT(TXEMPTY)) - break; - if (!timeout) - dev_warn(master->dev.parent, - "timeout waiting for TXEMPTY"); - while (spi_readl(as, SR) & SPI_BIT(RDRF)) - spi_readl(as, RDR); - /* Clear any overrun happening while cleaning up */ spi_readl(as, SR); as->done_status = -EIO; - atmel_spi_msg_done(master, as, msg, 0); + + complete(&as->xfer_completion); + } else if (pending & (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX))) { ret = IRQ_HANDLED; spi_writel(as, IDR, pending); - if (as->current_remaining_bytes == 0) { - msg->actual_length += xfer->len; - - if (!msg->is_dma_mapped) - atmel_spi_dma_unmap_xfer(master, xfer); - - /* REVISIT: udelay in irq is unfriendly */ - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); - - if (atmel_spi_xfer_is_last(msg, xfer)) { - /* report completed message */ - atmel_spi_msg_done(master, as, msg, - xfer->cs_change); - } else { - if (xfer->cs_change) { - cs_deactivate(as, msg->spi); - udelay(1); - cs_activate(as, msg->spi); - } - - /* - * Not done yet. Submit the next transfer. - * - * FIXME handle protocol options for xfer - */ - atmel_spi_pdc_next_xfer(master, msg); - } - } else { - /* - * Keep going, we still have data to send in - * the current transfer. - */ - atmel_spi_pdc_next_xfer(master, msg); - } + complete(&as->xfer_completion); } - atmel_spi_unlock(as); - return ret; } @@ -1264,17 +986,13 @@ static int atmel_spi_setup(struct spi_device *spi) { struct atmel_spi *as; struct atmel_spi_device *asd; - u32 scbr, csr; + u32 csr; unsigned int bits = spi->bits_per_word; - unsigned long bus_hz; unsigned int npcs_pin; int ret; as = spi_master_get_devdata(spi->master); - if (as->stopping) - return -ESHUTDOWN; - if (spi->chip_select > spi->master->num_chipselect) { dev_dbg(&spi->dev, "setup: invalid chipselect %u (%u defined)\n", @@ -1290,33 +1008,7 @@ static int atmel_spi_setup(struct spi_device *spi) return -EINVAL; } - /* v1 chips start out at half the peripheral bus speed. */ - bus_hz = clk_get_rate(as->clk); - if (!atmel_spi_is_v2(as)) - bus_hz /= 2; - - if (spi->max_speed_hz) { - /* - * Calculate the lowest divider that satisfies the - * constraint, assuming div32/fdiv/mbz == 0. - */ - scbr = DIV_ROUND_UP(bus_hz, spi->max_speed_hz); - - /* - * If the resulting divider doesn't fit into the - * register bitfield, we can't satisfy the constraint. - */ - if (scbr >= (1 << SPI_SCBR_SIZE)) { - dev_dbg(&spi->dev, - "setup: %d Hz too slow, scbr %u; min %ld Hz\n", - spi->max_speed_hz, scbr, bus_hz/255); - return -EINVAL; - } - } else - /* speed zero means "as slow as possible" */ - scbr = 0xff; - - csr = SPI_BF(SCBR, scbr) | SPI_BF(BITS, bits - 8); + csr = SPI_BF(BITS, bits - 8); if (spi->mode & SPI_CPOL) csr |= SPI_BIT(CPOL); if (!(spi->mode & SPI_CPHA)) @@ -1352,19 +1044,13 @@ static int atmel_spi_setup(struct spi_device *spi) asd->npcs_pin = npcs_pin; spi->controller_state = asd; gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); - } else { - atmel_spi_lock(as); - if (as->stay == spi) - as->stay = NULL; - cs_deactivate(as, spi); - atmel_spi_unlock(as); } asd->csr = csr; dev_dbg(&spi->dev, - "setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n", - bus_hz / scbr, bits, spi->mode, spi->chip_select, csr); + "setup: bpw %u mode 0x%x -> csr%d %08x\n", + bits, spi->mode, spi->chip_select, csr); if (!atmel_spi_is_v2(as)) spi_writel(as, CSR0 + 4 * spi->chip_select, csr); @@ -1372,103 +1058,218 @@ static int atmel_spi_setup(struct spi_device *spi) return 0; } -static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) +static int atmel_spi_one_transfer(struct spi_master *master, + struct spi_message *msg, + struct spi_transfer *xfer) { struct atmel_spi *as; - struct spi_transfer *xfer; - struct device *controller = spi->master->dev.parent; + struct spi_device *spi = msg->spi; u8 bits; + u32 len; struct atmel_spi_device *asd; + int timeout; + int ret; - as = spi_master_get_devdata(spi->master); - - dev_dbg(controller, "new message %p submitted for %s\n", - msg, dev_name(&spi->dev)); + as = spi_master_get_devdata(master); - if (unlikely(list_empty(&msg->transfers))) + if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) { + dev_dbg(&spi->dev, "missing rx or tx buf\n"); return -EINVAL; + } - if (as->stopping) - return -ESHUTDOWN; + if (xfer->bits_per_word) { + asd = spi->controller_state; + bits = (asd->csr >> 4) & 0xf; + if (bits != xfer->bits_per_word - 8) { + dev_dbg(&spi->dev, + "you can't yet change bits_per_word in transfers\n"); + return -ENOPROTOOPT; + } + } - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) { - dev_dbg(&spi->dev, "missing rx or tx buf\n"); + if (xfer->bits_per_word > 8) { + if (xfer->len % 2) { + dev_dbg(&spi->dev, + "buffer len should be 16 bits aligned\n"); return -EINVAL; } + } - if (xfer->bits_per_word) { - asd = spi->controller_state; - bits = (asd->csr >> 4) & 0xf; - if (bits != xfer->bits_per_word - 8) { - dev_dbg(&spi->dev, - "you can't yet change bits_per_word in transfers\n"); - return -ENOPROTOOPT; + /* + * DMA map early, for performance (empties dcache ASAP) and + * better fault reporting. + */ + if ((!msg->is_dma_mapped) + && (atmel_spi_use_dma(as, xfer) || as->use_pdc)) { + if (atmel_spi_dma_map_xfer(as, xfer) < 0) + return -ENOMEM; + } + + atmel_spi_set_xfer_speed(as, msg->spi, xfer); + + as->done_status = 0; + as->current_transfer = xfer; + as->current_remaining_bytes = xfer->len; + while (as->current_remaining_bytes) { + reinit_completion(&as->xfer_completion); + + if (as->use_pdc) { + atmel_spi_pdc_next_xfer(master, msg, xfer); + } else if (atmel_spi_use_dma(as, xfer)) { + len = as->current_remaining_bytes; + ret = atmel_spi_next_xfer_dma_submit(master, + xfer, &len); + if (ret) { + dev_err(&spi->dev, + "unable to use DMA, fallback to PIO\n"); + atmel_spi_next_xfer_pio(master, xfer); + } else { + as->current_remaining_bytes -= len; } + } else { + atmel_spi_next_xfer_pio(master, xfer); } - if (xfer->bits_per_word > 8) { - if (xfer->len % 2) { - dev_dbg(&spi->dev, "buffer len should be 16 bits aligned\n"); - return -EINVAL; - } + ret = wait_for_completion_timeout(&as->xfer_completion, + SPI_DMA_TIMEOUT); + if (WARN_ON(ret == 0)) { + dev_err(&spi->dev, + "spi trasfer timeout, err %d\n", ret); + as->done_status = -EIO; + } else { + ret = 0; } - /* FIXME implement these protocol options!! */ - if (xfer->speed_hz < spi->max_speed_hz) { - dev_dbg(&spi->dev, "can't change speed in transfer\n"); - return -ENOPROTOOPT; + if (as->done_status) + break; + } + + if (as->done_status) { + if (as->use_pdc) { + dev_warn(master->dev.parent, + "overrun (%u/%u remaining)\n", + spi_readl(as, TCR), spi_readl(as, RCR)); + + /* + * Clean up DMA registers and make sure the data + * registers are empty. + */ + spi_writel(as, RNCR, 0); + spi_writel(as, TNCR, 0); + spi_writel(as, RCR, 0); + spi_writel(as, TCR, 0); + for (timeout = 1000; timeout; timeout--) + if (spi_readl(as, SR) & SPI_BIT(TXEMPTY)) + break; + if (!timeout) + dev_warn(master->dev.parent, + "timeout waiting for TXEMPTY"); + while (spi_readl(as, SR) & SPI_BIT(RDRF)) + spi_readl(as, RDR); + + /* Clear any overrun happening while cleaning up */ + spi_readl(as, SR); + + } else if (atmel_spi_use_dma(as, xfer)) { + atmel_spi_stop_dma(as); } - /* - * DMA map early, for performance (empties dcache ASAP) and - * better fault reporting. - */ - if ((!msg->is_dma_mapped) && (atmel_spi_use_dma(as, xfer) - || as->use_pdc)) { - if (atmel_spi_dma_map_xfer(as, xfer) < 0) - return -ENOMEM; + if (!msg->is_dma_mapped + && (atmel_spi_use_dma(as, xfer) || as->use_pdc)) + atmel_spi_dma_unmap_xfer(master, xfer); + + return 0; + + } else { + /* only update length if no error */ + msg->actual_length += xfer->len; + } + + if (!msg->is_dma_mapped + && (atmel_spi_use_dma(as, xfer) || as->use_pdc)) + atmel_spi_dma_unmap_xfer(master, xfer); + + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + if (xfer->cs_change) { + if (list_is_last(&xfer->transfer_list, + &msg->transfers)) { + as->keep_cs = true; + } else { + as->cs_active = !as->cs_active; + if (as->cs_active) + cs_activate(as, msg->spi); + else + cs_deactivate(as, msg->spi); } } -#ifdef VERBOSE + return 0; +} + +static int atmel_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct atmel_spi *as; + struct spi_transfer *xfer; + struct spi_device *spi = msg->spi; + int ret = 0; + + as = spi_master_get_devdata(master); + + dev_dbg(&spi->dev, "new message %p submitted for %s\n", + msg, dev_name(&spi->dev)); + + if (unlikely(list_empty(&msg->transfers))) + return -EINVAL; + + atmel_spi_lock(as); + cs_activate(as, spi); + + as->cs_active = true; + as->keep_cs = false; + + msg->status = 0; + msg->actual_length = 0; + list_for_each_entry(xfer, &msg->transfers, transfer_list) { - dev_dbg(controller, + ret = atmel_spi_one_transfer(master, msg, xfer); + if (ret) + goto msg_done; + } + + if (as->use_pdc) + atmel_spi_disable_pdc_transfer(as); + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + dev_dbg(&spi->dev, " xfer %p: len %u tx %p/%08x rx %p/%08x\n", xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, xfer->rx_buf, xfer->rx_dma); } -#endif - msg->status = -EINPROGRESS; - msg->actual_length = 0; +msg_done: + if (!as->keep_cs) + cs_deactivate(as, msg->spi); - atmel_spi_lock(as); - list_add_tail(&msg->queue, &as->queue); - if (!as->current_transfer) - atmel_spi_next_message(spi->master); atmel_spi_unlock(as); - return 0; + msg->status = as->done_status; + spi_finalize_current_message(spi->master); + + return ret; } static void atmel_spi_cleanup(struct spi_device *spi) { - struct atmel_spi *as = spi_master_get_devdata(spi->master); struct atmel_spi_device *asd = spi->controller_state; unsigned gpio = (unsigned) spi->controller_data; if (!asd) return; - atmel_spi_lock(as); - if (as->stay == spi) { - as->stay = NULL; - cs_deactivate(as, spi); - } - atmel_spi_unlock(as); - spi->controller_state = NULL; gpio_free(gpio); kfree(asd); @@ -1510,7 +1311,7 @@ static int atmel_spi_probe(struct platform_device *pdev) if (irq < 0) return irq; - clk = clk_get(&pdev->dev, "spi_clk"); + clk = devm_clk_get(&pdev->dev, "spi_clk"); if (IS_ERR(clk)) return PTR_ERR(clk); @@ -1527,7 +1328,7 @@ static int atmel_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = master->dev.of_node ? 0 : 4; master->setup = atmel_spi_setup; - master->transfer = atmel_spi_transfer; + master->transfer_one_message = atmel_spi_transfer_one_message; master->cleanup = atmel_spi_cleanup; platform_set_drvdata(pdev, master); @@ -1543,7 +1344,6 @@ static int atmel_spi_probe(struct platform_device *pdev) goto out_free; spin_lock_init(&as->lock); - INIT_LIST_HEAD(&as->queue); as->pdev = pdev; as->regs = devm_ioremap_resource(&pdev->dev, regs); @@ -1555,6 +1355,8 @@ static int atmel_spi_probe(struct platform_device *pdev) as->irq = irq; as->clk = clk; + init_completion(&as->xfer_completion); + atmel_get_caps(as); as->use_dma = false; @@ -1570,14 +1372,11 @@ static int atmel_spi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n"); if (as->use_pdc) { - ret = request_irq(irq, atmel_spi_pdc_interrupt, 0, - dev_name(&pdev->dev), master); + ret = devm_request_irq(&pdev->dev, irq, atmel_spi_pdc_interrupt, + 0, dev_name(&pdev->dev), master); } else { - tasklet_init(&as->tasklet, atmel_spi_tasklet_func, - (unsigned long)master); - - ret = request_irq(irq, atmel_spi_pio_interrupt, 0, - dev_name(&pdev->dev), master); + ret = devm_request_irq(&pdev->dev, irq, atmel_spi_pio_interrupt, + 0, dev_name(&pdev->dev), master); } if (ret) goto out_unmap_regs; @@ -1603,7 +1402,7 @@ static int atmel_spi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", (unsigned long)regs->start, irq); - ret = spi_register_master(master); + ret = devm_spi_register_master(&pdev->dev, master); if (ret) goto out_free_dma; @@ -1617,15 +1416,11 @@ out_free_dma: spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ clk_disable_unprepare(clk); out_free_irq: - free_irq(irq, master); out_unmap_regs: out_free_buffer: - if (!as->use_pdc) - tasklet_kill(&as->tasklet); dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, as->buffer_dma); out_free: - clk_put(clk); spi_master_put(master); return ret; } @@ -1634,12 +1429,9 @@ static int atmel_spi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); - struct spi_message *msg; - struct spi_transfer *xfer; /* reset the hardware and block queue progress */ spin_lock_irq(&as->lock); - as->stopping = 1; if (as->use_dma) { atmel_spi_stop_dma(as); atmel_spi_release_dma(as); @@ -1650,28 +1442,10 @@ static int atmel_spi_remove(struct platform_device *pdev) spi_readl(as, SR); spin_unlock_irq(&as->lock); - /* Terminate remaining queued transfers */ - list_for_each_entry(msg, &as->queue, queue) { - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - if (!msg->is_dma_mapped - && (atmel_spi_use_dma(as, xfer) - || as->use_pdc)) - atmel_spi_dma_unmap_xfer(master, xfer); - } - msg->status = -ESHUTDOWN; - msg->complete(msg->context); - } - - if (!as->use_pdc) - tasklet_kill(&as->tasklet); dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, as->buffer_dma); clk_disable_unprepare(as->clk); - clk_put(as->clk); - free_irq(as->irq, master); - - spi_unregister_master(master); return 0; } diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 469ecd876358..dceb7b216f46 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -169,8 +169,6 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, transfer_list); } - len -= prepend_len; - init_completion(&bs->done); /* Fill in the Message control register */ diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 8641b03bdd7a..a37f15665a60 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -320,8 +320,10 @@ static void dspi_chipselect(struct spi_device *spi, int value) switch (value) { case BITBANG_CS_ACTIVE: pushr |= SPI_PUSHR_CONT; + break; case BITBANG_CS_INACTIVE: pushr &= ~SPI_PUSHR_CONT; + break; } writel(pushr, dspi->base + SPI_PUSHR); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index b80f2f70fef7..a5474ef9d2a0 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -206,7 +206,8 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, #define MX51_ECSPI_STAT_RR (1 << 3) /* MX51 eCSPI */ -static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi) +static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi, + unsigned int *fres) { /* * there are two 4-bit dividers, the pre-divider divides by @@ -234,6 +235,10 @@ static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi) pr_debug("%s: fin: %u, fspi: %u, post: %u, pre: %u\n", __func__, fin, fspi, post, pre); + + /* Resulting frequency for the SCLK line. */ + *fres = (fin / (pre + 1)) >> post; + return (pre << MX51_ECSPI_CTRL_PREDIV_OFFSET) | (post << MX51_ECSPI_CTRL_POSTDIV_OFFSET); } @@ -264,6 +269,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, struct spi_imx_config *config) { u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0; + u32 clk = config->speed_hz, delay; /* * The hardware seems to have a race condition when changing modes. The @@ -275,7 +281,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, ctrl |= MX51_ECSPI_CTRL_MODE_MASK; /* set clock speed */ - ctrl |= mx51_ecspi_clkdiv(spi_imx->spi_clk, config->speed_hz); + ctrl |= mx51_ecspi_clkdiv(spi_imx->spi_clk, config->speed_hz, &clk); /* set chip select to use */ ctrl |= MX51_ECSPI_CTRL_CS(config->cs); @@ -297,6 +303,23 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG); + /* + * Wait until the changes in the configuration register CONFIGREG + * propagate into the hardware. It takes exactly one tick of the + * SCLK clock, but we will wait two SCLK clock just to be sure. The + * effect of the delay it takes for the hardware to apply changes + * is noticable if the SCLK clock run very slow. In such a case, if + * the polarity of SCLK should be inverted, the GPIO ChipSelect might + * be asserted before the SCLK polarity changes, which would disrupt + * the SPI communication as the device on the other end would consider + * the change of SCLK polarity as a clock tick already. + */ + delay = (2 * 1000000) / clk; + if (likely(delay < 10)) /* SCLK is faster than 100 kHz */ + udelay(delay); + else /* SCLK is _very_ slow */ + usleep_range(delay, delay + 10); + return 0; } diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 7765b1999537..cbc68848789d 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1066,6 +1066,8 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) pdata->num_chipselect = 1; pdata->enable_dma = true; + pdata->tx_chan_id = -1; + pdata->rx_chan_id = -1; return pdata; } diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 292567ab4c6c..40179d201966 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -353,4 +353,4 @@ module_platform_driver(hspi_driver); MODULE_DESCRIPTION("SuperH HSPI bus driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); -MODULE_ALIAS("platform:sh_spi"); +MODULE_ALIAS("platform:sh-hspi"); diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index c74298cf70e2..ac8795f2e700 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -152,7 +152,7 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, size_t k; if (!WARN_ON(!spi_hz || !parent_rate)) - div = parent_rate / spi_hz; + div = DIV_ROUND_UP(parent_rate, spi_hz); /* TODO: make more fine grained */ diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 349ebba4b199..63613a96233c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -370,6 +370,17 @@ static void spi_dev_set_name(struct spi_device *spi) spi->chip_select); } +static int spi_dev_check(struct device *dev, void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_device *new_spi = data; + + if (spi->master == new_spi->master && + spi->chip_select == new_spi->chip_select) + return -EBUSY; + return 0; +} + /** * spi_add_device - Add spi_device allocated with spi_alloc_device * @spi: spi_device to register @@ -384,7 +395,6 @@ int spi_add_device(struct spi_device *spi) static DEFINE_MUTEX(spi_add_lock); struct spi_master *master = spi->master; struct device *dev = master->dev.parent; - struct device *d; int status; /* Chipselects are numbered 0..max; validate. */ @@ -404,12 +414,10 @@ int spi_add_device(struct spi_device *spi) */ mutex_lock(&spi_add_lock); - d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); - if (d != NULL) { + status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check); + if (status) { dev_err(dev, "chipselect %d already in use\n", spi->chip_select); - put_device(d); - status = -EBUSY; goto done; } @@ -591,8 +599,10 @@ static int spi_transfer_one_message(struct spi_master *master, goto out; } - if (ret > 0) + if (ret > 0) { + ret = 0; wait_for_completion(&master->xfer_completion); + } trace_spi_transfer_stop(msg, xfer); @@ -632,7 +642,7 @@ out: * * Called by SPI drivers using the core transfer_one_message() * implementation to notify it that the current interrupt driven - * transfer has finised and the next one may be scheduled. + * transfer has finished and the next one may be scheduled. */ void spi_finalize_current_transfer(struct spi_master *master) { @@ -685,7 +695,7 @@ static void spi_pump_messages(struct kthread_work *work) } /* Extract head of queue */ master->cur_msg = - list_entry(master->queue.next, struct spi_message, queue); + list_first_entry(&master->queue, struct spi_message, queue); list_del_init(&master->cur_msg->queue); if (master->busy) @@ -735,7 +745,9 @@ static void spi_pump_messages(struct kthread_work *work) ret = master->transfer_one_message(master, master->cur_msg); if (ret) { dev_err(&master->dev, - "failed to transfer one message from queue\n"); + "failed to transfer one message from queue: %d\n", ret); + master->cur_msg->status = ret; + spi_finalize_current_message(master); return; } } @@ -791,11 +803,8 @@ struct spi_message *spi_get_next_queued_message(struct spi_master *master) /* get a pointer to the next message, if any */ spin_lock_irqsave(&master->queue_lock, flags); - if (list_empty(&master->queue)) - next = NULL; - else - next = list_entry(master->queue.next, - struct spi_message, queue); + next = list_first_entry_or_null(&master->queue, struct spi_message, + queue); spin_unlock_irqrestore(&master->queue_lock, flags); return next; @@ -1596,15 +1605,11 @@ int spi_setup(struct spi_device *spi) } EXPORT_SYMBOL_GPL(spi_setup); -static int __spi_async(struct spi_device *spi, struct spi_message *message) +static int __spi_validate(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; struct spi_transfer *xfer; - message->spi = spi; - - trace_spi_message_submit(message); - if (list_empty(&message->transfers)) return -EINVAL; if (!message->complete) @@ -1667,9 +1672,8 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) if (xfer->rx_buf && !xfer->rx_nbits) xfer->rx_nbits = SPI_NBITS_SINGLE; /* check transfer tx/rx_nbits: - * 1. keep the value is not out of single, dual and quad - * 2. keep tx/rx_nbits is contained by mode in spi_device - * 3. if SPI_3WIRE, tx/rx_nbits should be in single + * 1. check the value matches one of single, dual and quad + * 2. check tx/rx_nbits match the mode in spi_device */ if (xfer->tx_buf) { if (xfer->tx_nbits != SPI_NBITS_SINGLE && @@ -1682,9 +1686,6 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) if ((xfer->tx_nbits == SPI_NBITS_QUAD) && !(spi->mode & SPI_TX_QUAD)) return -EINVAL; - if ((spi->mode & SPI_3WIRE) && - (xfer->tx_nbits != SPI_NBITS_SINGLE)) - return -EINVAL; } /* check transfer rx_nbits */ if (xfer->rx_buf) { @@ -1698,13 +1699,22 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) if ((xfer->rx_nbits == SPI_NBITS_QUAD) && !(spi->mode & SPI_RX_QUAD)) return -EINVAL; - if ((spi->mode & SPI_3WIRE) && - (xfer->rx_nbits != SPI_NBITS_SINGLE)) - return -EINVAL; } } message->status = -EINPROGRESS; + + return 0; +} + +static int __spi_async(struct spi_device *spi, struct spi_message *message) +{ + struct spi_master *master = spi->master; + + message->spi = spi; + + trace_spi_message_submit(message); + return master->transfer(spi, message); } @@ -1743,6 +1753,10 @@ int spi_async(struct spi_device *spi, struct spi_message *message) int ret; unsigned long flags; + ret = __spi_validate(spi, message); + if (ret != 0) + return ret; + spin_lock_irqsave(&master->bus_lock_spinlock, flags); if (master->bus_lock_flag) @@ -1791,6 +1805,10 @@ int spi_async_locked(struct spi_device *spi, struct spi_message *message) int ret; unsigned long flags; + ret = __spi_validate(spi, message); + if (ret != 0) + return ret; + spin_lock_irqsave(&master->bus_lock_spinlock, flags); ret = __spi_async(spi, message); |