dmaengine: dw-edma: Improve the linked list and data blocks definition
authorGustavo Pimentel <Gustavo.Pimentel@synopsys.com>
Thu, 18 Feb 2021 19:04:03 +0000 (20:04 +0100)
committerVinod Koul <vkoul@kernel.org>
Tue, 16 Mar 2021 17:28:53 +0000 (22:58 +0530)
In the previous implementation, the driver assumed that there existed
only two memory spaces that would equally distribute the amount of
read/write channels.

This might not be the case on some other implementations, therefore this
patch change this requirement so that each write/read channel has
its own linked list and data space well defined, which allows
different sizes and locations.

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Link: https://lore.kernel.org/r/2e316cb983f8a1e09ce929029f87619dc92a52de.1613674948.git.gustavo.pimentel@synopsys.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/dw-edma/dw-edma-core.c
drivers/dma/dw-edma/dw-edma-core.h
drivers/dma/dw-edma/dw-edma-pcie.c

index cc39107..552b5f9 100644 (file)
@@ -81,8 +81,13 @@ static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
         *  - Even chunks originate CB equal to 1
         */
        chunk->cb = !(desc->chunks_alloc % 2);
-       chunk->ll_region.paddr = dw->ll_region.paddr + chan->ll_off;
-       chunk->ll_region.vaddr = dw->ll_region.vaddr + chan->ll_off;
+       if (chan->dir == EDMA_DIR_WRITE) {
+               chunk->ll_region.paddr = dw->ll_region_wr[chan->id].paddr;
+               chunk->ll_region.vaddr = dw->ll_region_wr[chan->id].vaddr;
+       } else {
+               chunk->ll_region.paddr = dw->ll_region_rd[chan->id].paddr;
+               chunk->ll_region.vaddr = dw->ll_region_rd[chan->id].vaddr;
+       }
 
        if (desc->chunk) {
                /* Create and add new element into the linked list */
@@ -691,24 +696,13 @@ static int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write,
        struct device *dev = chip->dev;
        struct dw_edma *dw = chip->dw;
        struct dw_edma_chan *chan;
-       size_t ll_chunk, dt_chunk;
        struct dw_edma_irq *irq;
        struct dma_device *dma;
-       u32 i, j, cnt, ch_cnt;
        u32 alloc, off_alloc;
+       u32 i, j, cnt;
        int err = 0;
        u32 pos;
 
-       ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
-       ll_chunk = dw->ll_region.sz;
-       dt_chunk = dw->dt_region.sz;
-
-       /* Calculate linked list chunk for each channel */
-       ll_chunk /= roundup_pow_of_two(ch_cnt);
-
-       /* Calculate linked list chunk for each channel */
-       dt_chunk /= roundup_pow_of_two(ch_cnt);
-
        if (write) {
                i = 0;
                cnt = dw->wr_ch_cnt;
@@ -740,14 +734,14 @@ static int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write,
                chan->request = EDMA_REQ_NONE;
                chan->status = EDMA_ST_IDLE;
 
-               chan->ll_off = (ll_chunk * i);
-               chan->ll_max = (ll_chunk / EDMA_LL_SZ) - 1;
-
-               chan->dt_off = (dt_chunk * i);
+               if (write)
+                       chan->ll_max = (dw->ll_region_wr[j].sz / EDMA_LL_SZ);
+               else
+                       chan->ll_max = (dw->ll_region_rd[j].sz / EDMA_LL_SZ);
+               chan->ll_max -= 1;
 
-               dev_vdbg(dev, "L. List:\tChannel %s[%u] off=0x%.8lx, max_cnt=%u\n",
-                        write ? "write" : "read", j,
-                        chan->ll_off, chan->ll_max);
+               dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
+                        write ? "write" : "read", j, chan->ll_max);
 
                if (dw->nr_irqs == 1)
                        pos = 0;
@@ -772,12 +766,15 @@ static int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write,
                chan->vc.desc_free = vchan_free_desc;
                vchan_init(&chan->vc, dma);
 
-               dt_region->paddr = dw->dt_region.paddr + chan->dt_off;
-               dt_region->vaddr = dw->dt_region.vaddr + chan->dt_off;
-               dt_region->sz = dt_chunk;
-
-               dev_vdbg(dev, "Data:\tChannel %s[%u] off=0x%.8lx\n",
-                        write ? "write" : "read", j, chan->dt_off);
+               if (write) {
+                       dt_region->paddr = dw->dt_region_wr[j].paddr;
+                       dt_region->vaddr = dw->dt_region_wr[j].vaddr;
+                       dt_region->sz = dw->dt_region_wr[j].sz;
+               } else {
+                       dt_region->paddr = dw->dt_region_rd[j].paddr;
+                       dt_region->vaddr = dw->dt_region_rd[j].vaddr;
+                       dt_region->sz = dw->dt_region_rd[j].sz;
+               }
 
                dw_edma_v0_core_device_config(chan);
        }
