diff options
| author | Linus Walleij <linus.walleij@stericsson.com> | 2010-09-29 04:31:35 -0400 |
|---|---|---|
| committer | Grant Likely <grant.likely@secretlab.ca> | 2010-10-12 23:37:37 -0400 |
| commit | b1b6b9aa6fd32db97469e65d301ebc32dcd67992 (patch) | |
| tree | 2a6f3f7d34a50a574854ea7495476f21fd6fcfce | |
| parent | cdbc8f042f4f2568bb58ba8bd50d0692f3059417 (diff) | |
spi/pl022: add PrimeCell generic DMA support
This extends the PL022 SSP/SPI driver with generic DMA engine
support using the PrimeCell DMA engine interface. Also fix up the
test code for the U300 platform.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
| -rw-r--r-- | drivers/spi/amba-pl022.c | 516 | ||||
| -rw-r--r-- | include/linux/amba/pl022.h | 6 |
2 files changed, 434 insertions, 88 deletions
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 8cdddc97325..db0c67908d2 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c | |||
| @@ -27,7 +27,6 @@ | |||
| 27 | /* | 27 | /* |
| 28 | * TODO: | 28 | * TODO: |
| 29 | * - add timeout on polled transfers | 29 | * - add timeout on polled transfers |
| 30 | * - add generic DMA framework support | ||
| 31 | */ | 30 | */ |
| 32 | 31 | ||
| 33 | #include <linux/init.h> | 32 | #include <linux/init.h> |
| @@ -45,6 +44,9 @@ | |||
| 45 | #include <linux/amba/pl022.h> | 44 | #include <linux/amba/pl022.h> |
| 46 | #include <linux/io.h> | 45 | #include <linux/io.h> |
| 47 | #include <linux/slab.h> | 46 | #include <linux/slab.h> |
| 47 | #include <linux/dmaengine.h> | ||
| 48 | #include <linux/dma-mapping.h> | ||
| 49 | #include <linux/scatterlist.h> | ||
| 48 | 50 | ||
| 49 | /* | 51 | /* |
| 50 | * This macro is used to define some register default values. | 52 | * This macro is used to define some register default values. |
| @@ -381,6 +383,14 @@ struct pl022 { | |||
| 381 | enum ssp_reading read; | 383 | enum ssp_reading read; |
| 382 | enum ssp_writing write; | 384 | enum ssp_writing write; |
| 383 | u32 exp_fifo_level; | 385 | u32 exp_fifo_level; |
| 386 | /* DMA settings */ | ||
| 387 | #ifdef CONFIG_DMA_ENGINE | ||
| 388 | struct dma_chan *dma_rx_channel; | ||
| 389 | struct dma_chan *dma_tx_channel; | ||
| 390 | struct sg_table sgt_rx; | ||
| 391 | struct sg_table sgt_tx; | ||
| 392 | char *dummypage; | ||
| 393 | #endif | ||
| 384 | }; | 394 | }; |
| 385 | 395 | ||
| 386 | /** | 396 | /** |
| @@ -406,7 +416,7 @@ struct chip_data { | |||
| 406 | u16 dmacr; | 416 | u16 dmacr; |
| 407 | u16 cpsr; | 417 | u16 cpsr; |
| 408 | u8 n_bytes; | 418 | u8 n_bytes; |
| 409 | u8 enable_dma:1; | 419 | bool enable_dma; |
| 410 | enum ssp_reading read; | 420 | enum ssp_reading read; |
| 411 | enum ssp_writing write; | 421 | enum ssp_writing write; |
| 412 | void (*cs_control) (u32 command); | 422 | void (*cs_control) (u32 command); |
| @@ -763,6 +773,371 @@ static void *next_transfer(struct pl022 *pl022) | |||
| 763 | } | 773 | } |
| 764 | return STATE_DONE; | 774 | return STATE_DONE; |
| 765 | } | 775 | } |
| 776 | |||
| 777 | /* | ||
| 778 | * This DMA functionality is only compiled in if we have | ||
| 779 | * access to the generic DMA devices/DMA engine. | ||
| 780 | */ | ||
| 781 | #ifdef CONFIG_DMA_ENGINE | ||
| 782 | static void unmap_free_dma_scatter(struct pl022 *pl022) | ||
| 783 | { | ||
| 784 | /* Unmap and free the SG tables */ | ||
| 785 | dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl, | ||
| 786 | pl022->sgt_tx.nents, DMA_TO_DEVICE); | ||
| 787 | dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl, | ||
| 788 | pl022->sgt_rx.nents, DMA_FROM_DEVICE); | ||
| 789 | sg_free_table(&pl022->sgt_rx); | ||
| 790 | sg_free_table(&pl022->sgt_tx); | ||
| 791 | } | ||
| 792 | |||
| 793 | static void dma_callback(void *data) | ||
| 794 | { | ||
| 795 | struct pl022 *pl022 = data; | ||
| 796 | struct spi_message *msg = pl022->cur_msg; | ||
| 797 | |||
| 798 | BUG_ON(!pl022->sgt_rx.sgl); | ||
| 799 | |||
| 800 | #ifdef VERBOSE_DEBUG | ||
| 801 | /* | ||
| 802 | * Optionally dump out buffers to inspect contents, this is | ||
| 803 | * good if you want to convince yourself that the loopback | ||
| 804 | * read/write contents are the same, when adopting to a new | ||
| 805 | * DMA engine. | ||
| 806 | */ | ||
| 807 | { | ||
| 808 | struct scatterlist *sg; | ||
| 809 | unsigned int i; | ||
| 810 | |||
| 811 | dma_sync_sg_for_cpu(&pl022->adev->dev, | ||
| 812 | pl022->sgt_rx.sgl, | ||
| 813 | pl022->sgt_rx.nents, | ||
| 814 | DMA_FROM_DEVICE); | ||
| 815 | |||
| 816 | for_each_sg(pl022->sgt_rx.sgl, sg, pl022->sgt_rx.nents, i) { | ||
| 817 | dev_dbg(&pl022->adev->dev, "SPI RX SG ENTRY: %d", i); | ||
| 818 | print_hex_dump(KERN_ERR, "SPI RX: ", | ||
| 819 | DUMP_PREFIX_OFFSET, | ||
| 820 | 16, | ||
| 821 | 1, | ||
| 822 | sg_virt(sg), | ||
| 823 | sg_dma_len(sg), | ||
| 824 | 1); | ||
| 825 | } | ||
| 826 | for_each_sg(pl022->sgt_tx.sgl, sg, pl022->sgt_tx.nents, i) { | ||
| 827 | dev_dbg(&pl022->adev->dev, "SPI TX SG ENTRY: %d", i); | ||
| 828 | print_hex_dump(KERN_ERR, "SPI TX: ", | ||
| 829 | DUMP_PREFIX_OFFSET, | ||
| 830 | 16, | ||
| 831 | 1, | ||
| 832 | sg_virt(sg), | ||
| 833 | sg_dma_len(sg), | ||
| 834 | 1); | ||
| 835 | } | ||
| 836 | } | ||
| 837 | #endif | ||
| 838 | |||
| 839 | unmap_free_dma_scatter(pl022); | ||
| 840 | |||
| 841 | /* Update total bytes transfered */ | ||
| 842 | msg->actual_length += pl022->cur_transfer->len; | ||
| 843 | if (pl022->cur_transfer->cs_change) | ||
| 844 | pl022->cur_chip-> | ||
| 845 | cs_control(SSP_CHIP_DESELECT); | ||
| 846 | |||
| 847 | /* Move to next transfer */ | ||
| 848 | msg->state = next_transfer(pl022); | ||
| 849 | tasklet_schedule(&pl022->pump_transfers); | ||
| 850 | } | ||
| 851 | |||
| 852 | static void setup_dma_scatter(struct pl022 *pl022, | ||
| 853 | void *buffer, | ||
| 854 | unsigned int length, | ||
| 855 | struct sg_table *sgtab) | ||
| 856 | { | ||
| 857 | struct scatterlist *sg; | ||
| 858 | int bytesleft = length; | ||
| 859 | void *bufp = buffer; | ||
| 860 | int mapbytes; | ||
| 861 | int i; | ||
| 862 | |||
| 863 | if (buffer) { | ||
| 864 | for_each_sg(sgtab->sgl, sg, sgtab->nents, i) { | ||
| 865 | /* | ||
| 866 | * If there are less bytes left than what fits | ||
| 867 | * in the current page (plus page alignment offset) | ||
| 868 | * we just feed in this, else we stuff in as much | ||
| 869 | * as we can. | ||
| 870 | */ | ||
| 871 | if (bytesleft < (PAGE_SIZE - offset_in_page(bufp))) | ||
| 872 | mapbytes = bytesleft; | ||
| 873 | else | ||
| 874 | mapbytes = PAGE_SIZE - offset_in_page(bufp); | ||
| 875 | sg_set_page(sg, virt_to_page(bufp), | ||
| 876 | mapbytes, offset_in_page(bufp)); | ||
| 877 | bufp += mapbytes; | ||
| 878 | bytesleft -= mapbytes; | ||
| 879 | dev_dbg(&pl022->adev->dev, | ||
| 880 | "set RX/TX target page @ %p, %d bytes, %d left\n", | ||
| 881 | bufp, mapbytes, bytesleft); | ||
| 882 | } | ||
| 883 | } else { | ||
| 884 | /* Map the dummy buffer on every page */ | ||
| 885 | for_each_sg(sgtab->sgl, sg, sgtab->nents, i) { | ||
| 886 | if (bytesleft < PAGE_SIZE) | ||
| 887 | mapbytes = bytesleft; | ||
| 888 | else | ||
| 889 | mapbytes = PAGE_SIZE; | ||
| 890 | sg_set_page(sg, virt_to_page(pl022->dummypage), | ||
| 891 | mapbytes, 0); | ||
| 892 | bytesleft -= mapbytes; | ||
| 893 | dev_dbg(&pl022->adev->dev, | ||
| 894 | "set RX/TX to dummy page %d bytes, %d left\n", | ||
| 895 | mapbytes, bytesleft); | ||
| 896 | |||
| 897 | } | ||
| 898 | } | ||
| 899 | BUG_ON(bytesleft); | ||
| 900 | } | ||
| 901 | |||
| 902 | /** | ||
| 903 | * configure_dma - configures the channels for the next transfer | ||
| 904 | * @pl022: SSP driver's private data structure | ||
| 905 | */ | ||
| 906 | static int configure_dma(struct pl022 *pl022) | ||
| 907 | { | ||
| 908 | struct dma_slave_config rx_conf = { | ||
| 909 | .src_addr = SSP_DR(pl022->phybase), | ||
| 910 | .direction = DMA_FROM_DEVICE, | ||
| 911 | .src_maxburst = pl022->vendor->fifodepth >> 1, | ||
| 912 | }; | ||
| 913 | struct dma_slave_config tx_conf = { | ||
| 914 | .dst_addr = SSP_DR(pl022->phybase), | ||
| 915 | .direction = DMA_TO_DEVICE, | ||
| 916 | .dst_maxburst = pl022->vendor->fifodepth >> 1, | ||
| 917 | }; | ||
| 918 | unsigned int pages; | ||
| 919 | int ret; | ||
| 920 | int sglen; | ||
| 921 | struct dma_chan *rxchan = pl022->dma_rx_channel; | ||
| 922 | struct dma_chan *txchan = pl022->dma_tx_channel; | ||
| 923 | struct dma_async_tx_descriptor *rxdesc; | ||
| 924 | struct dma_async_tx_descriptor *txdesc; | ||
| 925 | dma_cookie_t cookie; | ||
| 926 | |||
| 927 | /* Check that the channels are available */ | ||
| 928 | if (!rxchan || !txchan) | ||
| 929 | return -ENODEV; | ||
| 930 | |||
