Skip to content

Commit

Permalink
spi: au1550_spi: improve pio transfer mode
Browse files Browse the repository at this point in the history
Improve PIO transfer mode of au1550 spi controller by continuing of spi
transfer, instead of aborting transfer when transmit underflow interrupt
occurrs.

Verified by oscilloscope that the spi clock pauses on trasmit underflow,
so transfer continuation is perfectly valid even though au1550 datasheet
says that on tx underflow zeroes will be transfered.

Also make some error messages more specific.

[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Jan Nikitenko <jan.nikitenko@gmail.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Jan Nikitenko authored and Linus Torvalds committed Jul 24, 2008
1 parent 3a93a15 commit bbe48ec
Showing 1 changed file with 49 additions and 20 deletions.
69 changes: 49 additions & 20 deletions drivers/spi/au1550_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}


Expand Down

0 comments on commit bbe48ec

Please sign in to comment.