diff options
author | Andy Shevchenko <andriy.shevchenko@linux.intel.com> | 2013-01-16 08:48:50 -0500 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2013-01-20 23:49:21 -0500 |
commit | f8122a82d2eae8ef42de48829deed0ca9d9e1f17 (patch) | |
tree | ade059660ab99a330c93dffa90e7e4f4972270af /drivers/dma | |
parent | 855372c013bbad8369223f7c75242bd3c94f9345 (diff) |
dw_dmac: allocate dma descriptors from DMA_COHERENT memory
Currently descriptors are allocated from normal cacheable memory and that slows
down filling the descriptors, as we need to call cache_coherency routines
afterwards. It would be better to allocate memory for these descriptors from
DMA_COHERENT memory. This would make code much cleaner too.
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/dw_dmac.c | 81 | ||||
-rw-r--r-- | drivers/dma/dw_dmac_regs.h | 1 |
2 files changed, 18 insertions, 64 deletions
diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 28d5f01c350c..76301c4ba1ad 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | #include <linux/dmaengine.h> | 15 | #include <linux/dmaengine.h> |
16 | #include <linux/dma-mapping.h> | 16 | #include <linux/dma-mapping.h> |
17 | #include <linux/dmapool.h> | ||
17 | #include <linux/init.h> | 18 | #include <linux/init.h> |
18 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
19 | #include <linux/io.h> | 20 | #include <linux/io.h> |
@@ -91,14 +92,6 @@ static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) | |||
91 | 92 | ||
92 | /*----------------------------------------------------------------------*/ | 93 | /*----------------------------------------------------------------------*/ |
93 | 94 | ||
94 | /* | ||
95 | * Because we're not relying on writeback from the controller (it may not | ||
96 | * even be configured into the core!) we don't need to use dma_pool. These | ||
97 | * descriptors -- and associated data -- are cacheable. We do need to make | ||
98 | * sure their dcache entries are written back before handing them off to | ||
99 | * the controller, though. | ||
100 | */ | ||
101 | |||
102 | static struct device *chan2dev(struct dma_chan *chan) | 95 | static struct device *chan2dev(struct dma_chan *chan) |
103 | { | 96 | { |
104 | return &chan->dev->device; | 97 | return &chan->dev->device; |
@@ -137,19 +130,6 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) | |||
137 | return ret; | 130 | return ret; |
138 | } | 131 | } |
139 | 132 | ||
140 | static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) | ||
141 | { | ||
142 | struct dw_desc *child; | ||
143 | |||
144 | list_for_each_entry(child, &desc->tx_list, desc_node) | ||
145 | dma_sync_single_for_cpu(chan2parent(&dwc->chan), | ||
146 | child->txd.phys, sizeof(child->lli), | ||
147 | DMA_TO_DEVICE); | ||
148 | dma_sync_single_for_cpu(chan2parent(&dwc->chan), | ||
149 | desc->txd.phys, sizeof(desc->lli), | ||
150 | DMA_TO_DEVICE); | ||
151 | } | ||
152 | |||
153 | /* | 133 | /* |
154 | * Move a descriptor, including any children, to the free list. | 134 | * Move a descriptor, including any children, to the free list. |
155 | * `desc' must not be on any lists. | 135 | * `desc' must not be on any lists. |
@@ -161,8 +141,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) | |||
161 | if (desc) { | 141 | if (desc) { |
162 | struct dw_desc *child; | 142 | struct dw_desc *child; |
163 | 143 | ||
164 | dwc_sync_desc_for_cpu(dwc, desc); | ||
165 | |||
166 | spin_lock_irqsave(&dwc->lock, flags); | 144 | spin_lock_irqsave(&dwc->lock, flags); |
167 | list_for_each_entry(child, &desc->tx_list, desc_node) | 145 | list_for_each_entry(child, &desc->tx_list, desc_node) |
168 | dev_vdbg(chan2dev(&dwc->chan), | 146 | dev_vdbg(chan2dev(&dwc->chan), |
@@ -335,8 +313,6 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, | |||
335 | param = txd->callback_param; | 313 | param = txd->callback_param; |
336 | } | 314 | } |
337 | 315 | ||
338 | dwc_sync_desc_for_cpu(dwc, desc); | ||
339 | |||
340 | /* async_tx_ack */ | 316 | /* async_tx_ack */ |
341 | list_for_each_entry(child, &desc->tx_list, desc_node) | 317 | list_for_each_entry(child, &desc->tx_list, desc_node) |
342 | async_tx_ack(&child->txd); | 318 | async_tx_ack(&child->txd); |
@@ -770,25 +746,17 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, | |||
770 | first = desc; | 746 | first = desc; |
771 | } else { | 747 | } else { |
772 | prev->lli.llp = desc->txd.phys; | 748 | prev->lli.llp = desc->txd.phys; |
773 | dma_sync_single_for_device(chan2parent(chan), | ||
774 | prev->txd.phys, sizeof(prev->lli), | ||
775 | DMA_TO_DEVICE); | ||
776 | list_add_tail(&desc->desc_node, | 749 | list_add_tail(&desc->desc_node, |
777 | &first->tx_list); | 750 | &first->tx_list); |
778 | } | 751 | } |
779 | prev = desc; | 752 | prev = desc; |
780 | } | 753 | } |
781 | 754 | ||
782 | |||
783 | if (flags & DMA_PREP_INTERRUPT) | 755 | if (flags & DMA_PREP_INTERRUPT) |
784 | /* Trigger interrupt after last block */ | 756 | /* Trigger interrupt after last block */ |
785 | prev->lli.ctllo |= DWC_CTLL_INT_EN; | 757 | prev->lli.ctllo |= DWC_CTLL_INT_EN; |
786 | 758 | ||
787 | prev->lli.llp = 0; | 759 | prev->lli.llp = 0; |
788 | dma_sync_single_for_device(chan2parent(chan), | ||
789 | prev->txd.phys, sizeof(prev->lli), | ||
790 | DMA_TO_DEVICE); | ||
791 | |||
792 | first->txd.flags = flags; | 760 | first->txd.flags = flags; |
793 | first->len = len; | 761 | first->len = len; |
794 | 762 | ||
@@ -876,10 +844,6 @@ slave_sg_todev_fill_desc: | |||
876 | first = desc; | 844 | first = desc; |
877 | } else { | 845 | } else { |
878 | prev->lli.llp = desc->txd.phys; | 846 | prev->lli.llp = desc->txd.phys; |
879 | dma_sync_single_for_device(chan2parent(chan), | ||
880 | prev->txd.phys, | ||
881 | sizeof(prev->lli), | ||
882 | DMA_TO_DEVICE); | ||
883 | list_add_tail(&desc->desc_node, | 847 | list_add_tail(&desc->desc_node, |
884 | &first->tx_list); | 848 | &first->tx_list); |
885 | } | 849 | } |
@@ -938,10 +902,6 @@ slave_sg_fromdev_fill_desc: | |||
938 | first = desc; | 902 | first = desc; |
939 | } else { | 903 | } else { |
940 | prev->lli.llp = desc->txd.phys; | 904 | prev->lli.llp = desc->txd.phys; |
941 | dma_sync_single_for_device(chan2parent(chan), | ||
942 | prev->txd.phys, | ||
943 | sizeof(prev->lli), | ||
944 | DMA_TO_DEVICE); | ||
945 | list_add_tail(&desc->desc_node, | 905 | list_add_tail(&desc->desc_node, |
946 | &first->tx_list); | 906 | &first->tx_list); |
947 | } | 907 | } |
@@ -961,10 +921,6 @@ slave_sg_fromdev_fill_desc: | |||
961 | prev->lli.ctllo |= DWC_CTLL_INT_EN; | 921 | prev->lli.ctllo |= DWC_CTLL_INT_EN; |
962 | 922 | ||
963 | prev->lli.llp = 0; | 923 | prev->lli.llp = 0; |
964 | dma_sync_single_for_device(chan2parent(chan), | ||
965 | prev->txd.phys, sizeof(prev->lli), | ||
966 | DMA_TO_DEVICE); | ||
967 | |||
968 | first->len = total_len; | 924 | first->len = total_len; |
969 | 925 | ||
970 | return &first->txd; | 926 | return &first->txd; |
@@ -1118,7 +1074,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) | |||
1118 | struct dw_desc *desc; | 1074 | struct dw_desc *desc; |
1119 | int i; | 1075 | int i; |
1120 | unsigned long flags; | 1076 | unsigned long flags; |
1121 | int ret; | ||
1122 | 1077 | ||
1123 | dev_vdbg(chan2dev(chan), "%s\n", __func__); | 1078 | dev_vdbg(chan2dev(chan), "%s\n", __func__); |
1124 | 1079 | ||
@@ -1139,21 +1094,21 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) | |||
1139 | spin_lock_irqsave(&dwc->lock, flags); | 1094 | spin_lock_irqsave(&dwc->lock, flags); |
1140 | i = dwc->descs_allocated; | 1095 | i = dwc->descs_allocated; |
1141 | while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { | 1096 | while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { |
1097 | dma_addr_t phys; | ||
1098 | |||
1142 | spin_unlock_irqrestore(&dwc->lock, flags); | 1099 | spin_unlock_irqrestore(&dwc->lock, flags); |
1143 | 1100 | ||
1144 | desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); | 1101 | desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); |
1145 | if (!desc) | 1102 | if (!desc) |
1146 | goto err_desc_alloc; | 1103 | goto err_desc_alloc; |
1147 | 1104 | ||
1105 | memset(desc, 0, sizeof(struct dw_desc)); | ||
1106 | |||
1148 | INIT_LIST_HEAD(&desc->tx_list); | 1107 | INIT_LIST_HEAD(&desc->tx_list); |
1149 | dma_async_tx_descriptor_init(&desc->txd, chan); | 1108 | dma_async_tx_descriptor_init(&desc->txd, chan); |
1150 | desc->txd.tx_submit = dwc_tx_submit; | 1109 | desc->txd.tx_submit = dwc_tx_submit; |
1151 | desc->txd.flags = DMA_CTRL_ACK; | 1110 | desc->txd.flags = DMA_CTRL_ACK; |
1152 | desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, | 1111 | desc->txd.phys = phys; |
1153 | sizeof(desc->lli), DMA_TO_DEVICE); | ||
1154 | ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); | ||
1155 | if (ret) | ||
1156 | goto err_desc_alloc; | ||
1157 | 1112 | ||
1158 | dwc_desc_put(dwc, desc); | 1113 | dwc_desc_put(dwc, desc); |
1159 | 1114 | ||
@@ -1168,8 +1123,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) | |||
1168 | return i; | 1123 | return i; |
1169 | 1124 | ||
1170 | err_desc_alloc: | 1125 | err_desc_alloc: |
1171 | kfree(desc); | ||
1172 | |||
1173 | dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); | 1126 | dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); |
1174 | 1127 | ||
1175 | return i; | 1128 | return i; |
@@ -1204,9 +1157,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) | |||
1204 | 1157 | ||
1205 | list_for_each_entry_safe(desc, _desc, &list, desc_node) { | 1158 | list_for_each_entry_safe(desc, _desc, &list, desc_node) { |
1206 | dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); | 1159 | dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); |
1207 | dma_unmap_single(chan2parent(chan), desc->txd.phys, | 1160 | dma_pool_free(dw->desc_pool, desc, desc->txd.phys); |
1208 | sizeof(desc->lli), DMA_TO_DEVICE); | ||
1209 | kfree(desc); | ||
1210 | } | 1161 | } |
1211 | 1162 | ||
1212 | dev_vdbg(chan2dev(chan), "%s: done\n", __func__); | 1163 | dev_vdbg(chan2dev(chan), "%s: done\n", __func__); |
@@ -1451,20 +1402,14 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, | |||
1451 | desc->lli.ctlhi = (period_len >> reg_width); | 1402 | desc->lli.ctlhi = (period_len >> reg_width); |
1452 | cdesc->desc[i] = desc; | 1403 | cdesc->desc[i] = desc; |
1453 | 1404 | ||
1454 | if (last) { | 1405 | if (last) |
1455 | last->lli.llp = desc->txd.phys; | 1406 | last->lli.llp = desc->txd.phys; |
1456 | dma_sync_single_for_device(chan2parent(chan), | ||
1457 | last->txd.phys, sizeof(last->lli), | ||
1458 | DMA_TO_DEVICE); | ||
1459 | } | ||
1460 | 1407 | ||
1461 | last = desc; | 1408 | last = desc; |
1462 | } | 1409 | } |
1463 | 1410 | ||
1464 | /* lets make a cyclic list */ | 1411 | /* lets make a cyclic list */ |
1465 | last->lli.llp = cdesc->desc[0]->txd.phys; | 1412 | last->lli.llp = cdesc->desc[0]->txd.phys; |
1466 | dma_sync_single_for_device(chan2parent(chan), last->txd.phys, | ||
1467 | sizeof(last->lli), DMA_TO_DEVICE); | ||
1468 | 1413 | ||
1469 | dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " | 1414 | dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " |
1470 | "period %zu periods %d\n", (unsigned long long)buf_addr, | 1415 | "period %zu periods %d\n", (unsigned long long)buf_addr, |
@@ -1722,6 +1667,14 @@ static int dw_probe(struct platform_device *pdev) | |||
1722 | 1667 | ||
1723 | platform_set_drvdata(pdev, dw); | 1668 | platform_set_drvdata(pdev, dw); |
1724 | 1669 | ||
1670 | /* create a pool of consistent memory blocks for hardware descriptors */ | ||
1671 | dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, | ||
1672 | sizeof(struct dw_desc), 4, 0); | ||
1673 | if (!dw->desc_pool) { | ||
1674 | dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); | ||
1675 | return -ENOMEM; | ||
1676 | } | ||
1677 | |||
1725 | tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); | 1678 | tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); |
1726 | 1679 | ||
1727 | INIT_LIST_HEAD(&dw->dma.channels); | 1680 | INIT_LIST_HEAD(&dw->dma.channels); |
diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 577f2dd35882..fef296de4af1 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h | |||
@@ -235,6 +235,7 @@ static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) | |||
235 | struct dw_dma { | 235 | struct dw_dma { |
236 | struct dma_device dma; | 236 | struct dma_device dma; |
237 | void __iomem *regs; | 237 | void __iomem *regs; |
238 | struct dma_pool *desc_pool; | ||
238 | struct tasklet_struct tasklet; | 239 | struct tasklet_struct tasklet; |
239 | struct clk *clk; | 240 | struct clk *clk; |
240 | 241 | ||