summaryrefslogtreecommitdiff
path: root/drivers/dma/sun6i-dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma/sun6i-dma.c')
-rw-r--r--drivers/dma/sun6i-dma.c108
1 files changed, 66 insertions, 42 deletions
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 5cadd4d2b824..2215ff877bf7 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -9,15 +9,17 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_dma.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/types.h>
#include "virt-dma.h"
@@ -90,6 +92,14 @@
#define DMA_CHAN_CUR_PARA 0x1c
+/*
+ * LLI address mangling
+ *
+ * The LLI link physical address is also mangled, but we avoid dealing
+ * with that by allocating LLIs from the DMA32 zone.
+ */
+#define SRC_HIGH_ADDR(x) (((x) & 0x3U) << 16)
+#define DST_HIGH_ADDR(x) (((x) & 0x3U) << 18)
/*
* Various hardware related defines
@@ -132,6 +142,7 @@ struct sun6i_dma_config {
u32 dst_burst_lengths;
u32 src_addr_widths;
u32 dst_addr_widths;
+ bool has_high_addr;
bool has_mbus_clk;
};
@@ -241,9 +252,7 @@ static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
struct sun6i_pchan *pchan)
{
- phys_addr_t reg = virt_to_phys(pchan->base);
-
- dev_dbg(sdev->slave.dev, "Chan %d reg: %pa\n"
+ dev_dbg(sdev->slave.dev, "Chan %d reg:\n"
"\t___en(%04x): \t0x%08x\n"
"\tpause(%04x): \t0x%08x\n"
"\tstart(%04x): \t0x%08x\n"
@@ -252,7 +261,7 @@ static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
"\t__dst(%04x): \t0x%08x\n"
"\tcount(%04x): \t0x%08x\n"
"\t_para(%04x): \t0x%08x\n\n",
- pchan->idx, &reg,
+ pchan->idx,
DMA_CHAN_ENABLE,
readl(pchan->base + DMA_CHAN_ENABLE),
DMA_CHAN_PAUSE,
@@ -385,17 +394,16 @@ static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev,
}
static inline void sun6i_dma_dump_lli(struct sun6i_vchan *vchan,
- struct sun6i_dma_lli *lli)
+ struct sun6i_dma_lli *v_lli,
+ dma_addr_t p_lli)
{
- phys_addr_t p_lli = virt_to_phys(lli);
-
dev_dbg(chan2dev(&vchan->vc.chan),
- "\n\tdesc: p - %pa v - 0x%p\n"
+ "\n\tdesc:\tp - %pad v - 0x%p\n"
"\t\tc - 0x%08x s - 0x%08x d - 0x%08x\n"
"\t\tl - 0x%08x p - 0x%08x n - 0x%08x\n",
- &p_lli, lli,
- lli->cfg, lli->src, lli->dst,
- lli->len, lli->para, lli->p_lli_next);
+ &p_lli, v_lli,
+ v_lli->cfg, v_lli->src, v_lli->dst,
+ v_lli->len, v_lli->para, v_lli->p_lli_next);
}
static void sun6i_dma_free_desc(struct virt_dma_desc *vd)
@@ -445,7 +453,7 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
pchan->desc = to_sun6i_desc(&desc->tx);
pchan->done = NULL;
- sun6i_dma_dump_lli(vchan, pchan->desc->v_lli);
+ sun6i_dma_dump_lli(vchan, pchan->desc->v_lli, pchan->desc->p_lli);
irq_reg = pchan->idx / DMA_IRQ_CHAN_NR;
irq_offset = pchan->idx % DMA_IRQ_CHAN_NR;
@@ -546,7 +554,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
continue;
dev_dbg(sdev->slave.dev, "DMA irq status %s: 0x%x\n",
- i ? "high" : "low", status);
+ str_high_low(i), status);
writel(status, sdev->base + DMA_IRQ_STAT(i));
@@ -626,6 +634,18 @@ static int set_config(struct sun6i_dma_dev *sdev,
return 0;
}
+static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
+ struct sun6i_dma_lli *v_lli,
+ dma_addr_t src, dma_addr_t dst)
+{
+ v_lli->src = lower_32_bits(src);
+ v_lli->dst = lower_32_bits(dst);
+
+ if (sdev->cfg->has_high_addr)
+ v_lli->para |= SRC_HIGH_ADDR(upper_32_bits(src)) |
+ DST_HIGH_ADDR(upper_32_bits(dst));
+}
+
static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
size_t len, unsigned long flags)
@@ -648,16 +668,15 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
if (!txd)
return NULL;
- v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli);
+ v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32 | GFP_NOWAIT, &p_lli);
if (!v_lli) {
dev_err(sdev->slave.dev, "Failed to alloc lli memory\n");
goto err_txd_free;
}
- v_lli->src = src;
- v_lli->dst = dest;
v_lli->len = len;
v_lli->para = NORMAL_WAIT;
+ sun6i_dma_set_addr(sdev, v_lli, src, dest);
burst = convert_burst(8);
width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES);
@@ -670,7 +689,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
sun6i_dma_lli_add(NULL, v_lli, p_lli, txd);
- sun6i_dma_dump_lli(vchan, v_lli);
+ sun6i_dma_dump_lli(vchan, v_lli, p_lli);
return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
@@ -708,7 +727,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
return NULL;
for_each_sg(sgl, sg, sg_len, i) {
- v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli);
+ v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32 | GFP_NOWAIT, &p_lli);
if (!v_lli)
goto err_lli_free;
@@ -716,8 +735,9 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
v_lli->para = NORMAL_WAIT;
if (dir == DMA_MEM_TO_DEV) {
- v_lli->src = sg_dma_address(sg);
- v_lli->dst = sconfig->dst_addr;
+ sun6i_dma_set_addr(sdev, v_lli,
+ sg_dma_address(sg),
+ sconfig->dst_addr);
v_lli->cfg = lli_cfg;
sdev->cfg->set_drq(&v_lli->cfg, DRQ_SDRAM, vchan->port);
sdev->cfg->set_mode(&v_lli->cfg, LINEAR_MODE, IO_MODE);
@@ -729,8 +749,9 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
sg_dma_len(sg), flags);
} else {
- v_lli->src = sconfig->src_addr;
- v_lli->dst = sg_dma_address(sg);
+ sun6i_dma_set_addr(sdev, v_lli,
+ sconfig->src_addr,
+ sg_dma_address(sg));
v_lli->cfg = lli_cfg;
sdev->cfg->set_drq(&v_lli->cfg, vchan->port, DRQ_SDRAM);
sdev->cfg->set_mode(&v_lli->cfg, IO_MODE, LINEAR_MODE);
@@ -746,14 +767,16 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
}
dev_dbg(chan2dev(chan), "First: %pad\n", &txd->p_lli);
- for (prev = txd->v_lli; prev; prev = prev->v_lli_next)
- sun6i_dma_dump_lli(vchan, prev);
+ for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
+ p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
+ sun6i_dma_dump_lli(vchan, v_lli, p_lli);
return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
err_lli_free:
- for (prev = txd->v_lli; prev; prev = prev->v_lli_next)
- dma_pool_free(sdev->pool, prev, virt_to_phys(prev));
+ for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
+ p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
+ dma_pool_free(sdev->pool, v_lli, p_lli);
kfree(txd);
return NULL;
}
@@ -787,7 +810,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
return NULL;
for (i = 0; i < periods; i++) {
- v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli);
+ v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32 | GFP_NOWAIT, &p_lli);
if (!v_lli) {
dev_err(sdev->slave.dev, "Failed to alloc lli memory\n");
goto err_lli_free;
@@ -797,14 +820,16 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
v_lli->para = NORMAL_WAIT;
if (dir == DMA_MEM_TO_DEV) {
- v_lli->src = buf_addr + period_len * i;
- v_lli->dst = sconfig->dst_addr;
+ sun6i_dma_set_addr(sdev, v_lli,
+ buf_addr + period_len * i,
+ sconfig->dst_addr);
v_lli->cfg = lli_cfg;
sdev->cfg->set_drq(&v_lli->cfg, DRQ_SDRAM, vchan->port);
sdev->cfg->set_mode(&v_lli->cfg, LINEAR_MODE, IO_MODE);
} else {
- v_lli->src = sconfig->src_addr;
- v_lli->dst = buf_addr + period_len * i;
+ sun6i_dma_set_addr(sdev, v_lli,
+ sconfig->src_addr,
+ buf_addr + period_len * i);
v_lli->cfg = lli_cfg;
sdev->cfg->set_drq(&v_lli->cfg, vchan->port, DRQ_SDRAM);
sdev->cfg->set_mode(&v_lli->cfg, IO_MODE, LINEAR_MODE);
@@ -820,8 +845,9 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
err_lli_free:
- for (prev = txd->v_lli; prev; prev = prev->v_lli_next)
- dma_pool_free(sdev->pool, prev, virt_to_phys(prev));
+ for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
+ p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
+ dma_pool_free(sdev->pool, v_lli, p_lli);
kfree(txd);
return NULL;
}
@@ -1174,8 +1200,6 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
};
/*
- * TODO: Add support for more than 4g physical addressing.
- *
* The A100 binding uses the number of dma channels from the
* device tree node.
*/
@@ -1194,6 +1218,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ .has_high_addr = true,
.has_mbus_clk = true,
};
@@ -1248,6 +1273,7 @@ static const struct of_device_id sun6i_dma_match[] = {
{ .compatible = "allwinner,sun8i-a83t-dma", .data = &sun8i_a83t_dma_cfg },
{ .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg },
{ .compatible = "allwinner,sun8i-v3s-dma", .data = &sun8i_v3s_dma_cfg },
+ { .compatible = "allwinner,sun20i-d1-dma", .data = &sun50i_a100_dma_cfg },
{ .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg },
{ .compatible = "allwinner,sun50i-a100-dma", .data = &sun50i_a100_dma_cfg },
{ .compatible = "allwinner,sun50i-h6-dma", .data = &sun50i_h6_dma_cfg },
@@ -1259,7 +1285,6 @@ static int sun6i_dma_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sun6i_dma_dev *sdc;
- struct resource *res;
int ret, i;
sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL);
@@ -1270,8 +1295,7 @@ static int sun6i_dma_probe(struct platform_device *pdev)
if (!sdc->cfg)
return -ENODEV;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- sdc->base = devm_ioremap_resource(&pdev->dev, res);
+ sdc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(sdc->base))
return PTR_ERR(sdc->base);
@@ -1310,6 +1334,8 @@ static int sun6i_dma_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&sdc->pending);
spin_lock_init(&sdc->lock);
+ dma_set_max_seg_size(&pdev->dev, SZ_32M - 1);
+
dma_cap_set(DMA_PRIVATE, sdc->slave.cap_mask);
dma_cap_set(DMA_MEMCPY, sdc->slave.cap_mask);
dma_cap_set(DMA_SLAVE, sdc->slave.cap_mask);
@@ -1445,7 +1471,7 @@ err_chan_free:
return ret;
}
-static int sun6i_dma_remove(struct platform_device *pdev)
+static void sun6i_dma_remove(struct platform_device *pdev)
{
struct sun6i_dma_dev *sdc = platform_get_drvdata(pdev);
@@ -1459,8 +1485,6 @@ static int sun6i_dma_remove(struct platform_device *pdev)
reset_control_assert(sdc->rstc);
sun6i_dma_free(sdc);
-
- return 0;
}
static struct platform_driver sun6i_dma_driver = {