diff options
-rw-r--r-- | drivers/spi/au1550_spi.c | 69 |
1 files changed, 49 insertions, 20 deletions
diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index 3860dd2fa5d9..9149689c79d9 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c | |||
@@ -484,9 +484,13 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw) | |||
484 | au1xxx_dbdma_reset(hw->dma_tx_ch); | 484 | au1xxx_dbdma_reset(hw->dma_tx_ch); |
485 | au1550_spi_reset_fifos(hw); | 485 | au1550_spi_reset_fifos(hw); |
486 | 486 | ||
487 | dev_err(hw->dev, | 487 | if (evnt == PSC_SPIEVNT_RO) |
488 | "Unexpected SPI error: event=0x%x stat=0x%x!\n", | 488 | dev_err(hw->dev, |
489 | evnt, stat); | 489 | "dma transfer: receive FIFO overflow!\n"); |
490 | else | ||
491 | dev_err(hw->dev, | ||
492 | "dma transfer: unexpected SPI error " | ||
493 | "(event=0x%x stat=0x%x)!\n", evnt, stat); | ||
490 | 494 | ||
491 | complete(&hw->master_done); | 495 | complete(&hw->master_done); |
492 | return IRQ_HANDLED; | 496 | return IRQ_HANDLED; |
@@ -596,17 +600,17 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw) | |||
596 | 600 | ||
597 | if ((evnt & (PSC_SPIEVNT_MM | PSC_SPIEVNT_RO | 601 | if ((evnt & (PSC_SPIEVNT_MM | PSC_SPIEVNT_RO |
598 | | PSC_SPIEVNT_RU | PSC_SPIEVNT_TO | 602 | | PSC_SPIEVNT_RU | PSC_SPIEVNT_TO |
599 | | PSC_SPIEVNT_TU | PSC_SPIEVNT_SD)) | 603 | | PSC_SPIEVNT_SD)) |
600 | != 0) { | 604 | != 0) { |
601 | dev_err(hw->dev, | ||
602 | "Unexpected SPI error: event=0x%x stat=0x%x!\n", | ||
603 | evnt, stat); | ||
604 | /* | 605 | /* |
605 | * due to an error we consider transfer as done, | 606 | * due to an error we consider transfer as done, |
606 | * so mask all events until before next transfer start | 607 | * so mask all events until before next transfer start |
607 | */ | 608 | */ |
608 | au1550_spi_mask_ack_all(hw); | 609 | au1550_spi_mask_ack_all(hw); |
609 | au1550_spi_reset_fifos(hw); | 610 | au1550_spi_reset_fifos(hw); |
611 | dev_err(hw->dev, | ||
612 | "pio transfer: unexpected SPI error " | ||
613 | "(event=0x%x stat=0x%x)!\n", evnt, stat); | ||
610 | complete(&hw->master_done); | 614 | complete(&hw->master_done); |
611 | return IRQ_HANDLED; | 615 | return IRQ_HANDLED; |
612 | } | 616 | } |
@@ -620,27 +624,50 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw) | |||
620 | stat = hw->regs->psc_spistat; | 624 | stat = hw->regs->psc_spistat; |
621 | au_sync(); | 625 | au_sync(); |
622 | 626 | ||
623 | if ((stat & PSC_SPISTAT_RE) == 0 && hw->rx_count < hw->len) { | 627 | /* |
628 | * Take care to not let the Rx FIFO overflow. | ||
629 | * | ||
630 | * We only write a byte if we have read one at least. Initially, | ||
631 | * the write fifo is full, so we should read from the read fifo | ||
632 | * first. | ||
633 | * In case we miss a word from the read fifo, we should get a | ||
634 | * RO event and should back out. | ||
635 | */ | ||
636 | if (!(stat & PSC_SPISTAT_RE) && hw->rx_count < hw->len) { | ||
624 | hw->rx_word(hw); | 637 | hw->rx_word(hw); |
625 | /* ack the receive request event */ | ||
626 | hw->regs->psc_spievent = PSC_SPIEVNT_RR; | ||
627 | au_sync(); | ||
628 | busy = 1; | 638 | busy = 1; |
629 | } | ||
630 | 639 | ||
631 | if ((stat & PSC_SPISTAT_TF) == 0 && hw->tx_count < hw->len) { | 640 | if (!(stat & PSC_SPISTAT_TF) && hw->tx_count < hw->len) |
632 | hw->tx_word(hw); | 641 | hw->tx_word(hw); |
633 | /* ack the transmit request event */ | ||
634 | hw->regs->psc_spievent = PSC_SPIEVNT_TR; | ||
635 | au_sync(); | ||
636 | busy = 1; | ||
637 | } | 642 | } |
638 | } while (busy); | 643 | } while (busy); |
639 | 644 | ||
640 | evnt = hw->regs->psc_spievent; | 645 | hw->regs->psc_spievent = PSC_SPIEVNT_RR | PSC_SPIEVNT_TR; |
641 | au_sync(); | 646 | au_sync(); |
642 | 647 | ||
643 | if (hw->rx_count >= hw->len || (evnt & PSC_SPIEVNT_MD) != 0) { | 648 | /* |
649 | * Restart the SPI transmission in case of a transmit underflow. | ||
650 | * This seems to work despite the notes in the Au1550 data book | ||
651 | * of Figure 8-4 with flowchart for SPI master operation: | ||
652 | * | ||
653 | * """Note 1: An XFR Error Interrupt occurs, unless masked, | ||
654 | * for any of the following events: Tx FIFO Underflow, | ||
655 | * Rx FIFO Overflow, or Multiple-master Error | ||
656 | * Note 2: In case of a Tx Underflow Error, all zeroes are | ||
657 | * transmitted.""" | ||
658 | * | ||
659 | * By simply restarting the spi transfer on Tx Underflow Error, | ||
660 | * we assume that spi transfer was paused instead of zeroes | ||
661 | * transmittion mentioned in the Note 2 of Au1550 data book. | ||
662 | */ | ||
663 | if (evnt & PSC_SPIEVNT_TU) { | ||
664 | hw->regs->psc_spievent = PSC_SPIEVNT_TU | PSC_SPIEVNT_MD; | ||
665 | au_sync(); | ||
666 | hw->regs->psc_spipcr = PSC_SPIPCR_MS; | ||
667 | au_sync(); | ||
668 | } | ||
669 | |||
670 | if (hw->rx_count >= hw->len) { | ||
644 | /* transfer completed successfully */ | 671 | /* transfer completed successfully */ |
645 | au1550_spi_mask_ack_all(hw); | 672 | au1550_spi_mask_ack_all(hw); |
646 | complete(&hw->master_done); | 673 | complete(&hw->master_done); |
@@ -729,6 +756,8 @@ static void __init au1550_spi_setup_psc_as_spi(struct au1550_spi *hw) | |||
729 | stat = hw->regs->psc_spistat; | 756 | stat = hw->regs->psc_spistat; |
730 | au_sync(); | 757 | au_sync(); |
731 | } while ((stat & PSC_SPISTAT_DR) == 0); | 758 | } while ((stat & PSC_SPISTAT_DR) == 0); |
759 | |||
760 | au1550_spi_reset_fifos(hw); | ||
732 | } | 761 | } |
733 | 762 | ||
734 | 763 | ||