diff options
author | Chen-Yu Tsai <wens@csie.org> | 2014-11-06 23:15:46 -0500 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2014-11-17 03:20:36 -0500 |
commit | 25a37c2f12351ada1e42d0663480a182f4e301db (patch) | |
tree | b00403764639686a0e6e5d23c9935eff14606399 /drivers/dma | |
parent | 19bfc7726506b48620106b0bb4d32eb37cbcb355 (diff) |
dmaengine: sun6i: support parameterized compatible strings
This patch adds support for hardware parameters tied to compatible
strings, so similar hardware can reuse the driver.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/sun6i-dma.c | 94 |
1 files changed, 60 insertions, 34 deletions
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index a00157afc5b8..531abbf68a9d 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/of_dma.h> | 20 | #include <linux/of_dma.h> |
21 | #include <linux/of_device.h> | ||
21 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
22 | #include <linux/reset.h> | 23 | #include <linux/reset.h> |
23 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
@@ -26,24 +27,6 @@ | |||
26 | #include "virt-dma.h" | 27 | #include "virt-dma.h" |
27 | 28 | ||
28 | /* | 29 | /* |
29 | * There's 16 physical channels that can work in parallel. | ||
30 | * | ||
31 | * However we have 30 different endpoints for our requests. | ||
32 | * | ||
33 | * Since the channels are able to handle only an unidirectional | ||
34 | * transfer, we need to allocate more virtual channels so that | ||
35 | * everyone can grab one channel. | ||
36 | * | ||
37 | * Some devices can't work in both direction (mostly because it | ||
38 | * wouldn't make sense), so we have a bit fewer virtual channels than | ||
39 | * 2 channels per endpoints. | ||
40 | */ | ||
41 | |||
42 | #define NR_MAX_CHANNELS 16 | ||
43 | #define NR_MAX_REQUESTS 30 | ||
44 | #define NR_MAX_VCHANS 53 | ||
45 | |||
46 | /* | ||
47 | * Common registers | 30 | * Common registers |
48 | */ | 31 | */ |
49 | #define DMA_IRQ_EN(x) ((x) * 0x04) | 32 | #define DMA_IRQ_EN(x) ((x) * 0x04) |
@@ -102,6 +85,19 @@ | |||
102 | #define DRQ_SDRAM 1 | 85 | #define DRQ_SDRAM 1 |
103 | 86 | ||
104 | /* | 87 | /* |
88 | * Hardware channels / ports representation | ||
89 | * | ||
90 | * The hardware is used in several SoCs, with differing numbers | ||
91 | * of channels and endpoints. This structure ties those numbers | ||
92 | * to a certain compatible string. | ||
93 | */ | ||
94 | struct sun6i_dma_config { | ||
95 | u32 nr_max_channels; | ||
96 | u32 nr_max_requests; | ||
97 | u32 nr_max_vchans; | ||
98 | }; | ||
99 | |||
100 | /* | ||
105 | * Hardware representation of the LLI | 101 | * Hardware representation of the LLI |
106 | * | 102 | * |
107 | * The hardware will be fed the physical address of this structure, | 103 | * The hardware will be fed the physical address of this structure, |
@@ -159,6 +155,7 @@ struct sun6i_dma_dev { | |||
159 | struct dma_pool *pool; | 155 | struct dma_pool *pool; |
160 | struct sun6i_pchan *pchans; | 156 | struct sun6i_pchan *pchans; |
161 | struct sun6i_vchan *vchans; | 157 | struct sun6i_vchan *vchans; |
158 | const struct sun6i_dma_config *cfg; | ||
162 | }; | 159 | }; |
163 | 160 | ||
164 | static struct device *chan2dev(struct dma_chan *chan) | 161 | static struct device *chan2dev(struct dma_chan *chan) |
@@ -432,6 +429,7 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan) | |||
432 | static void sun6i_dma_tasklet(unsigned long data) | 429 | static void sun6i_dma_tasklet(unsigned long data) |
433 | { | 430 | { |
434 | struct sun6i_dma_dev *sdev = (struct sun6i_dma_dev *)data; | 431 | struct sun6i_dma_dev *sdev = (struct sun6i_dma_dev *)data; |
432 | const struct sun6i_dma_config *cfg = sdev->cfg; | ||
435 | struct sun6i_vchan *vchan; | 433 | struct sun6i_vchan *vchan; |
436 | struct sun6i_pchan *pchan; | 434 | struct sun6i_pchan *pchan; |
437 | unsigned int pchan_alloc = 0; | 435 | unsigned int pchan_alloc = 0; |
@@ -459,7 +457,7 @@ static void sun6i_dma_tasklet(unsigned long data) | |||
459 | } | 457 | } |
460 | 458 | ||
461 | spin_lock_irq(&sdev->lock); | 459 | spin_lock_irq(&sdev->lock); |
462 | for (pchan_idx = 0; pchan_idx < NR_MAX_CHANNELS; pchan_idx++) { | 460 | for (pchan_idx = 0; pchan_idx < cfg->nr_max_channels; pchan_idx++) { |
463 | pchan = &sdev->pchans[pchan_idx]; | 461 | pchan = &sdev->pchans[pchan_idx]; |
464 | 462 | ||
465 | if (pchan->vchan || list_empty(&sdev->pending)) | 463 | if (pchan->vchan || list_empty(&sdev->pending)) |
@@ -480,7 +478,7 @@ static void sun6i_dma_tasklet(unsigned long data) | |||
480 | } | 478 | } |
481 | spin_unlock_irq(&sdev->lock); | 479 | spin_unlock_irq(&sdev->lock); |
482 | 480 | ||
483 | for (pchan_idx = 0; pchan_idx < NR_MAX_CHANNELS; pchan_idx++) { | 481 | for (pchan_idx = 0; pchan_idx < cfg->nr_max_channels; pchan_idx++) { |
484 | if (!(pchan_alloc & BIT(pchan_idx))) | 482 | if (!(pchan_alloc & BIT(pchan_idx))) |
485 | continue; | 483 | continue; |
486 | 484 | ||
@@ -502,7 +500,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id) | |||
502 | int i, j, ret = IRQ_NONE; | 500 | int i, j, ret = IRQ_NONE; |
503 | u32 status; | 501 | u32 status; |
504 | 502 | ||
505 | for (i = 0; i < 2; i++) { | 503 | for (i = 0; i < sdev->cfg->nr_max_channels / DMA_IRQ_CHAN_NR; i++) { |
506 | status = readl(sdev->base + DMA_IRQ_STAT(i)); | 504 | status = readl(sdev->base + DMA_IRQ_STAT(i)); |
507 | if (!status) | 505 | if (!status) |
508 | continue; | 506 | continue; |
@@ -512,7 +510,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id) | |||
512 | 510 | ||
513 | writel(status, sdev->base + DMA_IRQ_STAT(i)); | 511 | writel(status, sdev->base + DMA_IRQ_STAT(i)); |
514 | 512 | ||
515 | for (j = 0; (j < 8) && status; j++) { | 513 | for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) { |
516 | if (status & DMA_IRQ_QUEUE) { | 514 | if (status & DMA_IRQ_QUEUE) { |
517 | pchan = sdev->pchans + j; | 515 | pchan = sdev->pchans + j; |
518 | vchan = pchan->vchan; | 516 | vchan = pchan->vchan; |
@@ -525,7 +523,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id) | |||
525 | } | 523 | } |
526 | } | 524 | } |
527 | 525 | ||
528 | status = status >> 4; | 526 | status = status >> DMA_IRQ_CHAN_WIDTH; |
529 | } | 527 | } |
530 | 528 | ||
531 | if (!atomic_read(&sdev->tasklet_shutdown)) | 529 | if (!atomic_read(&sdev->tasklet_shutdown)) |
@@ -817,7 +815,7 @@ static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec, | |||
817 | struct dma_chan *chan; | 815 | struct dma_chan *chan; |
818 | u8 port = dma_spec->args[0]; | 816 | u8 port = dma_spec->args[0]; |
819 | 817 | ||
820 | if (port > NR_MAX_REQUESTS) | 818 | if (port > sdev->cfg->nr_max_requests) |
821 | return NULL; | 819 | return NULL; |
822 | 820 | ||
823 | chan = dma_get_any_slave_channel(&sdev->slave); | 821 | chan = dma_get_any_slave_channel(&sdev->slave); |
@@ -850,7 +848,7 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev) | |||
850 | { | 848 | { |
851 | int i; | 849 | int i; |
852 | 850 | ||
853 | for (i = 0; i < NR_MAX_VCHANS; i++) { | 851 | for (i = 0; i < sdev->cfg->nr_max_vchans; i++) { |
854 | struct sun6i_vchan *vchan = &sdev->vchans[i]; | 852 | struct sun6i_vchan *vchan = &sdev->vchans[i]; |
855 | 853 | ||
856 | list_del(&vchan->vc.chan.device_node); | 854 | list_del(&vchan->vc.chan.device_node); |
@@ -858,8 +856,36 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev) | |||
858 | } | 856 | } |
859 | } | 857 | } |
860 | 858 | ||
859 | /* | ||
860 | * For A31: | ||
861 | * | ||
862 | * There's 16 physical channels that can work in parallel. | ||
863 | * | ||
864 | * However we have 30 different endpoints for our requests. | ||
865 | * | ||
866 | * Since the channels are able to handle only an unidirectional | ||
867 | * transfer, we need to allocate more virtual channels so that | ||
868 | * everyone can grab one channel. | ||
869 | * | ||
870 | * Some devices can't work in both direction (mostly because it | ||
871 | * wouldn't make sense), so we have a bit fewer virtual channels than | ||
872 | * 2 channels per endpoints. | ||
873 | */ | ||
874 | |||
875 | static struct sun6i_dma_config sun6i_a31_dma_cfg = { | ||
876 | .nr_max_channels = 16, | ||
877 | .nr_max_requests = 30, | ||
878 | .nr_max_vchans = 53, | ||
879 | }; | ||
880 | |||
881 | static struct of_device_id sun6i_dma_match[] = { | ||
882 | { .compatible = "allwinner,sun6i-a31-dma", .data = &sun6i_a31_dma_cfg }, | ||
883 | { /* sentinel */ } | ||
884 | }; | ||
885 | |||
861 | static int sun6i_dma_probe(struct platform_device *pdev) | 886 | static int sun6i_dma_probe(struct platform_device *pdev) |
862 | { | 887 | { |
888 | const struct of_device_id *device; | ||
863 | struct sun6i_dma_dev *sdc; | 889 | struct sun6i_dma_dev *sdc; |
864 | struct resource *res; | 890 | struct resource *res; |
865 | int ret, i; | 891 | int ret, i; |
@@ -868,6 +894,11 @@ static int sun6i_dma_probe(struct platform_device *pdev) | |||
868 | if (!sdc) | 894 | if (!sdc) |
869 | return -ENOMEM; | 895 | return -ENOMEM; |
870 | 896 | ||
897 | device = of_match_device(sun6i_dma_match, &pdev->dev); | ||
898 | if (!device) | ||
899 | return -ENODEV; | ||
900 | sdc->cfg = device->data; | ||
901 | |||
871 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 902 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
872 | sdc->base = devm_ioremap_resource(&pdev->dev, res); | 903 | sdc->base = devm_ioremap_resource(&pdev->dev, res); |
873 | if (IS_ERR(sdc->base)) | 904 | if (IS_ERR(sdc->base)) |
@@ -917,26 +948,26 @@ static int sun6i_dma_probe(struct platform_device *pdev) | |||
917 | 948 | ||
918 | sdc->slave.dev = &pdev->dev; | 949 | sdc->slave.dev = &pdev->dev; |
919 | 950 | ||
920 | sdc->pchans = devm_kcalloc(&pdev->dev, NR_MAX_CHANNELS, | 951 | sdc->pchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_channels, |
921 | sizeof(struct sun6i_pchan), GFP_KERNEL); | 952 | sizeof(struct sun6i_pchan), GFP_KERNEL); |
922 | if (!sdc->pchans) | 953 | if (!sdc->pchans) |
923 | return -ENOMEM; | 954 | return -ENOMEM; |
924 | 955 | ||
925 | sdc->vchans = devm_kcalloc(&pdev->dev, NR_MAX_VCHANS, | 956 | sdc->vchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_vchans, |
926 | sizeof(struct sun6i_vchan), GFP_KERNEL); | 957 | sizeof(struct sun6i_vchan), GFP_KERNEL); |
927 | if (!sdc->vchans) | 958 | if (!sdc->vchans) |
928 | return -ENOMEM; | 959 | return -ENOMEM; |
929 | 960 | ||
930 | tasklet_init(&sdc->task, sun6i_dma_tasklet, (unsigned long)sdc); | 961 | tasklet_init(&sdc->task, sun6i_dma_tasklet, (unsigned long)sdc); |
931 | 962 | ||
932 | for (i = 0; i < NR_MAX_CHANNELS; i++) { | 963 | for (i = 0; i < sdc->cfg->nr_max_channels; i++) { |
933 | struct sun6i_pchan *pchan = &sdc->pchans[i]; | 964 | struct sun6i_pchan *pchan = &sdc->pchans[i]; |
934 | 965 | ||
935 | pchan->idx = i; | 966 | pchan->idx = i; |
936 | pchan->base = sdc->base + 0x100 + i * 0x40; | 967 | pchan->base = sdc->base + 0x100 + i * 0x40; |
937 | } | 968 | } |
938 | 969 | ||
939 | for (i = 0; i < NR_MAX_VCHANS; i++) { | 970 | for (i = 0; i < sdc->cfg->nr_max_vchans; i++) { |
940 | struct sun6i_vchan *vchan = &sdc->vchans[i]; | 971 | struct sun6i_vchan *vchan = &sdc->vchans[i]; |
941 | 972 | ||
942 | INIT_LIST_HEAD(&vchan->node); | 973 | INIT_LIST_HEAD(&vchan->node); |
@@ -1008,11 +1039,6 @@ static int sun6i_dma_remove(struct platform_device *pdev) | |||
1008 | return 0; | 1039 | return 0; |
1009 | } | 1040 | } |
1010 | 1041 | ||
1011 | static struct of_device_id sun6i_dma_match[] = { | ||
1012 | { .compatible = "allwinner,sun6i-a31-dma" }, | ||
1013 | { /* sentinel */ } | ||
1014 | }; | ||
1015 | |||
1016 | static struct platform_driver sun6i_dma_driver = { | 1042 | static struct platform_driver sun6i_dma_driver = { |
1017 | .probe = sun6i_dma_probe, | 1043 | .probe = sun6i_dma_probe, |
1018 | .remove = sun6i_dma_remove, | 1044 | .remove = sun6i_dma_remove, |