1
0
Fork 0
alistair23-linux/drivers/pci/controller/dwc/pci-imx6-ep.c

177 lines
4.0 KiB
C
Raw Normal View History

PCI: imx: add the imx pcie ep verification solution Enable the PCIE EP RC for iMX - hw setup: * two imx boards, one is used as pcie rc, the other is used as pcie ep. RC TX N/P <--> EP RX N/P RX N/P <--> EP TX N/P - sw setup: * when build rc image, make sure that CONFIG_PCI_IMX6=y CONFIG_RC_MODE_IN_EP_RC_SYS=y * when build ep image CONFIG_PCI_IMX6=y CONFIG_EP_MODE_IN_EP_RC_SYS=y - features: * set-up link between rc and ep by their stand-alone ref clk running internally. * in ep's system, ep can access the reserved ddr memory (default address:0x4000_0000 on imx6q sd board, and 0xb000_0000 on imx6sx sdb and imx7d arm2 boards) of pcie rc's system, by the interconnection between pcie ep and pcie rc. * provide one example, howto configure the bar# of ep and so on, when pcie ep emaluates one memory ram ep device * setup one new outbound memory region at rc side, let imx pcie rc can access the memory of imx pcie ep in imx pcie rc ep validation system. - NOTE: * boot up ep platform firstly, then boot up rc platform. * For imx6q/6dl/6sx/7d sabresd boards, make sure that mem=768M is contained in the kernel command line, since the start address of the upper 256mb of the 1g ddr mem is reserved to do the pcie ep rc access operations in default. - RC access memory of EP: - EP: write the <ddr_region_address> to the bar0 of ep. echo <ddr_region_address> > /sys/devices/.../pcie/ep_bar0_addr - RC: access the <pcie_mem_base_addr>, and this address would be mapped to the <ddr_region_address> of ep. - Note: ddr_region_address pcie_mem_base_addr bar0_addr imx6qdl 0x4000_0000 0x0100_0000 0x01ff_c010 imx6sx 0xb000_0000 0x0800_0000 0x08ff_c010 imx7d 0xb000_0000 0x4000_0000 0x3380_0010 imx8mq 0xb820_0000 0x2000_0000 0x33c0_0010 imx8mm 0xb820_0000 0x1800_0000 0x3380_0010 imx8qm 0x9020_0000 0x6000_0000 0x5f00_0010 imx8qxp 0x9020_0000 0x7000_0000 0x5f01_0010 - The example of the RC access memory of EP step1: EP side: echo 0x90200000 > /sys/devices/platform/bus@5f000000/5f000000.pcie /ep_bar0_addr root@imx8_all:~# ./memtool 90200000 4 Reading 0x4 count starting at address 0x90200000 0x90200000: 00000000 00000000 00000000 00000000 RC side: ./memtool 60000000=55aa55aa Writing 32-bit value 0x55AA55AA to address 0x60000000 EP side: root@imx8_all:~# ./memtool 90200000 4 Reading 0x4 count starting at address 0x90200000 0x90200000: 55AA55AA 00000000 00000000 00000000 Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com> Reviewed-by: Fugang Duan <fugang.duan@nxp.com>
2019-11-21 19:10:00 -07:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define DRV_DESCRIPTION "i.MX PCIE endpoint device driver"
#define DRV_VERSION "version 0.1"
#define DRV_NAME "imx_pcie_ep"
struct imx_pcie_ep_priv {
struct pci_dev *pci_dev;
};
/**
* imx_pcie_ep_probe - Device Initialization Routine
* @pdev: PCI device information struct
* @id: entry in id_tbl
*
* Returns 0 on success, negative on failure
**/
static int imx_pcie_ep_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int ret = 0, index = 0, found = 0;
unsigned int hard_wired = 0, msi_addr = 0, local_addr;
struct resource cfg_res;
const char *name = NULL;
struct device_node *np = NULL;
struct device *dev = &pdev->dev;
struct imx_pcie_ep_priv *priv;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->pci_dev = pdev;
if (pci_enable_device(pdev)) {
ret = -ENODEV;
goto out;
}
pci_set_master(pdev);
pci_set_drvdata(pdev, priv);
ret = pci_enable_msi(priv->pci_dev);
if (ret < 0) {
dev_err(dev, "can't enable msi\n");
goto err_pci_unmap_mmio;
}
/* Use the first none-hard-wired port as ep */
while ((np = of_find_node_by_type(np, "pci"))) {
if (!of_device_is_available(np))
continue;
if (of_property_read_u32(np, "hard-wired", &hard_wired)) {
if (hard_wired == 0)
break;
}
}
if (of_property_read_u32(np, "local-addr", &local_addr))
local_addr = 0;
while (!of_property_read_string_index(np, "reg-names", index, &name)) {
if (strcmp("config", name)) {
index++;
continue;
}
found = 1;
break;
}
if (!found) {
dev_err(dev, "can't find config reg space.\n");
ret = -EINVAL;
goto err_pci_disable_msi;
}
ret = of_address_to_resource(np, index, &cfg_res);
if (ret) {
dev_err(dev, "can't get cfg_res.\n");
ret = -EINVAL;
goto err_pci_disable_msi;
} else {
msi_addr = cfg_res.start + resource_size(&cfg_res);
}
pr_info("msi_addr 0x%08x, local_addr 0x%08x\n", msi_addr, local_addr);
pci_bus_write_config_dword(pdev->bus, 0, 0x54, msi_addr);
if (local_addr) {
msi_addr = msi_addr & 0xFFFFFFF;
msi_addr |= (local_addr & 0xF0000000);
}
pci_bus_write_config_dword(pdev->bus->parent, 0, 0x820, msi_addr);
/* configure rc's msi cap */
pci_bus_read_config_dword(pdev->bus->parent, 0, 0x50, &ret);
ret |= (PCI_MSI_FLAGS_ENABLE << 16);
pci_bus_write_config_dword(pdev->bus->parent, 0, 0x50, ret);
pci_bus_write_config_dword(pdev->bus->parent, 0, 0x828, 0x1);
pci_bus_write_config_dword(pdev->bus->parent, 0, 0x82C, 0xFFFFFFFE);
return 0;
err_pci_disable_msi:
pci_disable_msi(pdev);
err_pci_unmap_mmio:
pci_disable_device(pdev);
out:
kfree(priv);
return ret;
}
static void imx_pcie_ep_remove(struct pci_dev *pdev)
{
struct imx_pcie_ep_priv *priv = pci_get_drvdata(pdev);
if (!priv)
return;
pr_info("***imx pcie ep driver unload***\n");
}
static struct pci_device_id imx_pcie_ep_ids[] = {
{
.class = PCI_CLASS_MEMORY_RAM << 8,
.class_mask = ~0,
.vendor = 0xbeaf,
.device = 0xdead,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, imx_pcie_ep_ids);
static struct pci_driver imx_pcie_ep_driver = {
.name = DRV_NAME,
.id_table = imx_pcie_ep_ids,
.probe = imx_pcie_ep_probe,
.remove = imx_pcie_ep_remove,
};
static int __init imx_pcie_ep_init(void)
{
int ret;
pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n");
ret = pci_register_driver(&imx_pcie_ep_driver);
if (ret)
pr_err("Unable to initialize PCI module\n");
return ret;
}
static void __exit imx_pcie_ep_exit(void)
{
pci_unregister_driver(&imx_pcie_ep_driver);
}
module_exit(imx_pcie_ep_exit);
module_init(imx_pcie_ep_init);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("imx_pcie_ep");