index 650b1c7..cba5436 100644 (file)
@@ -91,11 +91,8 @@ struct dw_edma_chan {
        int                             id;
        enum dw_edma_dir                dir;
 
-       off_t                           ll_off;
        u32                             ll_max;
 
-       off_t                           dt_off;
-
        struct msi_msg                  msi;
 
        enum dw_edma_request            request;
@@ -126,8 +123,10 @@ struct dw_edma {
        u16                             rd_ch_cnt;
 
        struct dw_edma_region           rg_region;      /* Registers */
-       struct dw_edma_region           ll_region;      /* Linked list */
-       struct dw_edma_region           dt_region;      /* Data */
+       struct dw_edma_region           ll_region_wr[EDMA_MAX_WR_CH];
+       struct dw_edma_region           ll_region_rd[EDMA_MAX_RD_CH];
+       struct dw_edma_region           dt_region_wr[EDMA_MAX_WR_CH];
+       struct dw_edma_region           dt_region_rd[EDMA_MAX_RD_CH];
 
        struct dw_edma_irq              *irq;
        int                             nr_irqs;
index 63c62e1..1055fdb 100644 (file)
 #define DW_PCIE_VSEC_DMA_WR_CH                 GENMASK(9, 0)
 #define DW_PCIE_VSEC_DMA_RD_CH                 GENMASK(25, 16)
 
+#define DW_BLOCK(a, b, c) \
+       { \
+               .bar = a, \
+               .off = b, \
+               .sz = c, \
+       },
+
+struct dw_edma_block {
+       enum pci_barno                  bar;
+       off_t                           off;
+       size_t                          sz;
+};
+
 struct dw_edma_pcie_data {
        /* eDMA registers location */
-       enum pci_barno                  rg_bar;
-       off_t                           rg_off;
-       size_t                          rg_sz;
+       struct dw_edma_block            rg;
        /* eDMA memory linked list location */
-       enum pci_barno                  ll_bar;
-       off_t                           ll_off;
-       size_t                          ll_sz;
+       struct dw_edma_block            ll_wr[EDMA_MAX_WR_CH];
+       struct dw_edma_block            ll_rd[EDMA_MAX_RD_CH];
        /* eDMA memory data location */
-       enum pci_barno                  dt_bar;
-       off_t                           dt_off;
-       size_t                          dt_sz;
+       struct dw_edma_block            dt_wr[EDMA_MAX_WR_CH];
+       struct dw_edma_block            dt_rd[EDMA_MAX_RD_CH];
        /* Other */
        enum dw_edma_map_format         mf;
        u8                              irqs;
@@ -45,22 +54,40 @@ struct dw_edma_pcie_data {
 
 static const struct dw_edma_pcie_data snps_edda_data = {
        /* eDMA registers location */
-       .rg_bar                         = BAR_0,
-       .rg_off                         = 0x00001000,   /*  4 Kbytes */
-       .rg_sz                          = 0x00002000,   /*  8 Kbytes */
+       .rg.bar                         = BAR_0,
+       .rg.off                         = 0x00001000,   /*  4 Kbytes */
+       .rg.sz                          = 0x00002000,   /*  8 Kbytes */
        /* eDMA memory linked list location */
-       .ll_bar                         = BAR_2,
-       .ll_off                         = 0x00000000,   /*  0 Kbytes */
-       .ll_sz                          = 0x00800000,   /*  8 Mbytes */
+       .ll_wr = {
+               /* Channel 0 - BAR 2, offset 0 Mbytes, size 2 Mbytes */
+               DW_BLOCK(BAR_2, 0x00000000, 0x00200000)
+               /* Channel 1 - BAR 2, offset 2 Mbytes, size 2 Mbytes */
+               DW_BLOCK(BAR_2, 0x00200000, 0x00200000)
+       },
+       .ll_rd = {
+               /* Channel 0 - BAR 2, offset 4 Mbytes, size 2 Mbytes */
+               DW_BLOCK(BAR_2, 0x00400000, 0x00200000)
+               /* Channel 1 - BAR 2, offset 6 Mbytes, size 2 Mbytes */
+               DW_BLOCK(BAR_2, 0x00600000, 0x00200000)
+       },
        /* eDMA memory data location */
-       .dt_bar                         = BAR_2,
-       .dt_off                         = 0x00800000,   /*  8 Mbytes */
-       .dt_sz                          = 0x03800000,   /* 56 Mbytes */
+       .dt_wr = {
+               /* Channel 0 - BAR 2, offset 8 Mbytes, size 14 Mbytes */
+               DW_BLOCK(BAR_2, 0x00800000, 0x00e00000)
+               /* Channel 1 - BAR 2, offset 22 Mbytes, size 14 Mbytes */
+               DW_BLOCK(BAR_2, 0x01600000, 0x00e00000)
+       },
+       .dt_rd = {
+               /* Channel 0 - BAR 2, offset 36 Mbytes, size 14 Mbytes */
+               DW_BLOCK(BAR_2, 0x02400000, 0x00e00000)
+               /* Channel 1 - BAR 2, offset 50 Mbytes, size 14 Mbytes */
+               DW_BLOCK(BAR_2, 0x03200000, 0x00e00000)
+       },
        /* Other */
        .mf                             = EDMA_MF_EDMA_UNROLL,
        .irqs                           = 1,
-       .wr_ch_cnt                      = 0,
-       .rd_ch_cnt                      = 0,
+       .wr_ch_cnt                      = 2,
+       .rd_ch_cnt                      = 2,
 };
 
 static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int nr)
@@ -98,18 +125,20 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
                return;
 
        pdata->mf = map;
-       pdata->rg_bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
+       pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
 
        pci_read_config_dword(pdev, vsec + 0xc, &val);
-       pdata->wr_ch_cnt = FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val);
-       pdata->rd_ch_cnt = FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val);
+       pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
+                                FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val));
+       pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
+                                FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));
 
        pci_read_config_dword(pdev, vsec + 0x14, &val);
        off = val;
        pci_read_config_dword(pdev, vsec + 0x10, &val);
        off <<= 32;
        off |= val;
