PCI: cadence: Refactor driver to use as a core library
Cadence PCIe host and endpoint IP may be embedded into a variety of SoCs/platforms. Let's extract the platform related APIs/Structures in the current driver to a separate file (pcie-cadence-plat.c), such that the common functionality can be used by future platforms. Signed-off-by: Tom Joseph <tjoseph@cadence.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Reviewed-by: Andrew Murray <andrew.murray@arm.com>
This commit is contained in:
parent
54ecb8f702
commit
bd22885aa1
6 changed files with 287 additions and 187 deletions
|
@ -28,23 +28,38 @@ config PCIE_CADENCE
|
||||||
bool
|
bool
|
||||||
|
|
||||||
config PCIE_CADENCE_HOST
|
config PCIE_CADENCE_HOST
|
||||||
bool "Cadence PCIe host controller"
|
bool
|
||||||
depends on OF
|
depends on OF
|
||||||
depends on PCI
|
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select PCIE_CADENCE
|
select PCIE_CADENCE
|
||||||
help
|
|
||||||
Say Y here if you want to support the Cadence PCIe controller in host
|
|
||||||
mode. This PCIe controller may be embedded into many different vendors
|
|
||||||
SoCs.
|
|
||||||
|
|
||||||
config PCIE_CADENCE_EP
|
config PCIE_CADENCE_EP
|
||||||
bool "Cadence PCIe endpoint controller"
|
bool
|
||||||
depends on OF
|
depends on OF
|
||||||
depends on PCI_ENDPOINT
|
depends on PCI_ENDPOINT
|
||||||
select PCIE_CADENCE
|
select PCIE_CADENCE
|
||||||
|
|
||||||
|
config PCIE_CADENCE_PLAT
|
||||||
|
bool
|
||||||
|
|
||||||
|
config PCIE_CADENCE_PLAT_HOST
|
||||||
|
bool "Cadence PCIe platform host controller"
|
||||||
|
depends on OF
|
||||||
|
select PCIE_CADENCE_HOST
|
||||||
|
select PCIE_CADENCE_PLAT
|
||||||
help
|
help
|
||||||
Say Y here if you want to support the Cadence PCIe controller in
|
Say Y here if you want to support the Cadence PCIe platform controller in
|
||||||
|
host mode. This PCIe controller may be embedded into many different
|
||||||
|
vendors SoCs.
|
||||||
|
|
||||||
|
config PCIE_CADENCE_PLAT_EP
|
||||||
|
bool "Cadence PCIe platform endpoint controller"
|
||||||
|
depends on OF
|
||||||
|
depends on PCI_ENDPOINT
|
||||||
|
select PCIE_CADENCE_EP
|
||||||
|
select PCIE_CADENCE_PLAT
|
||||||
|
help
|
||||||
|
Say Y here if you want to support the Cadence PCIe platform controller in
|
||||||
endpoint mode. This PCIe controller may be embedded into many
|
endpoint mode. This PCIe controller may be embedded into many
|
||||||
different vendors SoCs.
|
different vendors SoCs.
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o
|
obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o
|
||||||
obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o
|
obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o
|
||||||
obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o
|
obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o
|
||||||
|
obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o
|
||||||
obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o
|
obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o
|
||||||
obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o
|
obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o
|
||||||
obj-$(CONFIG_PCI_HYPERV_INTERFACE) += pci-hyperv-intf.o
|
obj-$(CONFIG_PCI_HYPERV_INTERFACE) += pci-hyperv-intf.o
|
||||||
|
|
|
@ -17,35 +17,6 @@
|
||||||
#define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE 0x1
|
#define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE 0x1
|
||||||
#define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x3
|
#define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x3
|
||||||
|
|
||||||
/**
|
|
||||||
* struct cdns_pcie_ep - private data for this PCIe endpoint controller driver
|
|
||||||
* @pcie: Cadence PCIe controller
|
|
||||||
* @max_regions: maximum number of regions supported by hardware
|
|
||||||
* @ob_region_map: bitmask of mapped outbound regions
|
|
||||||
* @ob_addr: base addresses in the AXI bus where the outbound regions start
|
|
||||||
* @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ
|
|
||||||
* dedicated outbound regions is mapped.
|
|
||||||
* @irq_cpu_addr: base address in the CPU space where a write access triggers
|
|
||||||
* the sending of a memory write (MSI) / normal message (legacy
|
|
||||||
* IRQ) TLP through the PCIe bus.
|
|
||||||
* @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ
|
|
||||||
* dedicated outbound region.
|
|
||||||
* @irq_pci_fn: the latest PCI function that has updated the mapping of
|
|
||||||
* the MSI/legacy IRQ dedicated outbound region.
|
|
||||||
* @irq_pending: bitmask of asserted legacy IRQs.
|
|
||||||
*/
|
|
||||||
struct cdns_pcie_ep {
|
|
||||||
struct cdns_pcie pcie;
|
|
||||||
u32 max_regions;
|
|
||||||
unsigned long ob_region_map;
|
|
||||||
phys_addr_t *ob_addr;
|
|
||||||
phys_addr_t irq_phys_addr;
|
|
||||||
void __iomem *irq_cpu_addr;
|
|
||||||
u64 irq_pci_addr;
|
|
||||||
u8 irq_pci_fn;
|
|
||||||
u8 irq_pending;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn,
|
static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn,
|
||||||
struct pci_epf_header *hdr)
|
struct pci_epf_header *hdr)
|
||||||
{
|
{
|
||||||
|
@ -424,28 +395,17 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
|
||||||
.get_features = cdns_pcie_ep_get_features,
|
.get_features = cdns_pcie_ep_get_features,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id cdns_pcie_ep_of_match[] = {
|
|
||||||
{ .compatible = "cdns,cdns-pcie-ep" },
|
|
||||||
|
|
||||||
{ },
|
int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
|
||||||
};
|
|
||||||
|
|
||||||
static int cdns_pcie_ep_probe(struct platform_device *pdev)
|
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = ep->pcie.dev;
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct cdns_pcie_ep *ep;
|
struct cdns_pcie *pcie = &ep->pcie;
|
||||||
struct cdns_pcie *pcie;
|
|
||||||
struct pci_epc *epc;
|
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
struct pci_epc *epc;
|
||||||
int ret;
|
int ret;
|
||||||
int phy_count;
|
|
||||||
|
|
||||||
ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
|
|
||||||
if (!ep)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
pcie = &ep->pcie;
|
|
||||||
pcie->is_rc = false;
|
pcie->is_rc = false;
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg");
|
||||||
|
@ -474,19 +434,6 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev)
|
||||||
if (!ep->ob_addr)
|
if (!ep->ob_addr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = cdns_pcie_init_phy(dev, pcie);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "failed to init phy\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
platform_set_drvdata(pdev, pcie);
|
|
||||||
pm_runtime_enable(dev);
|
|
||||||
ret = pm_runtime_get_sync(dev);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "pm_runtime_get_sync() failed\n");
|
|
||||||
goto err_get_sync;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */
|
/* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */
|
||||||
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0));
|
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0));
|
||||||
|
|
||||||
|
@ -528,38 +475,5 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev)
|
||||||
err_init:
|
err_init:
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_put_sync(dev);
|
||||||
|
|
||||||
err_get_sync:
|
|
||||||
pm_runtime_disable(dev);
|
|
||||||
cdns_pcie_disable_phy(pcie);
|
|
||||||
phy_count = pcie->phy_count;
|
|
||||||
while (phy_count--)
|
|
||||||
device_link_del(pcie->link[phy_count]);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cdns_pcie_ep_shutdown(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
struct cdns_pcie *pcie = dev_get_drvdata(dev);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = pm_runtime_put_sync(dev);
|
|
||||||
if (ret < 0)
|
|
||||||
dev_dbg(dev, "pm_runtime_put_sync failed\n");
|
|
||||||
|
|
||||||
pm_runtime_disable(dev);
|
|
||||||
|
|
||||||
cdns_pcie_disable_phy(pcie);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct platform_driver cdns_pcie_ep_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "cdns-pcie-ep",
|
|
||||||
.of_match_table = cdns_pcie_ep_of_match,
|
|
||||||
.pm = &cdns_pcie_pm_ops,
|
|
||||||
},
|
|
||||||
.probe = cdns_pcie_ep_probe,
|
|
||||||
.shutdown = cdns_pcie_ep_shutdown,
|
|
||||||
};
|
|
||||||
builtin_platform_driver(cdns_pcie_ep_driver);
|
|
||||||
|
|
|
@ -11,33 +11,6 @@
|
||||||
|
|
||||||
#include "pcie-cadence.h"
|
#include "pcie-cadence.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* struct cdns_pcie_rc - private data for this PCIe Root Complex driver
|
|
||||||
* @pcie: Cadence PCIe controller
|
|
||||||
* @dev: pointer to PCIe device
|
|
||||||
* @cfg_res: start/end offsets in the physical system memory to map PCI
|
|
||||||
* configuration space accesses
|
|
||||||
* @bus_range: first/last buses behind the PCIe host controller
|
|
||||||
* @cfg_base: IO mapped window to access the PCI configuration space of a
|
|
||||||
* single function at a time
|
|
||||||
* @max_regions: maximum number of regions supported by the hardware
|
|
||||||
* @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address
|
|
||||||
* translation (nbits sets into the "no BAR match" register)
|
|
||||||
* @vendor_id: PCI vendor ID
|
|
||||||
* @device_id: PCI device ID
|
|
||||||
*/
|
|
||||||
struct cdns_pcie_rc {
|
|
||||||
struct cdns_pcie pcie;
|
|
||||||
struct device *dev;
|
|
||||||
struct resource *cfg_res;
|
|
||||||
struct resource *bus_range;
|
|
||||||
void __iomem *cfg_base;
|
|
||||||
u32 max_regions;
|
|
||||||
u32 no_bar_nbits;
|
|
||||||
u16 vendor_id;
|
|
||||||
u16 device_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
|
static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where)
|
int where)
|
||||||
{
|
{
|
||||||
|
@ -92,11 +65,6 @@ static struct pci_ops cdns_pcie_host_ops = {
|
||||||
.write = pci_generic_config_write,
|
.write = pci_generic_config_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id cdns_pcie_host_of_match[] = {
|
|
||||||
{ .compatible = "cdns,cdns-pcie-host" },
|
|
||||||
|
|
||||||
{ },
|
|
||||||
};
|
|
||||||
|
|
||||||
static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
|
static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
|
||||||
{
|
{
|
||||||
|
@ -136,10 +104,10 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
|
||||||
static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc)
|
static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc)
|
||||||
{
|
{
|
||||||
struct cdns_pcie *pcie = &rc->pcie;
|
struct cdns_pcie *pcie = &rc->pcie;
|
||||||
struct resource *cfg_res = rc->cfg_res;
|
|
||||||
struct resource *mem_res = pcie->mem_res;
|
struct resource *mem_res = pcie->mem_res;
|
||||||
struct resource *bus_range = rc->bus_range;
|
struct resource *bus_range = rc->bus_range;
|
||||||
struct device *dev = rc->dev;
|
struct resource *cfg_res = rc->cfg_res;
|
||||||
|
struct device *dev = pcie->dev;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct of_pci_range_parser parser;
|
struct of_pci_range_parser parser;
|
||||||
struct of_pci_range range;
|
struct of_pci_range range;
|
||||||
|
@ -233,25 +201,21 @@ static int cdns_pcie_host_init(struct device *dev,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cdns_pcie_host_probe(struct platform_device *pdev)
|
int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = rc->pcie.dev;
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct pci_host_bridge *bridge;
|
struct pci_host_bridge *bridge;
|
||||||
struct list_head resources;
|
struct list_head resources;
|
||||||
struct cdns_pcie_rc *rc;
|
|
||||||
struct cdns_pcie *pcie;
|
struct cdns_pcie *pcie;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int ret;
|
int ret;
|
||||||
int phy_count;
|
|
||||||
|
|
||||||
bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc));
|
bridge = pci_host_bridge_from_priv(rc);
|
||||||
if (!bridge)
|
if (!bridge)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
rc = pci_host_bridge_priv(bridge);
|
|
||||||
rc->dev = dev;
|
|
||||||
|
|
||||||
pcie = &rc->pcie;
|
pcie = &rc->pcie;
|
||||||
pcie->is_rc = true;
|
pcie->is_rc = true;
|
||||||
|
|
||||||
|
@ -287,22 +251,9 @@ static int cdns_pcie_host_probe(struct platform_device *pdev)
|
||||||
dev_err(dev, "missing \"mem\"\n");
|
dev_err(dev, "missing \"mem\"\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcie->mem_res = res;
|
pcie->mem_res = res;
|
||||||
|
|
||||||
ret = cdns_pcie_init_phy(dev, pcie);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "failed to init phy\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
platform_set_drvdata(pdev, pcie);
|
|
||||||
|
|
||||||
pm_runtime_enable(dev);
|
|
||||||
ret = pm_runtime_get_sync(dev);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "pm_runtime_get_sync() failed\n");
|
|
||||||
goto err_get_sync;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = cdns_pcie_host_init(dev, &resources, rc);
|
ret = cdns_pcie_host_init(dev, &resources, rc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_init;
|
goto err_init;
|
||||||
|
@ -326,37 +277,5 @@ static int cdns_pcie_host_probe(struct platform_device *pdev)
|
||||||
err_init:
|
err_init:
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_put_sync(dev);
|
||||||
|
|
||||||
err_get_sync:
|
|
||||||
pm_runtime_disable(dev);
|
|
||||||
cdns_pcie_disable_phy(pcie);
|
|
||||||
phy_count = pcie->phy_count;
|
|
||||||
while (phy_count--)
|
|
||||||
device_link_del(pcie->link[phy_count]);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cdns_pcie_shutdown(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
struct cdns_pcie *pcie = dev_get_drvdata(dev);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = pm_runtime_put_sync(dev);
|
|
||||||
if (ret < 0)
|
|
||||||
dev_dbg(dev, "pm_runtime_put_sync failed\n");
|
|
||||||
|
|
||||||
pm_runtime_disable(dev);
|
|
||||||
cdns_pcie_disable_phy(pcie);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct platform_driver cdns_pcie_host_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "cdns-pcie-host",
|
|
||||||
.of_match_table = cdns_pcie_host_of_match,
|
|
||||||
.pm = &cdns_pcie_pm_ops,
|
|
||||||
},
|
|
||||||
.probe = cdns_pcie_host_probe,
|
|
||||||
.shutdown = cdns_pcie_shutdown,
|
|
||||||
};
|
|
||||||
builtin_platform_driver(cdns_pcie_host_driver);
|
|
||||||
|
|
174
drivers/pci/controller/pcie-cadence-plat.c
Normal file
174
drivers/pci/controller/pcie-cadence-plat.c
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Cadence PCIe platform driver.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019, Cadence Design Systems
|
||||||
|
* Author: Tom Joseph <tjoseph@cadence.com>
|
||||||
|
*/
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_pci.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include "pcie-cadence.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct cdns_plat_pcie - private data for this PCIe platform driver
|
||||||
|
* @pcie: Cadence PCIe controller
|
||||||
|
* @is_rc: Set to 1 indicates the PCIe controller mode is Root Complex,
|
||||||
|
* if 0 it is in Endpoint mode.
|
||||||
|
*/
|
||||||
|
struct cdns_plat_pcie {
|
||||||
|
struct cdns_pcie *pcie;
|
||||||
|
bool is_rc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cdns_plat_pcie_of_data {
|
||||||
|
bool is_rc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id cdns_plat_pcie_of_match[];
|
||||||
|
|
||||||
|
static int cdns_plat_pcie_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
const struct cdns_plat_pcie_of_data *data;
|
||||||
|
struct cdns_plat_pcie *cdns_plat_pcie;
|
||||||
|
const struct of_device_id *match;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct pci_host_bridge *bridge;
|
||||||
|
struct cdns_pcie_ep *ep;
|
||||||
|
struct cdns_pcie_rc *rc;
|
||||||
|
int phy_count;
|
||||||
|
bool is_rc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
match = of_match_device(cdns_plat_pcie_of_match, dev);
|
||||||
|
if (!match)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
data = (struct cdns_plat_pcie_of_data *)match->data;
|
||||||
|
is_rc = data->is_rc;
|
||||||
|
|
||||||
|
pr_debug(" Started %s with is_rc: %d\n", __func__, is_rc);
|
||||||
|
cdns_plat_pcie = devm_kzalloc(dev, sizeof(*cdns_plat_pcie), GFP_KERNEL);
|
||||||
|
if (!cdns_plat_pcie)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, cdns_plat_pcie);
|
||||||
|
if (is_rc) {
|
||||||
|
if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_HOST))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc));
|
||||||
|
if (!bridge)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rc = pci_host_bridge_priv(bridge);
|
||||||
|
rc->pcie.dev = dev;
|
||||||
|
cdns_plat_pcie->pcie = &rc->pcie;
|
||||||
|
cdns_plat_pcie->is_rc = is_rc;
|
||||||
|
|
||||||
|
ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to init phy\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
ret = pm_runtime_get_sync(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "pm_runtime_get_sync() failed\n");
|
||||||
|
goto err_get_sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cdns_pcie_host_setup(rc);
|
||||||
|
if (ret)
|
||||||
|
goto err_init;
|
||||||
|
} else {
|
||||||
|
if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_EP))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
|
||||||
|
if (!ep)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ep->pcie.dev = dev;
|
||||||
|
cdns_plat_pcie->pcie = &ep->pcie;
|
||||||
|
cdns_plat_pcie->is_rc = is_rc;
|
||||||
|
|
||||||
|
ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to init phy\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
ret = pm_runtime_get_sync(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "pm_runtime_get_sync() failed\n");
|
||||||
|
goto err_get_sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cdns_pcie_ep_setup(ep);
|
||||||
|
if (ret)
|
||||||
|
goto err_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
err_init:
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
|
|
||||||
|
err_get_sync:
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
cdns_pcie_disable_phy(cdns_plat_pcie->pcie);
|
||||||
|
phy_count = cdns_plat_pcie->pcie->phy_count;
|
||||||
|
while (phy_count--)
|
||||||
|
device_link_del(cdns_plat_pcie->pcie->link[phy_count]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cdns_plat_pcie_shutdown(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct cdns_pcie *pcie = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_put_sync(dev);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_dbg(dev, "pm_runtime_put_sync failed\n");
|
||||||
|
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
|
||||||
|
cdns_pcie_disable_phy(pcie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct cdns_plat_pcie_of_data cdns_plat_pcie_host_of_data = {
|
||||||
|
.is_rc = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct cdns_plat_pcie_of_data cdns_plat_pcie_ep_of_data = {
|
||||||
|
.is_rc = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id cdns_plat_pcie_of_match[] = {
|
||||||
|
{
|
||||||
|
.compatible = "cdns,cdns-pcie-host",
|
||||||
|
.data = &cdns_plat_pcie_host_of_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "cdns,cdns-pcie-ep",
|
||||||
|
.data = &cdns_plat_pcie_ep_of_data,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver cdns_plat_pcie_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "cdns-pcie",
|
||||||
|
.of_match_table = cdns_plat_pcie_of_match,
|
||||||
|
.pm = &cdns_pcie_pm_ops,
|
||||||
|
},
|
||||||
|
.probe = cdns_plat_pcie_probe,
|
||||||
|
.shutdown = cdns_plat_pcie_shutdown,
|
||||||
|
};
|
||||||
|
builtin_platform_driver(cdns_plat_pcie_driver);
|
|
@ -190,6 +190,8 @@ enum cdns_pcie_rp_bar {
|
||||||
(((code) << 8) & CDNS_PCIE_NORMAL_MSG_CODE_MASK)
|
(((code) << 8) & CDNS_PCIE_NORMAL_MSG_CODE_MASK)
|
||||||
#define CDNS_PCIE_MSG_NO_DATA BIT(16)
|
#define CDNS_PCIE_MSG_NO_DATA BIT(16)
|
||||||
|
|
||||||
|
struct cdns_pcie;
|
||||||
|
|
||||||
enum cdns_pcie_msg_code {
|
enum cdns_pcie_msg_code {
|
||||||
MSG_CODE_ASSERT_INTA = 0x20,
|
MSG_CODE_ASSERT_INTA = 0x20,
|
||||||
MSG_CODE_ASSERT_INTB = 0x21,
|
MSG_CODE_ASSERT_INTB = 0x21,
|
||||||
|
@ -231,13 +233,71 @@ enum cdns_pcie_msg_routing {
|
||||||
struct cdns_pcie {
|
struct cdns_pcie {
|
||||||
void __iomem *reg_base;
|
void __iomem *reg_base;
|
||||||
struct resource *mem_res;
|
struct resource *mem_res;
|
||||||
|
struct device *dev;
|
||||||
bool is_rc;
|
bool is_rc;
|
||||||
u8 bus;
|
u8 bus;
|
||||||
int phy_count;
|
int phy_count;
|
||||||
struct phy **phy;
|
struct phy **phy;
|
||||||
struct device_link **link;
|
struct device_link **link;
|
||||||
|
const struct cdns_pcie_common_ops *ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct cdns_pcie_rc - private data for this PCIe Root Complex driver
|
||||||
|
* @pcie: Cadence PCIe controller
|
||||||
|
* @dev: pointer to PCIe device
|
||||||
|
* @cfg_res: start/end offsets in the physical system memory to map PCI
|
||||||
|
* configuration space accesses
|
||||||
|
* @bus_range: first/last buses behind the PCIe host controller
|
||||||
|
* @cfg_base: IO mapped window to access the PCI configuration space of a
|
||||||
|
* single function at a time
|
||||||
|
* @max_regions: maximum number of regions supported by the hardware
|
||||||
|
* @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address
|
||||||
|
* translation (nbits sets into the "no BAR match" register)
|
||||||
|
* @vendor_id: PCI vendor ID
|
||||||
|
* @device_id: PCI device ID
|
||||||
|
*/
|
||||||
|
struct cdns_pcie_rc {
|
||||||
|
struct cdns_pcie pcie;
|
||||||
|
struct resource *cfg_res;
|
||||||
|
struct resource *bus_range;
|
||||||
|
void __iomem *cfg_base;
|
||||||
|
u32 max_regions;
|
||||||
|
u32 no_bar_nbits;
|
||||||
|
u16 vendor_id;
|
||||||
|
u16 device_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct cdns_pcie_ep - private data for this PCIe endpoint controller driver
|
||||||
|
* @pcie: Cadence PCIe controller
|
||||||
|
* @max_regions: maximum number of regions supported by hardware
|
||||||
|
* @ob_region_map: bitmask of mapped outbound regions
|
||||||
|
* @ob_addr: base addresses in the AXI bus where the outbound regions start
|
||||||
|
* @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ
|
||||||
|
* dedicated outbound regions is mapped.
|
||||||
|
* @irq_cpu_addr: base address in the CPU space where a write access triggers
|
||||||
|
* the sending of a memory write (MSI) / normal message (legacy
|
||||||
|
* IRQ) TLP through the PCIe bus.
|
||||||
|
* @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ
|
||||||
|
* dedicated outbound region.
|
||||||
|
* @irq_pci_fn: the latest PCI function that has updated the mapping of
|
||||||
|
* the MSI/legacy IRQ dedicated outbound region.
|
||||||
|
* @irq_pending: bitmask of asserted legacy IRQs.
|
||||||
|
*/
|
||||||
|
struct cdns_pcie_ep {
|
||||||
|
struct cdns_pcie pcie;
|
||||||
|
u32 max_regions;
|
||||||
|
unsigned long ob_region_map;
|
||||||
|
phys_addr_t *ob_addr;
|
||||||
|
phys_addr_t irq_phys_addr;
|
||||||
|
void __iomem *irq_cpu_addr;
|
||||||
|
u64 irq_pci_addr;
|
||||||
|
u8 irq_pci_fn;
|
||||||
|
u8 irq_pending;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Register access */
|
/* Register access */
|
||||||
static inline void cdns_pcie_writeb(struct cdns_pcie *pcie, u32 reg, u8 value)
|
static inline void cdns_pcie_writeb(struct cdns_pcie *pcie, u32 reg, u8 value)
|
||||||
{
|
{
|
||||||
|
@ -306,6 +366,23 @@ static inline u32 cdns_pcie_ep_fn_readl(struct cdns_pcie *pcie, u8 fn, u32 reg)
|
||||||
return readl(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg);
|
return readl(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCIE_CADENCE_HOST
|
||||||
|
int cdns_pcie_host_setup(struct cdns_pcie_rc *rc);
|
||||||
|
#else
|
||||||
|
static inline int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCIE_CADENCE_EP
|
||||||
|
int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep);
|
||||||
|
#else
|
||||||
|
static inline int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn,
|
void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn,
|
||||||
u32 r, bool is_io,
|
u32 r, bool is_io,
|
||||||
u64 cpu_addr, u64 pci_addr, size_t size);
|
u64 cpu_addr, u64 pci_addr, size_t size);
|
||||||
|
|
Loading…
Add table
Reference in a new issue