diff options
Diffstat (limited to 'drivers/spi/spi_imx.c')
-rw-r--r-- | drivers/spi/spi_imx.c | 223 |
1 files changed, 114 insertions, 109 deletions
diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index d4ba640366b6..c730d05bfeb6 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c | |||
@@ -270,19 +270,26 @@ struct chip_data { | |||
270 | 270 | ||
271 | static void pump_messages(struct work_struct *work); | 271 | static void pump_messages(struct work_struct *work); |
272 | 272 | ||
273 | static int flush(struct driver_data *drv_data) | 273 | static void flush(struct driver_data *drv_data) |
274 | { | 274 | { |
275 | unsigned long limit = loops_per_jiffy << 1; | ||
276 | void __iomem *regs = drv_data->regs; | 275 | void __iomem *regs = drv_data->regs; |
277 | volatile u32 d; | 276 | u32 control; |
278 | 277 | ||
279 | dev_dbg(&drv_data->pdev->dev, "flush\n"); | 278 | dev_dbg(&drv_data->pdev->dev, "flush\n"); |
279 | |||
280 | /* Wait for end of transaction */ | ||
280 | do { | 281 | do { |
281 | while (readl(regs + SPI_INT_STATUS) & SPI_STATUS_RR) | 282 | control = readl(regs + SPI_CONTROL); |
282 | d = readl(regs + SPI_RXDATA); | 283 | } while (control & SPI_CONTROL_XCH); |
283 | } while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && limit--); | 284 | |
285 | /* Release chip select if requested, transfer delays are | ||
286 | handled in pump_transfers */ | ||
287 | if (drv_data->cs_change) | ||
288 | drv_data->cs_control(SPI_CS_DEASSERT); | ||
284 | 289 | ||
285 | return limit; | 290 | /* Disable SPI to flush FIFOs */ |
291 | writel(control & ~SPI_CONTROL_SPIEN, regs + SPI_CONTROL); | ||
292 | writel(control, regs + SPI_CONTROL); | ||
286 | } | 293 | } |
287 | 294 | ||
288 | static void restore_state(struct driver_data *drv_data) | 295 | static void restore_state(struct driver_data *drv_data) |
@@ -570,6 +577,7 @@ static void giveback(struct spi_message *message, struct driver_data *drv_data) | |||
570 | writel(0, regs + SPI_INT_STATUS); | 577 | writel(0, regs + SPI_INT_STATUS); |
571 | writel(0, regs + SPI_DMA); | 578 | writel(0, regs + SPI_DMA); |
572 | 579 | ||
580 | /* Unconditioned deselct */ | ||
573 | drv_data->cs_control(SPI_CS_DEASSERT); | 581 | drv_data->cs_control(SPI_CS_DEASSERT); |
574 | 582 | ||
575 | message->state = NULL; | 583 | message->state = NULL; |
@@ -592,13 +600,10 @@ static void dma_err_handler(int channel, void *data, int errcode) | |||
592 | /* Disable both rx and tx dma channels */ | 600 | /* Disable both rx and tx dma channels */ |
593 | imx_dma_disable(drv_data->rx_channel); | 601 | imx_dma_disable(drv_data->rx_channel); |
594 | imx_dma_disable(drv_data->tx_channel); | 602 | imx_dma_disable(drv_data->tx_channel); |
595 | |||
596 | if (flush(drv_data) == 0) | ||
597 | dev_err(&drv_data->pdev->dev, | ||
598 | "dma_err_handler - flush failed\n"); | ||
599 | |||
600 | unmap_dma_buffers(drv_data); | 603 | unmap_dma_buffers(drv_data); |
601 | 604 | ||
605 | flush(drv_data); | ||
606 | |||
602 | msg->state = ERROR_STATE; | 607 | msg->state = ERROR_STATE; |
603 | tasklet_schedule(&drv_data->pump_transfers); | 608 | tasklet_schedule(&drv_data->pump_transfers); |
604 | } | 609 | } |
@@ -612,8 +617,7 @@ static void dma_tx_handler(int channel, void *data) | |||
612 | imx_dma_disable(channel); | 617 | imx_dma_disable(channel); |
613 | 618 | ||
614 | /* Now waits for TX FIFO empty */ | 619 | /* Now waits for TX FIFO empty */ |
615 | writel(readl(drv_data->regs + SPI_INT_STATUS) | SPI_INTEN_TE, | 620 | writel(SPI_INTEN_TE, drv_data->regs + SPI_INT_STATUS); |
616 | drv_data->regs + SPI_INT_STATUS); | ||
617 | } | 621 | } |
618 | 622 | ||
619 | static irqreturn_t dma_transfer(struct driver_data *drv_data) | 623 | static irqreturn_t dma_transfer(struct driver_data *drv_data) |
@@ -621,19 +625,18 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) | |||
621 | u32 status; | 625 | u32 status; |
622 | struct spi_message *msg = drv_data->cur_msg; | 626 | struct spi_message *msg = drv_data->cur_msg; |
623 | void __iomem *regs = drv_data->regs; | 627 | void __iomem *regs = drv_data->regs; |
624 | unsigned long limit; | ||
625 | 628 | ||
626 | status = readl(regs + SPI_INT_STATUS); | 629 | status = readl(regs + SPI_INT_STATUS); |
627 | 630 | ||
628 | if ((status & SPI_INTEN_RO) && (status & SPI_STATUS_RO)) { | 631 | if ((status & (SPI_INTEN_RO | SPI_STATUS_RO)) |
632 | == (SPI_INTEN_RO | SPI_STATUS_RO)) { | ||
629 | writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); | 633 | writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); |
630 | 634 | ||
635 | imx_dma_disable(drv_data->tx_channel); | ||
631 | imx_dma_disable(drv_data->rx_channel); | 636 | imx_dma_disable(drv_data->rx_channel); |
632 | unmap_dma_buffers(drv_data); | 637 | unmap_dma_buffers(drv_data); |
633 | 638 | ||
634 | if (flush(drv_data) == 0) | 639 | flush(drv_data); |
635 | dev_err(&drv_data->pdev->dev, | ||
636 | "dma_transfer - flush failed\n"); | ||
637 | 640 | ||
638 | dev_warn(&drv_data->pdev->dev, | 641 | dev_warn(&drv_data->pdev->dev, |
639 | "dma_transfer - fifo overun\n"); | 642 | "dma_transfer - fifo overun\n"); |
@@ -649,20 +652,17 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) | |||
649 | 652 | ||
650 | if (drv_data->rx) { | 653 | if (drv_data->rx) { |
651 | /* Wait end of transfer before read trailing data */ | 654 | /* Wait end of transfer before read trailing data */ |
652 | limit = loops_per_jiffy << 1; | 655 | while (readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) |
653 | while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && | 656 | cpu_relax(); |
654 | limit--); | ||
655 | |||
656 | if (limit == 0) | ||
657 | dev_err(&drv_data->pdev->dev, | ||
658 | "dma_transfer - end of tx failed\n"); | ||
659 | else | ||
660 | dev_dbg(&drv_data->pdev->dev, | ||
661 | "dma_transfer - end of tx\n"); | ||
662 | 657 | ||
663 | imx_dma_disable(drv_data->rx_channel); | 658 | imx_dma_disable(drv_data->rx_channel); |
664 | unmap_dma_buffers(drv_data); | 659 | unmap_dma_buffers(drv_data); |
665 | 660 | ||
661 | /* Release chip select if requested, transfer delays are | ||
662 | handled in pump_transfers() */ | ||
663 | if (drv_data->cs_change) | ||
664 | drv_data->cs_control(SPI_CS_DEASSERT); | ||
665 | |||
666 | /* Calculate number of trailing data and read them */ | 666 | /* Calculate number of trailing data and read them */ |
667 | dev_dbg(&drv_data->pdev->dev, | 667 | dev_dbg(&drv_data->pdev->dev, |
668 | "dma_transfer - test = 0x%08X\n", | 668 | "dma_transfer - test = 0x%08X\n", |
@@ -676,19 +676,12 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) | |||
676 | /* Write only transfer */ | 676 | /* Write only transfer */ |
677 | unmap_dma_buffers(drv_data); | 677 | unmap_dma_buffers(drv_data); |
678 | 678 | ||
679 | if (flush(drv_data) == 0) | 679 | flush(drv_data); |
680 | dev_err(&drv_data->pdev->dev, | ||
681 | "dma_transfer - flush failed\n"); | ||
682 | } | 680 | } |
683 | 681 | ||
684 | /* End of transfer, update total byte transfered */ | 682 | /* End of transfer, update total byte transfered */ |
685 | msg->actual_length += drv_data->len; | 683 | msg->actual_length += drv_data->len; |
686 | 684 | ||
687 | /* Release chip select if requested, transfer delays are | ||
688 | handled in pump_transfers() */ | ||
689 | if (drv_data->cs_change) | ||
690 | drv_data->cs_control(SPI_CS_DEASSERT); | ||
691 | |||
692 | /* Move to next transfer */ | 685 | /* Move to next transfer */ |
693 | msg->state = next_transfer(drv_data); | 686 | msg->state = next_transfer(drv_data); |
694 | 687 | ||
@@ -711,44 +704,43 @@ static irqreturn_t interrupt_wronly_transfer(struct driver_data *drv_data) | |||
711 | 704 | ||
712 | status = readl(regs + SPI_INT_STATUS); | 705 | status = readl(regs + SPI_INT_STATUS); |
713 | 706 | ||
714 | while (status & SPI_STATUS_TH) { | 707 | if (status & SPI_INTEN_TE) { |
708 | /* TXFIFO Empty Interrupt on the last transfered word */ | ||
709 | writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); | ||
715 | dev_dbg(&drv_data->pdev->dev, | 710 | dev_dbg(&drv_data->pdev->dev, |
716 | "interrupt_wronly_transfer - status = 0x%08X\n", status); | 711 | "interrupt_wronly_transfer - end of tx\n"); |
717 | 712 | ||
718 | /* Pump data */ | 713 | flush(drv_data); |
719 | if (write(drv_data)) { | ||
720 | writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN, | ||
721 | regs + SPI_INT_STATUS); | ||
722 | 714 | ||
723 | dev_dbg(&drv_data->pdev->dev, | 715 | /* Update total byte transfered */ |
724 | "interrupt_wronly_transfer - end of tx\n"); | 716 | msg->actual_length += drv_data->len; |
725 | 717 | ||
726 | if (flush(drv_data) == 0) | 718 | /* Move to next transfer */ |
727 | dev_err(&drv_data->pdev->dev, | 719 | msg->state = next_transfer(drv_data); |
728 | "interrupt_wronly_transfer - " | ||
729 | "flush failed\n"); | ||
730 | 720 | ||
731 | /* End of transfer, update total byte transfered */ | 721 | /* Schedule transfer tasklet */ |
732 | msg->actual_length += drv_data->len; | 722 | tasklet_schedule(&drv_data->pump_transfers); |
733 | 723 | ||
734 | /* Release chip select if requested, transfer delays are | 724 | return IRQ_HANDLED; |
735 | handled in pump_transfers */ | 725 | } else { |
736 | if (drv_data->cs_change) | 726 | while (status & SPI_STATUS_TH) { |
737 | drv_data->cs_control(SPI_CS_DEASSERT); | 727 | dev_dbg(&drv_data->pdev->dev, |
728 | "interrupt_wronly_transfer - status = 0x%08X\n", | ||
729 | status); | ||
738 | 730 | ||
739 | /* Move to next transfer */ | 731 | /* Pump data */ |
740 | msg->state = next_transfer(drv_data); | 732 | if (write(drv_data)) { |
733 | /* End of TXFIFO writes, | ||
734 | now wait until TXFIFO is empty */ | ||
735 | writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); | ||
736 | return IRQ_HANDLED; | ||
737 | } | ||
741 | 738 | ||
742 | /* Schedule transfer tasklet */ | 739 | status = readl(regs + SPI_INT_STATUS); |
743 | tasklet_schedule(&drv_data->pump_transfers); | ||
744 | 740 | ||
745 | return IRQ_HANDLED; | 741 | /* We did something */ |
742 | handled = IRQ_HANDLED; | ||
746 | } | 743 | } |
747 | |||
748 | status = readl(regs + SPI_INT_STATUS); | ||
749 | |||
750 | /* We did something */ | ||
751 | handled = IRQ_HANDLED; | ||
752 | } | 744 | } |
753 | 745 | ||
754 | return handled; | 746 | return handled; |
@@ -758,45 +750,31 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) | |||
758 | { | 750 | { |
759 | struct spi_message *msg = drv_data->cur_msg; | 751 | struct spi_message *msg = drv_data->cur_msg; |
760 | void __iomem *regs = drv_data->regs; | 752 | void __iomem *regs = drv_data->regs; |
761 | u32 status; | 753 | u32 status, control; |
762 | irqreturn_t handled = IRQ_NONE; | 754 | irqreturn_t handled = IRQ_NONE; |
763 | unsigned long limit; | 755 | unsigned long limit; |
764 | 756 | ||
765 | status = readl(regs + SPI_INT_STATUS); | 757 | status = readl(regs + SPI_INT_STATUS); |
766 | 758 | ||
767 | while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) { | 759 | if (status & SPI_INTEN_TE) { |
760 | /* TXFIFO Empty Interrupt on the last transfered word */ | ||
761 | writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); | ||
768 | dev_dbg(&drv_data->pdev->dev, | 762 | dev_dbg(&drv_data->pdev->dev, |
769 | "interrupt_transfer - status = 0x%08X\n", status); | 763 | "interrupt_transfer - end of tx\n"); |
770 | |||
771 | if (status & SPI_STATUS_RO) { | ||
772 | writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN, | ||
773 | regs + SPI_INT_STATUS); | ||
774 | |||
775 | dev_warn(&drv_data->pdev->dev, | ||
776 | "interrupt_transfer - fifo overun\n" | ||
777 | " data not yet written = %d\n" | ||
778 | " data not yet read = %d\n", | ||
779 | data_to_write(drv_data), | ||
780 | data_to_read(drv_data)); | ||
781 | |||
782 | if (flush(drv_data) == 0) | ||
783 | dev_err(&drv_data->pdev->dev, | ||
784 | "interrupt_transfer - flush failed\n"); | ||
785 | |||
786 | msg->state = ERROR_STATE; | ||
787 | tasklet_schedule(&drv_data->pump_transfers); | ||
788 | 764 | ||
789 | return IRQ_HANDLED; | 765 | if (msg->state == ERROR_STATE) { |
790 | } | 766 | /* RXFIFO overrun was detected and message aborted */ |
791 | 767 | flush(drv_data); | |
792 | /* Pump data */ | 768 | } else { |
793 | read(drv_data); | 769 | /* Wait for end of transaction */ |
794 | if (write(drv_data)) { | 770 | do { |
795 | writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN, | 771 | control = readl(regs + SPI_CONTROL); |
796 | regs + SPI_INT_STATUS); | 772 | } while (control & SPI_CONTROL_XCH); |
797 | 773 | ||
798 | dev_dbg(&drv_data->pdev->dev, | 774 | /* Release chip select if requested, transfer delays are |
799 | "interrupt_transfer - end of tx\n"); | 775 | handled in pump_transfers */ |
776 | if (drv_data->cs_change) | ||
777 | drv_data->cs_control(SPI_CS_DEASSERT); | ||
800 | 778 | ||
801 | /* Read trailing bytes */ | 779 | /* Read trailing bytes */ |
802 | limit = loops_per_jiffy << 1; | 780 | limit = loops_per_jiffy << 1; |
@@ -810,27 +788,54 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) | |||
810 | dev_dbg(&drv_data->pdev->dev, | 788 | dev_dbg(&drv_data->pdev->dev, |
811 | "interrupt_transfer - end of rx\n"); | 789 | "interrupt_transfer - end of rx\n"); |
812 | 790 | ||
813 | /* End of transfer, update total byte transfered */ | 791 | /* Update total byte transfered */ |
814 | msg->actual_length += drv_data->len; | 792 | msg->actual_length += drv_data->len; |
815 | 793 | ||
816 | /* Release chip select if requested, transfer delays are | ||
817 | handled in pump_transfers */ | ||
818 | if (drv_data->cs_change) | ||
819 | drv_data->cs_control(SPI_CS_DEASSERT); | ||
820 | |||
821 | /* Move to next transfer */ | 794 | /* Move to next transfer */ |
822 | msg->state = next_transfer(drv_data); | 795 | msg->state = next_transfer(drv_data); |
796 | } | ||
823 | 797 | ||
824 | /* Schedule transfer tasklet */ | 798 | /* Schedule transfer tasklet */ |
825 | tasklet_schedule(&drv_data->pump_transfers); | 799 | tasklet_schedule(&drv_data->pump_transfers); |
826 | 800 | ||
827 | return IRQ_HANDLED; | 801 | return IRQ_HANDLED; |
828 | } | 802 | } else { |
803 | while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) { | ||
804 | dev_dbg(&drv_data->pdev->dev, | ||
805 | "interrupt_transfer - status = 0x%08X\n", | ||
806 | status); | ||
807 | |||
808 | if (status & SPI_STATUS_RO) { | ||
809 | /* RXFIFO overrun, abort message end wait | ||
810 | until TXFIFO is empty */ | ||
811 | writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); | ||
812 | |||
813 | dev_warn(&drv_data->pdev->dev, | ||
814 | "interrupt_transfer - fifo overun\n" | ||
815 | " data not yet written = %d\n" | ||
816 | " data not yet read = %d\n", | ||
817 | data_to_write(drv_data), | ||
818 | data_to_read(drv_data)); | ||
819 | |||
820 | msg->state = ERROR_STATE; | ||
821 | |||
822 | return IRQ_HANDLED; | ||
823 | } | ||
829 | 824 | ||
830 | status = readl(regs + SPI_INT_STATUS); | 825 | /* Pump data */ |
826 | read(drv_data); | ||
827 | if (write(drv_data)) { | ||
828 | /* End of TXFIFO writes, | ||
829 | now wait until TXFIFO is empty */ | ||
830 | writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); | ||
831 | return IRQ_HANDLED; | ||
832 | } | ||
831 | 833 | ||
832 | /* We did something */ | 834 | status = readl(regs + SPI_INT_STATUS); |
833 | handled = IRQ_HANDLED; | 835 | |
836 | /* We did something */ | ||
837 | handled = IRQ_HANDLED; | ||
838 | } | ||
834 | } | 839 | } |
835 | 840 | ||
836 | return handled; | 841 | return handled; |