2019-06-04 13:29:26 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
|
|
|
|
* Synopsys DesignWare eDMA PCIe driver
|
|
|
|
*
|
|
|
|
* Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/dma/edma.h>
|
|
|
|
#include <linux/pci-epf.h>
|
|
|
|
#include <linux/msi.h>
|
2021-02-18 19:03:59 +00:00
|
|
|
#include <linux/bitfield.h>
|
2019-06-04 13:29:26 +00:00
|
|
|
|
|
|
|
#include "dw-edma-core.h"
|
|
|
|
|
2021-02-18 19:03:59 +00:00
|
|
|
#define DW_PCIE_VSEC_DMA_ID 0x6
|
|
|
|
#define DW_PCIE_VSEC_DMA_BAR GENMASK(10, 8)
|
|
|
|
#define DW_PCIE_VSEC_DMA_MAP GENMASK(2, 0)
|
|
|
|
#define DW_PCIE_VSEC_DMA_WR_CH GENMASK(9, 0)
|
2021-02-18 19:04:02 +00:00
|
|
|
#define DW_PCIE_VSEC_DMA_RD_CH GENMASK(25, 16)
|
2021-02-18 19:03:59 +00:00
|
|
|
|
2021-02-18 19:04:03 +00:00
|
|
|
#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;
|
|
|
|
};
|
|
|
|
|
2019-06-04 13:29:26 +00:00
|
|
|
struct dw_edma_pcie_data {
|
|
|
|
/* eDMA registers location */
|
2021-02-18 19:04:03 +00:00
|
|
|
struct dw_edma_block rg;
|
2019-06-04 13:29:26 +00:00
|
|
|
/* eDMA memory linked list location */
|
2021-02-18 19:04:03 +00:00
|
|
|
struct dw_edma_block ll_wr[EDMA_MAX_WR_CH];
|
|
|
|
struct dw_edma_block ll_rd[EDMA_MAX_RD_CH];
|
2019-06-04 13:29:26 +00:00
|
|
|
/* eDMA memory data location */
|
2021-02-18 19:04:03 +00:00
|
|
|
struct dw_edma_block dt_wr[EDMA_MAX_WR_CH];
|
|
|
|
struct dw_edma_block dt_rd[EDMA_MAX_RD_CH];
|
2019-06-04 13:29:26 +00:00
|
|
|
/* Other */
|
2021-02-18 19:03:57 +00:00
|
|
|
enum dw_edma_map_format mf;
|
2019-06-04 13:29:26 +00:00
|
|
|
u8 irqs;
|
2021-02-18 19:03:59 +00:00
|
|
|
u16 wr_ch_cnt;
|
2021-02-18 19:04:02 +00:00
|
|
|
u16 rd_ch_cnt;
|
2019-06-04 13:29:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dw_edma_pcie_data snps_edda_data = {
|
|
|
|
/* eDMA registers location */
|
2021-02-18 19:04:03 +00:00
|
|
|
.rg.bar = BAR_0,
|
|
|
|
.rg.off = 0x00001000, /* 4 Kbytes */
|
|
|
|
.rg.sz = 0x00002000, /* 8 Kbytes */
|
2019-06-04 13:29:26 +00:00
|
|
|
/* eDMA memory linked list location */
|
2021-02-18 19:04:03 +00:00
|
|
|
.ll_wr = {
|
2021-02-18 19:04:04 +00:00
|
|
|
/* Channel 0 - BAR 2, offset 0 Mbytes, size 2 Kbytes */
|
|
|
|
DW_BLOCK(BAR_2, 0x00000000, 0x00000800)
|
|
|
|
/* Channel 1 - BAR 2, offset 2 Mbytes, size 2 Kbytes */
|
|
|
|
DW_BLOCK(BAR_2, 0x00200000, 0x00000800)
|
2021-02-18 19:04:03 +00:00
|
|
|
},
|
|
|
|
.ll_rd = {
|
2021-02-18 19:04:04 +00:00
|
|
|
/* Channel 0 - BAR 2, offset 4 Mbytes, size 2 Kbytes */
|
|
|
|
DW_BLOCK(BAR_2, 0x00400000, 0x00000800)
|
|
|
|
/* Channel 1 - BAR 2, offset 6 Mbytes, size 2 Kbytes */
|
|
|
|
DW_BLOCK(BAR_2, 0x00600000, 0x00000800)
|
2021-02-18 19:04:03 +00:00
|
|
|
},
|
2019-06-04 13:29:26 +00:00
|
|
|
/* eDMA memory data location */
|
2021-02-18 19:04:03 +00:00
|
|
|
.dt_wr = {
|
2021-02-18 19:04:04 +00:00
|
|
|
/* Channel 0 - BAR 2, offset 8 Mbytes, size 2 Kbytes */
|
|
|
|
DW_BLOCK(BAR_2, 0x00800000, 0x00000800)
|
|
|
|
/* Channel 1 - BAR 2, offset 9 Mbytes, size 2 Kbytes */
|
|
|
|
DW_BLOCK(BAR_2, 0x00900000, 0x00000800)
|
2021-02-18 19:04:03 +00:00
|
|
|
},
|
|
|
|
.dt_rd = {
|
2021-02-18 19:04:04 +00:00
|
|
|
/* Channel 0 - BAR 2, offset 10 Mbytes, size 2 Kbytes */
|
|
|
|
DW_BLOCK(BAR_2, 0x00a00000, 0x00000800)
|
|
|
|
/* Channel 1 - BAR 2, offset 11 Mbytes, size 2 Kbytes */
|
|
|
|
DW_BLOCK(BAR_2, 0x00b00000, 0x00000800)
|
2021-02-18 19:04:03 +00:00
|
|
|
},
|
2019-06-04 13:29:26 +00:00
|
|
|
/* Other */
|
2021-02-18 19:03:57 +00:00
|
|
|
.mf = EDMA_MF_EDMA_UNROLL,
|
2019-06-04 13:29:26 +00:00
|
|
|
.irqs = 1,
|
2021-02-18 19:04:03 +00:00
|
|
|
.wr_ch_cnt = 2,
|
|
|
|
.rd_ch_cnt = 2,
|
2019-06-04 13:29:26 +00:00
|
|
|
};
|
|
|
|
|
2020-04-15 17:27:09 +00:00
|
|
|
static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int nr)
|
|
|
|
{
|
|
|
|
return pci_irq_vector(to_pci_dev(dev), nr);
|
|
|
|
}
|
|
|
|
|
2023-01-13 17:13:50 +00:00
|
|
|
static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr)
|
|
|
|
{
|
|
|
|
struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
|
struct pci_bus_region region;
|
|
|
|
struct resource res = {
|
|
|
|
.flags = IORESOURCE_MEM,
|
|
|
|
.start = cpu_addr,
|
|
|
|
.end = cpu_addr,
|
|
|
|
};
|
|
|
|
|
|
|
|
pcibios_resource_to_bus(pdev->bus, ®ion, &res);
|
|
|
|
return region.start;
|
|
|
|
}
|
|
|
|
|
2023-05-20 05:08:49 +00:00
|
|
|
static const struct dw_edma_plat_ops dw_edma_pcie_plat_ops = {
|
2020-04-15 17:27:09 +00:00
|
|
|
.irq_vector = dw_edma_pcie_irq_vector,
|
2023-01-13 17:13:50 +00:00
|
|
|
.pci_address = dw_edma_pcie_address,
|
2020-04-15 17:27:09 +00:00
|
|
|
};
|
|
|
|
|
2021-02-18 19:03:59 +00:00
|
|
|
static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
|
|
|
|
struct dw_edma_pcie_data *pdata)
|
|
|
|
{
|
|
|
|
u32 val, map;
|
|
|
|
u16 vsec;
|
|
|
|
u64 off;
|
|
|
|
|
|
|
|
vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
|
|
|
|
DW_PCIE_VSEC_DMA_ID);
|
|
|
|
if (!vsec)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
|
|
|
|
if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
|
|
|
|
PCI_VNDR_HEADER_LEN(val) != 0x18)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability DMA\n");
|
|
|
|
pci_read_config_dword(pdev, vsec + 0x8, &val);
|
|
|
|
map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
|
|
|
|
if (map != EDMA_MF_EDMA_LEGACY &&
|
|
|
|
map != EDMA_MF_EDMA_UNROLL &&
|
|
|
|
map != EDMA_MF_HDMA_COMPAT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pdata->mf = map;
|
2021-02-18 19:04:03 +00:00
|
|
|
pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
|
2021-02-18 19:03:59 +00:00
|
|
|
|
|
|
|
pci_read_config_dword(pdev, vsec + 0xc, &val);
|
2021-02-18 19:04:03 +00:00
|
|
|
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));
|
2021-02-18 19:03:59 +00:00
|
|
|
|
|
|
|
pci_read_config_dword(pdev, vsec + 0x14, &val);
|
|
|
|
off = val;
|
|
|
|
pci_read_config_dword(pdev, vsec + 0x10, &val);
|
|
|
|
off <<= 32;
|
|
|
|
off |= val;
|
2021-02-18 19:04:03 +00:00
|
|
|
pdata->rg.off = off;
|
2021-02-18 19:03:59 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 13:29:26 +00:00
|
|
|
static int dw_edma_pcie_probe(struct pci_dev *pdev,
|
|
|
|
const struct pci_device_id *pid)
|
|
|
|
{
|
2021-02-18 19:03:59 +00:00
|
|
|
struct dw_edma_pcie_data *pdata = (void *)pid->driver_data;
|
|
|
|
struct dw_edma_pcie_data vsec_data;
|
2019-06-04 13:29:26 +00:00
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
struct dw_edma_chip *chip;
|
2021-02-18 19:03:57 +00:00
|
|
|
int err, nr_irqs;
|
2021-02-18 19:04:03 +00:00
|
|
|
int i, mask;
|
2019-06-04 13:29:26 +00:00
|
|
|
|
|
|
|
/* Enable PCI device */
|
|
|
|
err = pcim_enable_device(pdev);
|
|
|
|
if (err) {
|
|
|
|
pci_err(pdev, "enabling device failed\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-02-18 19:03:59 +00:00
|
|
|
memcpy(&vsec_data, pdata, sizeof(struct dw_edma_pcie_data));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tries to find if exists a PCIe Vendor-Specific Extended Capability
|
|
|
|
* for the DMA, if one exists, then reconfigures it.
|
|
|
|
*/
|
|
|
|
dw_edma_pcie_get_vsec_dma_data(pdev, &vsec_data);
|
|
|
|
|
2019-06-04 13:29:26 +00:00
|
|
|
/* Mapping PCI BAR regions */
|
2021-02-18 19:04:03 +00:00
|
|
|
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));
|
2019-06-04 13:29:26 +00:00
|
|
|
if (err) {
|
|
|
|
pci_err(pdev, "eDMA BAR I/O remapping failed\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
|
|
|
/* DMA configuration */
|
2021-10-08 03:28:27 +00:00
|
|
|
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
2021-11-09 21:09:56 +00:00
|
|
|
if (err) {
|
2021-10-08 03:28:27 +00:00
|
|
|
pci_err(pdev, "DMA mask 64 set failed\n");
|
|
|
|
return err;
|
2019-06-04 13:29:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Data structure allocation */
|
|
|
|
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
|
|
|
if (!chip)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* IRQs allocation */
|
2021-02-18 19:03:59 +00:00
|
|
|
nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data.irqs,
|
2019-06-04 13:29:26 +00:00
|
|
|
PCI_IRQ_MSI | PCI_IRQ_MSIX);
|
|
|
|
if (nr_irqs < 1) {
|
|
|
|
pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n",
|
|
|
|
nr_irqs);
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Data structure initialization */
|
|
|
|
chip->dev = dev;
|
|
|
|
|
2022-05-24 15:21:53 +00:00
|
|
|
chip->mf = vsec_data.mf;
|
|
|
|
chip->nr_irqs = nr_irqs;
|
2023-05-20 05:08:49 +00:00
|
|
|
chip->ops = &dw_edma_pcie_plat_ops;
|
2019-06-04 13:29:26 +00:00
|
|
|
|
2022-05-24 15:21:55 +00:00
|
|
|
chip->ll_wr_cnt = vsec_data.wr_ch_cnt;
|
|
|
|
chip->ll_rd_cnt = vsec_data.rd_ch_cnt;
|
2021-02-18 19:04:09 +00:00
|
|
|
|
2022-05-24 15:21:54 +00:00
|
|
|
chip->reg_base = pcim_iomap_table(pdev)[vsec_data.rg.bar];
|
|
|
|
if (!chip->reg_base)
|
2022-05-24 15:21:53 +00:00
|
|
|
return -ENOMEM;
|
2021-02-18 19:04:03 +00:00
|
|
|
|
2022-05-24 15:21:55 +00:00
|
|
|
for (i = 0; i < chip->ll_wr_cnt; i++) {
|
2022-05-24 15:21:53 +00:00
|
|
|
struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
|
|
|
|
struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
|
2021-02-18 19:04:03 +00:00
|
|
|
struct dw_edma_block *ll_block = &vsec_data.ll_wr[i];
|
|
|
|
struct dw_edma_block *dt_block = &vsec_data.dt_wr[i];
|
|
|
|
|
2023-01-13 17:14:05 +00:00
|
|
|
ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar];
|
|
|
|
if (!ll_region->vaddr.io)
|
2021-02-18 19:04:09 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-01-13 17:14:05 +00:00
|
|
|
ll_region->vaddr.io += ll_block->off;
|
dmaengine: dw-edma: Convert ll/dt phys address to PCI bus/DMA address
The dw_edma_region.paddr field should be a memory base address visible by
the DW eDMA controller. If the DMA engine is embedded in the DW PCIe
Host/Endpoint controller, the address should belong to the Local CPU/
Application memory. If eDMA is remotely accessible across the PCI bus via
PCI memory IOs, the address should be part of the PCI bus memory space.
The latter case hasn't been well covered in the corresponding glue-driver.
Since pci_dev.resource[] contains resources defined in the CPU memory
space, they need to be converted to the PCI bus address space. Convert the
LL, DT and CSRs PCI memory ranges with pci_bus_address().
In addition, extend the dw_edma_region.paddr field size. The field normally
contains a memory range base address to be set in the DW eDMA Linked-List
pointer register or as a base address of the Linked-List data buffer. In
accordance with [1] the LL range is supposed to be created in the Local
CPU/Application memory, but depending on the DW eDMA utilization the memory
can be created as a part of the PCI bus address space (as in the case of
the DW PCIe Endpoint prototype kit).
In the former case dw_edma_region.paddr should be a dma_addr_t, while in
the latter one it should be a pci_bus_addr_t. Since the corresponding CSRs
are always 64 bits wide, convert dw_edma_region.paddr to be u64, and let
the client make sure it has a valid address visible by the DW eDMA
controller. For instance, the DW eDMA PCIe glue-driver initializes the
field with addresses from the PCI bus memory space.
[1] DesignWare Cores PCI Express Controller Databook - DWC PCIe Root Port,
v.5.40a, March 2019, p.1103
Link: https://lore.kernel.org/r/20230113171409.30470-4-Sergey.Semin@baikalelectronics.ru
Fixes: 41aaff2a2ac0 ("dmaengine: Add Synopsys eDMA IP PCIe glue-logic")
Tested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Acked-by: Vinod Koul <vkoul@kernel.org>
2023-01-13 17:13:45 +00:00
|
|
|
ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
|
2021-02-18 19:04:03 +00:00
|
|
|
ll_region->paddr += ll_block->off;
|
|
|
|
ll_region->sz = ll_block->sz;
|
|
|
|
|
2023-01-13 17:14:05 +00:00
|
|
|
dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar];
|
|
|
|
if (!dt_region->vaddr.io)
|
2021-02-18 19:04:09 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-01-13 17:14:05 +00:00
|
|
|
dt_region->vaddr.io += dt_block->off;
|
dmaengine: dw-edma: Convert ll/dt phys address to PCI bus/DMA address
The dw_edma_region.paddr field should be a memory base address visible by
the DW eDMA controller. If the DMA engine is embedded in the DW PCIe
Host/Endpoint controller, the address should belong to the Local CPU/
Application memory. If eDMA is remotely accessible across the PCI bus via
PCI memory IOs, the address should be part of the PCI bus memory space.
The latter case hasn't been well covered in the corresponding glue-driver.
Since pci_dev.resource[] contains resources defined in the CPU memory
space, they need to be converted to the PCI bus address space. Convert the
LL, DT and CSRs PCI memory ranges with pci_bus_address().
In addition, extend the dw_edma_region.paddr field size. The field normally
contains a memory range base address to be set in the DW eDMA Linked-List
pointer register or as a base address of the Linked-List data buffer. In
accordance with [1] the LL range is supposed to be created in the Local
CPU/Application memory, but depending on the DW eDMA utilization the memory
can be created as a part of the PCI bus address space (as in the case of
the DW PCIe Endpoint prototype kit).
In the former case dw_edma_region.paddr should be a dma_addr_t, while in
the latter one it should be a pci_bus_addr_t. Since the corresponding CSRs
are always 64 bits wide, convert dw_edma_region.paddr to be u64, and let
the client make sure it has a valid address visible by the DW eDMA
controller. For instance, the DW eDMA PCIe glue-driver initializes the
field with addresses from the PCI bus memory space.
[1] DesignWare Cores PCI Express Controller Databook - DWC PCIe Root Port,
v.5.40a, March 2019, p.1103
Link: https://lore.kernel.org/r/20230113171409.30470-4-Sergey.Semin@baikalelectronics.ru
Fixes: 41aaff2a2ac0 ("dmaengine: Add Synopsys eDMA IP PCIe glue-logic")
Tested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Acked-by: Vinod Koul <vkoul@kernel.org>
2023-01-13 17:13:45 +00:00
|
|
|
dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
|
2021-02-18 19:04:03 +00:00
|
|
|
dt_region->paddr += dt_block->off;
|
|
|
|
dt_region->sz = dt_block->sz;
|
|
|
|
}
|
|
|
|
|
2022-05-24 15:21:55 +00:00
|
|
|
for (i = 0; i < chip->ll_rd_cnt; i++) {
|
2022-05-24 15:21:53 +00:00
|
|
|
struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
|
|
|
|
struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
|
2021-02-18 19:04:03 +00:00
|
|
|
struct dw_edma_block *ll_block = &vsec_data.ll_rd[i];
|
|
|
|
struct dw_edma_block *dt_block = &vsec_data.dt_rd[i];
|
|
|
|
|
2023-01-13 17:14:05 +00:00
|
|
|
ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar];
|
|
|
|
if (!ll_region->vaddr.io)
|
2021-02-18 19:04:09 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-01-13 17:14:05 +00:00
|
|
|
ll_region->vaddr.io += ll_block->off;
|
dmaengine: dw-edma: Convert ll/dt phys address to PCI bus/DMA address
The dw_edma_region.paddr field should be a memory base address visible by
the DW eDMA controller. If the DMA engine is embedded in the DW PCIe
Host/Endpoint controller, the address should belong to the Local CPU/
Application memory. If eDMA is remotely accessible across the PCI bus via
PCI memory IOs, the address should be part of the PCI bus memory space.
The latter case hasn't been well covered in the corresponding glue-driver.
Since pci_dev.resource[] contains resources defined in the CPU memory
space, they need to be converted to the PCI bus address space. Convert the
LL, DT and CSRs PCI memory ranges with pci_bus_address().
In addition, extend the dw_edma_region.paddr field size. The field normally
contains a memory range base address to be set in the DW eDMA Linked-List
pointer register or as a base address of the Linked-List data buffer. In
accordance with [1] the LL range is supposed to be created in the Local
CPU/Application memory, but depending on the DW eDMA utilization the memory
can be created as a part of the PCI bus address space (as in the case of
the DW PCIe Endpoint prototype kit).
In the former case dw_edma_region.paddr should be a dma_addr_t, while in
the latter one it should be a pci_bus_addr_t. Since the corresponding CSRs
are always 64 bits wide, convert dw_edma_region.paddr to be u64, and let
the client make sure it has a valid address visible by the DW eDMA
controller. For instance, the DW eDMA PCIe glue-driver initializes the
field with addresses from the PCI bus memory space.
[1] DesignWare Cores PCI Express Controller Databook - DWC PCIe Root Port,
v.5.40a, March 2019, p.1103
Link: https://lore.kernel.org/r/20230113171409.30470-4-Sergey.Semin@baikalelectronics.ru
Fixes: 41aaff2a2ac0 ("dmaengine: Add Synopsys eDMA IP PCIe glue-logic")
Tested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Acked-by: Vinod Koul <vkoul@kernel.org>
2023-01-13 17:13:45 +00:00
|
|
|
ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
|
2021-02-18 19:04:03 +00:00
|
|
|
ll_region->paddr += ll_block->off;
|
|
|
|
ll_region->sz = ll_block->sz;
|
|
|
|
|
2023-01-13 17:14:05 +00:00
|
|
|
dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar];
|
|
|
|
if (!dt_region->vaddr.io)
|
2021-02-18 19:04:09 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-01-13 17:14:05 +00:00
|
|
|
dt_region->vaddr.io += dt_block->off;
|
dmaengine: dw-edma: Convert ll/dt phys address to PCI bus/DMA address
The dw_edma_region.paddr field should be a memory base address visible by
the DW eDMA controller. If the DMA engine is embedded in the DW PCIe
Host/Endpoint controller, the address should belong to the Local CPU/
Application memory. If eDMA is remotely accessible across the PCI bus via
PCI memory IOs, the address should be part of the PCI bus memory space.
The latter case hasn't been well covered in the corresponding glue-driver.
Since pci_dev.resource[] contains resources defined in the CPU memory
space, they need to be converted to the PCI bus address space. Convert the
LL, DT and CSRs PCI memory ranges with pci_bus_address().
In addition, extend the dw_edma_region.paddr field size. The field normally
contains a memory range base address to be set in the DW eDMA Linked-List
pointer register or as a base address of the Linked-List data buffer. In
accordance with [1] the LL range is supposed to be created in the Local
CPU/Application memory, but depending on the DW eDMA utilization the memory
can be created as a part of the PCI bus address space (as in the case of
the DW PCIe Endpoint prototype kit).
In the former case dw_edma_region.paddr should be a dma_addr_t, while in
the latter one it should be a pci_bus_addr_t. Since the corresponding CSRs
are always 64 bits wide, convert dw_edma_region.paddr to be u64, and let
the client make sure it has a valid address visible by the DW eDMA
controller. For instance, the DW eDMA PCIe glue-driver initializes the
field with addresses from the PCI bus memory space.
[1] DesignWare Cores PCI Express Controller Databook - DWC PCIe Root Port,
v.5.40a, March 2019, p.1103
Link: https://lore.kernel.org/r/20230113171409.30470-4-Sergey.Semin@baikalelectronics.ru
Fixes: 41aaff2a2ac0 ("dmaengine: Add Synopsys eDMA IP PCIe glue-logic")
Tested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Acked-by: Vinod Koul <vkoul@kernel.org>
2023-01-13 17:13:45 +00:00
|
|
|
dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
|
2021-02-18 19:04:03 +00:00
|
|
|
dt_region->paddr += dt_block->off;
|
|
|
|
dt_region->sz = dt_block->sz;
|
|
|
|
}
|
|
|
|
|
2019-06-04 13:29:26 +00:00
|
|
|
/* Debug info */
|
2022-05-24 15:21:53 +00:00
|
|
|
if (chip->mf == EDMA_MF_EDMA_LEGACY)
|
|
|
|
pci_dbg(pdev, "Version:\teDMA Port Logic (0x%x)\n", chip->mf);
|
|
|
|
else if (chip->mf == EDMA_MF_EDMA_UNROLL)
|
|
|
|
pci_dbg(pdev, "Version:\teDMA Unroll (0x%x)\n", chip->mf);
|
|
|
|
else if (chip->mf == EDMA_MF_HDMA_COMPAT)
|
|
|
|
pci_dbg(pdev, "Version:\tHDMA Compatible (0x%x)\n", chip->mf);
|
2021-02-18 19:03:57 +00:00
|
|
|
else
|
2022-05-24 15:21:53 +00:00
|
|
|
pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", chip->mf);
|
2019-06-04 13:29:26 +00:00
|
|
|
|
2022-05-24 15:21:53 +00:00
|
|
|
pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p)\n",
|
2021-02-18 19:04:03 +00:00
|
|
|
vsec_data.rg.bar, vsec_data.rg.off, vsec_data.rg.sz,
|
2022-05-24 15:21:54 +00:00
|
|
|
chip->reg_base);
|
2019-06-04 13:29:26 +00:00
|
|
|
|
|
|
|
|
2022-05-24 15:21:55 +00:00
|
|
|
for (i = 0; i < chip->ll_wr_cnt; i++) {
|
2021-02-18 19:04:03 +00:00
|
|
|
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,
|
2022-05-24 15:21:53 +00:00
|
|
|
vsec_data.ll_wr[i].off, chip->ll_region_wr[i].sz,
|
2023-01-13 17:14:05 +00:00
|
|
|
chip->ll_region_wr[i].vaddr.io, &chip->ll_region_wr[i].paddr);
|
2021-02-18 19:04:03 +00:00
|
|
|
|
|
|
|
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,
|
2022-05-24 15:21:53 +00:00
|
|
|
vsec_data.dt_wr[i].off, chip->dt_region_wr[i].sz,
|
2023-01-13 17:14:05 +00:00
|
|
|
chip->dt_region_wr[i].vaddr.io, &chip->dt_region_wr[i].paddr);
|
2021-02-18 19:04:03 +00:00
|
|
|
}
|
|
|
|
|
2022-05-24 15:21:55 +00:00
|
|
|
for (i = 0; i < chip->ll_rd_cnt; i++) {
|
2021-02-18 19:04:03 +00:00
|
|
|
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,
|
2022-05-24 15:21:53 +00:00
|
|
|
vsec_data.ll_rd[i].off, chip->ll_region_rd[i].sz,
|
2023-01-13 17:14:05 +00:00
|
|
|
chip->ll_region_rd[i].vaddr.io, &chip->ll_region_rd[i].paddr);
|
2021-02-18 19:04:03 +00:00
|
|
|
|
|
|
|
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,
|
2022-05-24 15:21:53 +00:00
|
|
|
vsec_data.dt_rd[i].off, chip->dt_region_rd[i].sz,
|
2023-01-13 17:14:05 +00:00
|
|
|
chip->dt_region_rd[i].vaddr.io, &chip->dt_region_rd[i].paddr);
|
2021-02-18 19:04:03 +00:00
|
|
|
}
|
2019-06-04 13:29:26 +00:00
|
|
|
|
2022-05-24 15:21:53 +00:00
|
|
|
pci_dbg(pdev, "Nr. IRQs:\t%u\n", chip->nr_irqs);
|
2019-06-04 13:29:26 +00:00
|
|
|
|
|
|
|
/* Validating if PCI interrupts were enabled */
|
|
|
|
if (!pci_dev_msi_enabled(pdev)) {
|
|
|
|
pci_err(pdev, "enable interrupt failed\n");
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Starting eDMA driver */
|
|
|
|
err = dw_edma_probe(chip);
|
|
|
|
if (err) {
|
|
|
|
pci_err(pdev, "eDMA probe failed\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Saving data structure reference */
|
|
|
|
pci_set_drvdata(pdev, chip);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dw_edma_pcie_remove(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct dw_edma_chip *chip = pci_get_drvdata(pdev);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Stopping eDMA driver */
|
|
|
|
err = dw_edma_remove(chip);
|
|
|
|
if (err)
|
|
|
|
pci_warn(pdev, "can't remove device properly: %d\n", err);
|
|
|
|
|
|
|
|
/* Freeing IRQs */
|
|
|
|
pci_free_irq_vectors(pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct pci_device_id dw_edma_pcie_id_table[] = {
|
|
|
|
{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
|
|
|
|
|
|
|
|
static struct pci_driver dw_edma_pcie_driver = {
|
|
|
|
.name = "dw-edma-pcie",
|
|
|
|
.id_table = dw_edma_pcie_id_table,
|
|
|
|
.probe = dw_edma_pcie_probe,
|
|
|
|
.remove = dw_edma_pcie_remove,
|
|
|
|
};
|
|
|
|
|
|
|
|
module_pci_driver(dw_edma_pcie_driver);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
|
|
MODULE_DESCRIPTION("Synopsys DesignWare eDMA PCIe driver");
|
|
|
|
MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
|