Files
linux-nv-oot/drivers/misc/tegra-pcie-dma-test.c
Manikanta Maddireddy fc47fbceba PCI: Fix PCIe Endpoint device IDs
PCIe device ids 0x229a and 0x229c belongs to Root port, so this driver
is binding instead of pcieport. Tegra PCIe Endpoint production device ids
are 0x1ad4 and 0x1ad5, correct the device ids accordingly.

Bug 4477934

Change-Id: Ia86c4dcc6b4527e984a473c941713ef58dd6f74d
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3067109
Reviewed-by: Nagarjuna Kristam <nkristam@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2024-02-02 04:21:13 -08:00

313 lines
8.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* PCIe DMA test framework for Tegra PCIe.
*
* Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#include <nvidia/conftest.h>
#include <linux/aer.h>
#include <linux/delay.h>
#include <linux/crc32.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/pcie_dma.h>
#include <linux/random.h>
#include <linux/types.h>
#include <linux/tegra-pcie-edma-test-common.h>
#define MODULENAME "pcie_dma_host"
struct ep_pvt {
struct pci_dev *pdev;
void __iomem *bar0_virt;
void __iomem *dma_base;
u32 dma_size;
void *dma_virt;
dma_addr_t dma_phy;
dma_addr_t bar0_phy;
struct dentry *debugfs;
void *cookie;
u32 stress_count;
u32 edma_ch;
u32 prev_edma_ch;
u32 msi_irq;
u64 msi_addr;
u16 msi_data;
phys_addr_t dma_phy_base;
u32 dma_phy_size;
u64 tsz;
ktime_t edma_start_time[DMA_WR_CHNL_NUM];
struct edmalib_common edma;
};
static irqreturn_t ep_isr(int irq, void *arg)
{
struct ep_pvt *ep = (struct ep_pvt *)arg;
struct pcie_epf_bar0 *epf_bar0 = (__force struct pcie_epf_bar0 *)ep->bar0_virt;
epf_bar0->wr_data[0].crc = crc32_le(~0, ep->dma_virt + BAR0_DMA_BUF_OFFSET,
epf_bar0->wr_data[0].size);
return IRQ_HANDLED;
}
static void tegra_pcie_dma_raise_irq(void *p)
{
pr_err("%s: donot support raise IRQ from RP. CRC test if any started may fail.\n",
__func__);
}
static struct device *tegra_pci_dma_get_host_bridge_device(struct pci_dev *dev)
{
struct pci_bus *bus = dev->bus;
struct device *bridge;
while (bus->parent)
bus = bus->parent;
bridge = bus->bridge;
kobject_get(&bridge->kobj);
return bridge;
}
static void tegra_pci_dma_put_host_bridge_device(struct device *dev)
{
kobject_put(&dev->kobj);
}
/* debugfs to perform eDMA lib transfers */
static int edmalib_test(struct seq_file *s, void *data)
{
struct ep_pvt *ep = (struct ep_pvt *)dev_get_drvdata(s->private);
struct pcie_epf_bar0 *epf_bar0 = (__force struct pcie_epf_bar0 *)ep->bar0_virt;
/* RP uses 128M(used by EP) + 1M(reserved) offset for source and dest data transfers */
dma_addr_t ep_dma_addr = epf_bar0->ep_phy_addr + SZ_128M + SZ_1M;
dma_addr_t bar0_dma_addr = ep->bar0_phy + SZ_128M + SZ_1M;
dma_addr_t rp_dma_addr = ep->dma_phy + SZ_128M + SZ_1M;
struct pci_dev *pdev = ep->pdev;
struct device *bridge, *rdev;
struct edmalib_common *edma = &ep->edma;
ep->edma.src_dma_addr = rp_dma_addr;
ep->edma.src_virt = ep->dma_virt + SZ_128M + SZ_1M;
ep->edma.fdev = &ep->pdev->dev;
ep->edma.epf_bar0 = (__force struct pcie_epf_bar0 *)ep->bar0_virt;
ep->edma.bar0_phy = ep->bar0_phy;
ep->edma.dma_base = ep->dma_base;
ep->edma.priv = (void *)ep;
ep->edma.raise_irq = tegra_pcie_dma_raise_irq;
if (REMOTE_EDMA_TEST_EN) {
ep->edma.dst_dma_addr = ep_dma_addr;
ep->edma.edma_remote.msi_addr = ep->msi_addr;
ep->edma.edma_remote.msi_data = ep->msi_data;
ep->edma.edma_remote.msi_irq = ep->msi_irq;
ep->edma.edma_remote.dma_phy_base = ep->dma_phy_base;
ep->edma.edma_remote.dma_size = ep->dma_phy_size;
ep->edma.edma_remote.dev = &pdev->dev;
} else {
bridge = tegra_pci_dma_get_host_bridge_device(pdev);
rdev = bridge->parent;
tegra_pci_dma_put_host_bridge_device(bridge);
ep->edma.of_node = rdev->of_node;
ep->edma.dst_dma_addr = bar0_dma_addr;
}
return edmalib_common_test(&ep->edma);
}
static void init_debugfs(struct ep_pvt *ep)
{
debugfs_create_devm_seqfile(&ep->pdev->dev, "edmalib_test", ep->debugfs, edmalib_test);
debugfs_create_u32("edma_ch", 0644, ep->debugfs, &ep->edma.edma_ch);
/* Enable remote dma ASYNC for ch 0 as default */
ep->edma.edma_ch = 0x80000011;
ep->edma.st_as_ch = -1;
debugfs_create_u32("stress_count", 0644, ep->debugfs, &ep->edma.stress_count);
ep->edma.stress_count = 10;
debugfs_create_u32("dma_size", 0644, ep->debugfs, &ep->edma.dma_size);
ep->edma.dma_size = SZ_1M;
debugfs_create_u32("nents", 0644, ep->debugfs, &ep->edma.nents);
/* Set DMA_LL_DEFAULT_SIZE as default nents, Max NUM_EDMA_DESC */
ep->edma.nents = DMA_LL_DEFAULT_SIZE;
}
static int ep_test_dma_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct ep_pvt *ep;
struct pcie_epf_bar0 *epf_bar0;
int ret = 0;
u32 val, i;
u16 val_16;
char *name;
ep = devm_kzalloc(&pdev->dev, sizeof(*ep), GFP_KERNEL);
if (!ep)
return -ENOMEM;
ep->edma.ll_desc = devm_kzalloc(&pdev->dev, sizeof(*ep->edma.ll_desc) * NUM_EDMA_DESC,
GFP_KERNEL);
if (!ep->edma.ll_desc)
return -ENOMEM;
ep->pdev = pdev;
pci_set_drvdata(pdev, ep);
ret = pci_enable_device(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to enable PCI device\n");
return ret;
}
#if defined(NV_PCI_ENABLE_PCIE_ERROR_REPORTING_PRESENT) /* Linux 6.5 */
pci_enable_pcie_error_reporting(pdev);
#endif
pci_set_master(pdev);
ret = pci_request_regions(pdev, MODULENAME);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to request PCI regions\n");
goto fail_region_request;
}
ep->bar0_phy = pci_resource_start(pdev, 0);
ep->bar0_virt = devm_ioremap(&pdev->dev, ep->bar0_phy, pci_resource_len(pdev, 0));
if (!ep->bar0_virt) {
dev_err(&pdev->dev, "Failed to IO remap BAR0\n");
ret = -ENOMEM;
goto fail_region_remap;
}
ep->dma_base = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 4),
pci_resource_len(pdev, 4));
if (!ep->dma_base) {
dev_err(&pdev->dev, "Failed to IO remap BAR4\n");
ret = -ENOMEM;
goto fail_region_remap;
}
ret = pci_alloc_irq_vectors(pdev, 2, 2, PCI_IRQ_MSI);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to enable MSI interrupt\n");
ret = -ENODEV;
goto fail_region_remap;
}
ret = request_irq(pci_irq_vector(pdev, 1), ep_isr, IRQF_SHARED,
"pcie_ep_isr", ep);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register isr\n");
goto fail_isr;
}
ep->dma_virt = dma_alloc_coherent(&pdev->dev, BAR0_SIZE, &ep->dma_phy,
GFP_KERNEL);
if (!ep->dma_virt) {
dev_err(&pdev->dev, "Failed to allocate DMA memory\n");
ret = -ENOMEM;
goto fail_dma_alloc;
}
get_random_bytes(ep->dma_virt, BAR0_SIZE);
/* Update RP DMA system memory base address in BAR0 */
epf_bar0 = (__force struct pcie_epf_bar0 *)ep->bar0_virt;
epf_bar0->rp_phy_addr = ep->dma_phy;
dev_info(&pdev->dev, "DMA mem, IOVA: 0x%llx size: %d\n", ep->dma_phy, BAR0_SIZE);
pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &val_16);
if (val_16 & PCI_MSI_FLAGS_64BIT) {
pci_read_config_dword(pdev, pdev->msi_cap + PCI_MSI_ADDRESS_HI, &val);
ep->msi_addr = val;
pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_DATA_64, &val_16);
ep->msi_data = val_16;
} else {
pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_DATA_32, &val_16);
ep->msi_data = val_16;
}
pci_read_config_dword(pdev, pdev->msi_cap + PCI_MSI_ADDRESS_LO, &val);
ep->msi_addr = (ep->msi_addr << 32) | val;
ep->msi_irq = pci_irq_vector(pdev, 0);
ep->dma_phy_base = pci_resource_start(pdev, 4);
ep->dma_phy_size = pci_resource_len(pdev, 4);
name = devm_kasprintf(&ep->pdev->dev, GFP_KERNEL, "%s_pcie_dma_test", dev_name(&pdev->dev));
if (!name) {
dev_err(&pdev->dev, "%s: Fail to set debugfs name\n", __func__);
ret = -ENOMEM;
goto fail_name;
}
for (i = 0; i < DMA_WR_CHNL_NUM; i++)
init_waitqueue_head(&ep->edma.wr_wq[i]);
for (i = 0; i < DMA_RD_CHNL_NUM; i++)
init_waitqueue_head(&ep->edma.rd_wq[i]);
ep->debugfs = debugfs_create_dir(name, NULL);
init_debugfs(ep);
return ret;
fail_name:
dma_free_coherent(&pdev->dev, BAR0_SIZE, ep->dma_virt, ep->dma_phy);
fail_dma_alloc:
free_irq(pci_irq_vector(pdev, 1), ep);
fail_isr:
pci_free_irq_vectors(pdev);
fail_region_remap:
pci_release_regions(pdev);
fail_region_request:
pci_clear_master(pdev);
return ret;
}
static void ep_test_dma_remove(struct pci_dev *pdev)
{
struct ep_pvt *ep = pci_get_drvdata(pdev);
debugfs_remove_recursive(ep->debugfs);
tegra_pcie_edma_deinit(ep->edma.cookie);
dma_free_coherent(&pdev->dev, BAR0_SIZE, ep->dma_virt, ep->dma_phy);
free_irq(pci_irq_vector(pdev, 1), ep);
pci_free_irq_vectors(pdev);
pci_release_regions(pdev);
pci_clear_master(pdev);
}
static const struct pci_device_id ep_pci_tbl[] = {
{ PCI_DEVICE(0x10DE, 0x1AD4)},
{ PCI_DEVICE(0x10DE, 0x1AD5)},
{},
};
MODULE_DEVICE_TABLE(pci, ep_pci_tbl);
static struct pci_driver ep_pci_driver = {
.name = MODULENAME,
.id_table = ep_pci_tbl,
.probe = ep_test_dma_probe,
.remove = ep_test_dma_remove,
};
module_pci_driver(ep_pci_driver);
MODULE_DESCRIPTION("Tegra PCIe client driver for endpoint DMA test func");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Manikanta Maddireddy <mmaddireddy@nvidia.com>");