linux-brain/drivers/pci/controller/dwc/pci-imx6-ep.c
Richard Zhu f503843f85 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-27 11:16:57 +08:00

177 lines
4.0 KiB
C

// 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");