]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/spi/au1550_spi.c
spi: au1550_spi: improve pio transfer mode
[linux-2.6-omap-h63xx.git] / drivers / spi / au1550_spi.c
index 3860dd2fa5d9253d2e2f3ff050069165ce5009ab..9149689c79d9e4ac198fefe778a9aa2de27e065e 100644 (file)
@@ -484,9 +484,13 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw)
                au1xxx_dbdma_reset(hw->dma_tx_ch);
                au1550_spi_reset_fifos(hw);
 
-               dev_err(hw->dev,
-                       "Unexpected SPI error: event=0x%x stat=0x%x!\n",
-                       evnt, stat);
+               if (evnt == PSC_SPIEVNT_RO)
+                       dev_err(hw->dev,
+                               "dma transfer: receive FIFO overflow!\n");
+               else
+                       dev_err(hw->dev,
+                               "dma transfer: unexpected SPI error "
+                               "(event=0x%x stat=0x%x)!\n", evnt, stat);
 
                complete(&hw->master_done);
                return IRQ_HANDLED;
@@ -596,17 +600,17 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw)
 
        if ((evnt & (PSC_SPIEVNT_MM | PSC_SPIEVNT_RO
                                | PSC_SPIEVNT_RU | PSC_SPIEVNT_TO
-                               | PSC_SPIEVNT_TU | PSC_SPIEVNT_SD))
+                               | PSC_SPIEVNT_SD))
                        != 0) {
-               dev_err(hw->dev,
-                       "Unexpected SPI error: event=0x%x stat=0x%x!\n",
-                       evnt, stat);
                /*
                 * due to an error we consider transfer as done,
                 * so mask all events until before next transfer start
                 */
                au1550_spi_mask_ack_all(hw);
                au1550_spi_reset_fifos(hw);
+               dev_err(hw->dev,
+                       "pio transfer: unexpected SPI error "
+                       "(event=0x%x stat=0x%x)!\n", evnt, stat);
                complete(&hw->master_done);
                return IRQ_HANDLED;
        }
@@ -620,27 +624,50 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw)
                stat = hw->regs->psc_spistat;
                au_sync();
 
-               if ((stat & PSC_SPISTAT_RE) == 0 && hw->rx_count < hw->len) {
+               /*
+                * Take care to not let the Rx FIFO overflow.
+                *
+                * We only write a byte if we have read one at least. Initially,
+                * the write fifo is full, so we should read from the read fifo
+                * first.
+                * In case we miss a word from the read fifo, we should get a
+                * RO event and should back out.
+                */
+               if (!(stat & PSC_SPISTAT_RE) && hw->rx_count < hw->len) {
                        hw->rx_word(hw);
-                       /* ack the receive request event */
-                       hw->regs->psc_spievent = PSC_SPIEVNT_RR;
-                       au_sync();
                        busy = 1;
-               }
 
-               if ((stat & PSC_SPISTAT_TF) == 0 && hw->tx_count < hw->len) {
-                       hw->tx_word(hw);
-                       /* ack the transmit request event */
-                       hw->regs->psc_spievent = PSC_SPIEVNT_TR;
-                       au_sync();
-                       busy = 1;
+                       if (!(stat & PSC_SPISTAT_TF) && hw->tx_count < hw->len)
+                               hw->tx_word(hw);
                }
        } while (busy);
 
-       evnt = hw->regs->psc_spievent;
+       hw->regs->psc_spievent = PSC_SPIEVNT_RR | PSC_SPIEVNT_TR;
        au_sync();
 
-       if (hw->rx_count >= hw->len || (evnt & PSC_SPIEVNT_MD) != 0) {
+       /*
+        * Restart the SPI transmission in case of a transmit underflow.
+        * This seems to work despite the notes in the Au1550 data book
+        * of Figure 8-4 with flowchart for SPI master operation:
+        *
+        * """Note 1: An XFR Error Interrupt occurs, unless masked,
+        * for any of the following events: Tx FIFO Underflow,
+        * Rx FIFO Overflow, or Multiple-master Error
+        *    Note 2: In case of a Tx Underflow Error, all zeroes are
+        * transmitted."""
+        *
+        * By simply restarting the spi transfer on Tx Underflow Error,
+        * we assume that spi transfer was paused instead of zeroes
+        * transmittion mentioned in the Note 2 of Au1550 data book.
+        */
+       if (evnt & PSC_SPIEVNT_TU) {
+               hw->regs->psc_spievent = PSC_SPIEVNT_TU | PSC_SPIEVNT_MD;
+               au_sync();
+               hw->regs->psc_spipcr = PSC_SPIPCR_MS;
+               au_sync();
+       }
+
+       if (hw->rx_count >= hw->len) {
                /* transfer completed successfully */
                au1550_spi_mask_ack_all(hw);
                complete(&hw->master_done);
@@ -729,6 +756,8 @@ static void __init au1550_spi_setup_psc_as_spi(struct au1550_spi *hw)
                stat = hw->regs->psc_spistat;
                au_sync();
        } while ((stat & PSC_SPISTAT_DR) == 0);
+
+       au1550_spi_reset_fifos(hw);
 }