|
@@ -635,11 +635,14 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
|
|
{
|
|
|
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
|
|
const unsigned int fifo_len = (FIFO_LVL_MASK(sdd) >> 1) + 1;
|
|
|
+ const void *tx_buf = NULL;
|
|
|
+ void *rx_buf = NULL;
|
|
|
+ int target_len = 0, origin_len = 0;
|
|
|
+ int use_dma = 0;
|
|
|
int status;
|
|
|
u32 speed;
|
|
|
u8 bpw;
|
|
|
unsigned long flags;
|
|
|
- int use_dma;
|
|
|
|
|
|
reinit_completion(&sdd->xfer_completion);
|
|
|
|
|
@@ -654,47 +657,77 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
|
|
s3c64xx_spi_config(sdd);
|
|
|
}
|
|
|
|
|
|
- /* Polling method for xfers not bigger than FIFO capacity */
|
|
|
- use_dma = 0;
|
|
|
if (!is_polling(sdd) && (xfer->len > fifo_len) &&
|
|
|
- sdd->rx_dma.ch && sdd->tx_dma.ch)
|
|
|
+ sdd->rx_dma.ch && sdd->tx_dma.ch) {
|
|
|
use_dma = 1;
|
|
|
|
|
|
- spin_lock_irqsave(&sdd->lock, flags);
|
|
|
+ } else if (is_polling(sdd) && xfer->len > fifo_len) {
|
|
|
+ tx_buf = xfer->tx_buf;
|
|
|
+ rx_buf = xfer->rx_buf;
|
|
|
+ origin_len = xfer->len;
|
|
|
|
|
|
- /* Pending only which is to be done */
|
|
|
- sdd->state &= ~RXBUSY;
|
|
|
- sdd->state &= ~TXBUSY;
|
|
|
+ target_len = xfer->len;
|
|
|
+ if (xfer->len > fifo_len)
|
|
|
+ xfer->len = fifo_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ do {
|
|
|
+ spin_lock_irqsave(&sdd->lock, flags);
|
|
|
|
|
|
- s3c64xx_enable_datapath(sdd, xfer, use_dma);
|
|
|
+ /* Pending only which is to be done */
|
|
|
+ sdd->state &= ~RXBUSY;
|
|
|
+ sdd->state &= ~TXBUSY;
|
|
|
|
|
|
- /* Start the signals */
|
|
|
- s3c64xx_spi_set_cs(spi, true);
|
|
|
+ s3c64xx_enable_datapath(sdd, xfer, use_dma);
|
|
|
|
|
|
- spin_unlock_irqrestore(&sdd->lock, flags);
|
|
|
+ /* Start the signals */
|
|
|
+ s3c64xx_spi_set_cs(spi, true);
|
|
|
|
|
|
- if (use_dma)
|
|
|
- status = s3c64xx_wait_for_dma(sdd, xfer);
|
|
|
- else
|
|
|
- status = s3c64xx_wait_for_pio(sdd, xfer);
|
|
|
-
|
|
|
- if (status) {
|
|
|
- dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
|
|
|
- xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0,
|
|
|
- (sdd->state & RXBUSY) ? 'f' : 'p',
|
|
|
- (sdd->state & TXBUSY) ? 'f' : 'p',
|
|
|
- xfer->len);
|
|
|
-
|
|
|
- if (use_dma) {
|
|
|
- if (xfer->tx_buf != NULL
|
|
|
- && (sdd->state & TXBUSY))
|
|
|
- dmaengine_terminate_all(sdd->tx_dma.ch);
|
|
|
- if (xfer->rx_buf != NULL
|
|
|
- && (sdd->state & RXBUSY))
|
|
|
- dmaengine_terminate_all(sdd->rx_dma.ch);
|
|
|
+ spin_unlock_irqrestore(&sdd->lock, flags);
|
|
|
+
|
|
|
+ if (use_dma)
|
|
|
+ status = s3c64xx_wait_for_dma(sdd, xfer);
|
|
|
+ else
|
|
|
+ status = s3c64xx_wait_for_pio(sdd, xfer);
|
|
|
+
|
|
|
+ if (status) {
|
|
|
+ dev_err(&spi->dev,
|
|
|
+ "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
|
|
|
+ xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0,
|
|
|
+ (sdd->state & RXBUSY) ? 'f' : 'p',
|
|
|
+ (sdd->state & TXBUSY) ? 'f' : 'p',
|
|
|
+ xfer->len);
|
|
|
+
|
|
|
+ if (use_dma) {
|
|
|
+ if (xfer->tx_buf && (sdd->state & TXBUSY))
|
|
|
+ dmaengine_terminate_all(sdd->tx_dma.ch);
|
|
|
+ if (xfer->rx_buf && (sdd->state & RXBUSY))
|
|
|
+ dmaengine_terminate_all(sdd->rx_dma.ch);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ s3c64xx_flush_fifo(sdd);
|
|
|
}
|
|
|
- } else {
|
|
|
- s3c64xx_flush_fifo(sdd);
|
|
|
+ if (target_len > 0) {
|
|
|
+ target_len -= xfer->len;
|
|
|
+
|
|
|
+ if (xfer->tx_buf)
|
|
|
+ xfer->tx_buf += xfer->len;
|
|
|
+
|
|
|
+ if (xfer->rx_buf)
|
|
|
+ xfer->rx_buf += xfer->len;
|
|
|
+
|
|
|
+ if (target_len > fifo_len)
|
|
|
+ xfer->len = fifo_len;
|
|
|
+ else
|
|
|
+ xfer->len = target_len;
|
|
|
+ }
|
|
|
+ } while (target_len > 0);
|
|
|
+
|
|
|
+ if (origin_len) {
|
|
|
+ /* Restore original xfer buffers and length */
|
|
|
+ xfer->tx_buf = tx_buf;
|
|
|
+ xfer->rx_buf = rx_buf;
|
|
|
+ xfer->len = origin_len;
|
|
|
}
|
|
|
|
|
|
return status;
|