aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
authorChen-Yu Tsai <wens@csie.org>2014-11-06 23:15:46 -0500
committerVinod Koul <vinod.koul@intel.com>2014-11-17 03:20:36 -0500
commit25a37c2f12351ada1e42d0663480a182f4e301db (patch)
treeb00403764639686a0e6e5d23c9935eff14606399 /drivers/dma
parent19bfc7726506b48620106b0bb4d32eb37cbcb355 (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.c94
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 */
94struct 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
164static struct device *chan2dev(struct dma_chan *chan) 161static struct device *chan2dev(struct dma_chan *chan)
@@ -432,6 +429,7 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
432static void sun6i_dma_tasklet(unsigned long data) 429static 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
875static 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
881static struct of_device_id sun6i_dma_match[] = {
882 { .compatible = "allwinner,sun6i-a31-dma", .data = &sun6i_a31_dma_cfg },
883 { /* sentinel */ }
884};
885
861static int sun6i_dma_probe(struct platform_device *pdev) 886static 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
1011static struct of_device_id sun6i_dma_match[] = {
1012 { .compatible = "allwinner,sun6i-a31-dma" },
1013 { /* sentinel */ }
1014};
1015
1016static struct platform_driver sun6i_dma_driver = { 1042static 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,