|
@@ -484,9 +484,13 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw)
|
|
au1xxx_dbdma_reset(hw->dma_tx_ch);
|
|
au1xxx_dbdma_reset(hw->dma_tx_ch);
|
|
au1550_spi_reset_fifos(hw);
|
|
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);
|
|
complete(&hw->master_done);
|
|
return IRQ_HANDLED;
|
|
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
|
|
if ((evnt & (PSC_SPIEVNT_MM | PSC_SPIEVNT_RO
|
|
| PSC_SPIEVNT_RU | PSC_SPIEVNT_TO
|
|
| PSC_SPIEVNT_RU | PSC_SPIEVNT_TO
|
|
- | PSC_SPIEVNT_TU | PSC_SPIEVNT_SD))
|
|
|
|
|
|
+ | PSC_SPIEVNT_SD))
|
|
!= 0) {
|
|
!= 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,
|
|
* due to an error we consider transfer as done,
|
|
* so mask all events until before next transfer start
|
|
* so mask all events until before next transfer start
|
|
*/
|
|
*/
|
|
au1550_spi_mask_ack_all(hw);
|
|
au1550_spi_mask_ack_all(hw);
|
|
au1550_spi_reset_fifos(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);
|
|
complete(&hw->master_done);
|
|
return IRQ_HANDLED;
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
@@ -620,27 +624,50 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw)
|
|
stat = hw->regs->psc_spistat;
|
|
stat = hw->regs->psc_spistat;
|
|
au_sync();
|
|
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);
|
|
hw->rx_word(hw);
|
|
- /* ack the receive request event */
|
|
|
|
- hw->regs->psc_spievent = PSC_SPIEVNT_RR;
|
|
|
|
- au_sync();
|
|
|
|
busy = 1;
|
|
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);
|
|
} while (busy);
|
|
|
|
|
|
- evnt = hw->regs->psc_spievent;
|
|
|
|
|
|
+ hw->regs->psc_spievent = PSC_SPIEVNT_RR | PSC_SPIEVNT_TR;
|
|
au_sync();
|
|
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 */
|
|
/* transfer completed successfully */
|
|
au1550_spi_mask_ack_all(hw);
|
|
au1550_spi_mask_ack_all(hw);
|
|
complete(&hw->master_done);
|
|
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;
|
|
stat = hw->regs->psc_spistat;
|
|
au_sync();
|
|
au_sync();
|
|
} while ((stat & PSC_SPISTAT_DR) == 0);
|
|
} while ((stat & PSC_SPISTAT_DR) == 0);
|
|
|
|
+
|
|
|
|
+ au1550_spi_reset_fifos(hw);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|