-       pdata->rg_off = off;
+       pdata->rg.off = off;
 }
 
 static int dw_edma_pcie_probe(struct pci_dev *pdev,
@@ -121,6 +150,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
        struct dw_edma_chip *chip;
        struct dw_edma *dw;
        int err, nr_irqs;
+       int i, mask;
 
        /* Enable PCI device */
        err = pcim_enable_device(pdev);
@@ -138,10 +168,16 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
        dw_edma_pcie_get_vsec_dma_data(pdev, &vsec_data);
 
        /* Mapping PCI BAR regions */
-       err = pcim_iomap_regions(pdev, BIT(vsec_data.rg_bar) |
-                                      BIT(vsec_data.ll_bar) |
-                                      BIT(vsec_data.dt_bar),
-                                pci_name(pdev));
+       mask = BIT(vsec_data.rg.bar);
+       for (i = 0; i < vsec_data.wr_ch_cnt; i++) {
+               mask |= BIT(vsec_data.ll_wr[i].bar);
+               mask |= BIT(vsec_data.dt_wr[i].bar);
+       }
+       for (i = 0; i < vsec_data.rd_ch_cnt; i++) {
+               mask |= BIT(vsec_data.ll_rd[i].bar);
+               mask |= BIT(vsec_data.dt_rd[i].bar);
+       }
+       err = pcim_iomap_regions(pdev, mask, pci_name(pdev));
        if (err) {
                pci_err(pdev, "eDMA BAR I/O remapping failed\n");
                return err;
@@ -197,30 +233,56 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
        chip->id = pdev->devfn;
        chip->irq = pdev->irq;
 
-       dw->rg_region.vaddr = pcim_iomap_table(pdev)[vsec_data.rg_bar];
-       dw->rg_region.vaddr += vsec_data.rg_off;
-       dw->rg_region.paddr = pdev->resource[vsec_data.rg_bar].start;
-       dw->rg_region.paddr += vsec_data.rg_off;
-       dw->rg_region.sz = vsec_data.rg_sz;
-
-       dw->ll_region.vaddr = pcim_iomap_table(pdev)[vsec_data.ll_bar];
-       dw->ll_region.vaddr += vsec_data.ll_off;
-       dw->ll_region.paddr = pdev->resource[vsec_data.ll_bar].start;
-       dw->ll_region.paddr += vsec_data.ll_off;
-       dw->ll_region.sz = vsec_data.ll_sz;
-
-       dw->dt_region.vaddr = pcim_iomap_table(pdev)[vsec_data.dt_bar];
-       dw->dt_region.vaddr += vsec_data.dt_off;
-       dw->dt_region.paddr = pdev->resource[vsec_data.dt_bar].start;
-       dw->dt_region.paddr += vsec_data.dt_off;
-       dw->dt_region.sz = vsec_data.dt_sz;
-
        dw->mf = vsec_data.mf;
        dw->nr_irqs = nr_irqs;
        dw->ops = &dw_edma_pcie_core_ops;
        dw->wr_ch_cnt = vsec_data.wr_ch_cnt;
        dw->rd_ch_cnt = vsec_data.rd_ch_cnt;
 
+       dw->rg_region.vaddr = pcim_iomap_table(pdev)[vsec_data.rg.bar];
+       dw->rg_region.vaddr += vsec_data.rg.off;
+       dw->rg_region.paddr = pdev->resource[vsec_data.rg.bar].start;
+       dw->rg_region.paddr += vsec_data.rg.off;
+       dw->rg_region.sz = vsec_data.rg.sz;
+
+       for (i = 0; i < dw->wr_ch_cnt; i++) {
+               struct dw_edma_region *ll_region = &dw->ll_region_wr[i];
+               struct dw_edma_region *dt_region = &dw->dt_region_wr[i];
+               struct dw_edma_block *ll_block = &vsec_data.ll_wr[i];
+               struct dw_edma_block *dt_block = &vsec_data.dt_wr[i];
+
+               ll_region->vaddr = pcim_iomap_table(pdev)[ll_block->bar];
+               ll_region->vaddr += ll_block->off;
+               ll_region->paddr = pdev->resource[ll_block->bar].start;
+               ll_region->paddr += ll_block->off;
+               ll_region->sz = ll_block->sz;
+
+               dt_region->vaddr = pcim_iomap_table(pdev)[dt_block->bar];
+               dt_region->vaddr += dt_block->off;
+               dt_region->paddr = pdev->resource[dt_block->bar].start;
+               dt_region->paddr += dt_block->off;
+               dt_region->sz = dt_block->sz;
+       }
+
+       for (i = 0; i < dw->rd_ch_cnt; i++) {
+               struct dw_edma_region *ll_region = &dw->ll_region_rd[i];
+               struct dw_edma_region *dt_region = &dw->dt_region_rd[i];
+               struct dw_edma_block *ll_block = &vsec_data.ll_rd[i];
+               struct dw_edma_block *dt_block = &vsec_data.dt_rd[i];
+
+               ll_region->vaddr = pcim_iomap_table(pdev)[ll_block->bar];
+               ll_region->vaddr += ll_block->off;
+               ll_region->paddr = pdev->resource[ll_block->bar].start;
+               ll_region->paddr += ll_block->off;
+               ll_region->sz = ll_block->sz;
+
+               dt_region->vaddr = pcim_iomap_table(pdev)[dt_block->bar];
+               dt_region->vaddr += dt_block->off;
+               dt_region->paddr = pdev->resource[dt_block->bar].start;
+               dt_region->paddr += dt_block->off;
+               dt_region->sz = dt_block->sz;
+       }
+
        /* Debug info */
        if (dw->mf == EDMA_MF_EDMA_LEGACY)
                pci_dbg(pdev, "Version:\teDMA Port Logic (0x%x)\n", dw->mf);
@@ -232,16 +294,33 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
                pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", dw->mf);
 
        pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
-               vsec_data.rg_bar, vsec_data.rg_off, vsec_data.rg_sz,
+               vsec_data.rg.bar, vsec_data.rg.off, vsec_data.rg.sz,
                dw->rg_region.vaddr, &dw->rg_region.paddr);
 
-       pci_dbg(pdev, "L. List:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
-               vsec_data.ll_bar, vsec_data.ll_off, vsec_data.ll_sz,
-               dw->ll_region.vaddr, &dw->ll_region.paddr);
 
-       pci_dbg(pdev, "Data:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
-               vsec_data.dt_bar, vsec_data.dt_off, vsec_data.dt_sz,
-               dw->dt_region.vaddr, &dw->dt_region.paddr);
+       for (i = 0; i < dw->wr_ch_cnt; i++) {
+               pci_dbg(pdev, "L. List:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
+                       i, vsec_data.ll_wr[i].bar,
+                       vsec_data.ll_wr[i].off, dw->ll_region_wr[i].sz,
+                       dw->ll_region_wr[i].vaddr, &dw->ll_region_wr[i].paddr);
+
+               pci_dbg(pdev, "Data:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
+                       i, vsec_data.dt_wr[i].bar,
+                       vsec_data.dt_wr[i].off, dw->dt_region_wr[i].sz,
+                       dw->dt_region_wr[i].vaddr, &dw->dt_region_wr[i].paddr);
+       }
+
+       for (i = 0; i < dw->rd_ch_cnt; i++) {
+               pci_dbg(pdev, "L. List:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
+                       i, vsec_data.ll_rd[i].bar,
+                       vsec_data.ll_rd[i].off, dw->ll_region_rd[i].sz,
+                       dw->ll_region_rd[i].vaddr, &dw->ll_region_rd[i].paddr);
+
+               pci_dbg(pdev, "Data:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
+                       i, vsec_data.dt_rd[i].bar,
+                       vsec_data.dt_rd[i].off, dw->dt_region_rd[i].sz,
+                       dw->dt_region_rd[i].vaddr, &dw->dt_region_rd[i].paddr);
+       }
 
        pci_dbg(pdev, "Nr. IRQs:\t%u\n", dw->nr_irqs);