From: Manikandan K Pillai <mpillai@xxxxxxxxxxx> Add support for Cadence HPA PCIe controller based platform. Signed-off-by: Manikandan K Pillai <mpillai@xxxxxxxxxxx> Co-developed-by: Hans Zhang <hans.zhang@xxxxxxxxxxx> Signed-off-by: Hans Zhang <hans.zhang@xxxxxxxxxxx> --- drivers/pci/controller/cadence/Kconfig | 5 + drivers/pci/controller/cadence/Makefile | 1 + .../cadence/pcie-cadence-plat-hpa.c | 183 ++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 drivers/pci/controller/cadence/pcie-cadence-plat-hpa.c diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig index a1caf154888d..427aa9beca22 100644 --- a/drivers/pci/controller/cadence/Kconfig +++ b/drivers/pci/controller/cadence/Kconfig @@ -29,11 +29,15 @@ config PCIE_CADENCE_EP config PCIE_CADENCE_PLAT bool +config PCIE_CADENCE_PLAT_HPA + bool + config PCIE_CADENCE_PLAT_HOST bool "Cadence platform PCIe controller (host mode)" depends on OF select PCIE_CADENCE_HOST select PCIE_CADENCE_PLAT + select PCIE_CADENCE_PLAT_HPA help 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 @@ -45,6 +49,7 @@ config PCIE_CADENCE_PLAT_EP depends on PCI_ENDPOINT select PCIE_CADENCE_EP select PCIE_CADENCE_PLAT + select PCIE_CADENCE_PLAT_HPA 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 diff --git a/drivers/pci/controller/cadence/Makefile b/drivers/pci/controller/cadence/Makefile index e2df24ff4c33..f8575a0eee2d 100644 --- a/drivers/pci/controller/cadence/Makefile +++ b/drivers/pci/controller/cadence/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_PCIE_CADENCE_HOST_COMMON) += pcie-cadence-host-common.o obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o pcie-cadence-host-hpa.o obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o pcie-cadence-ep-hpa.o obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o +obj-$(CONFIG_PCIE_CADENCE_PLAT_HPA) += pcie-cadence-plat-hpa.o obj-$(CONFIG_PCI_J721E) += pci-j721e.o diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-plat-hpa.c new file mode 100644 index 000000000000..fb42547d47d2 --- /dev/null +++ b/drivers/pci/controller/cadence/pcie-cadence-plat-hpa.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence PCIe platform driver. + * + * Copyright (c) 2019, Cadence Design Systems + * Author: Manikandan K Pillai <mpillai@xxxxxxxxxxx> + */ +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_pci.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include "pcie-cadence.h" + +/** + * struct cdns_plat_pcie - private data for this PCIe platform driver + * @pcie: Cadence PCIe controller + */ +struct cdns_plat_pcie { + struct cdns_pcie *pcie; +}; + +static const struct cdns_pcie_ops cdns_plat_hpa_ops = { + .start_link = cdns_pcie_hpa_start_link, + .stop_link = cdns_pcie_hpa_stop_link, + .link_up = cdns_pcie_hpa_link_up, +}; + +static int cdns_plat_pcie_hpa_probe(struct platform_device *pdev) +{ + const struct cdns_plat_pcie_of_data *data; + struct cdns_plat_pcie *cdns_plat_pcie; + 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; + + data = of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + 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; + rc->pcie.ops = &cdns_plat_hpa_ops; + rc->pcie.is_rc = data->is_rc; + + /* + * Store the register bank offsets pointer + */ + rc->pcie.cdns_pcie_reg_offsets = data; + + cdns_plat_pcie->pcie = &rc->pcie; + + 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_hpa_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; + ep->pcie.ops = &cdns_plat_hpa_ops; + ep->pcie.is_rc = data->is_rc; + + /* + * Store the register bank offset pointer + */ + ep->pcie.cdns_pcie_reg_offsets = data; + + cdns_plat_pcie->pcie = &ep->pcie; + + 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_hpa_ep_setup(ep); + if (ret) + goto err_init; + } + + return 0; + + err_init: + err_get_sync: + pm_runtime_put_sync(dev); + 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_hpa_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_hpa_host_of_data = { + .is_rc = true, +}; + +static const struct cdns_plat_pcie_of_data cdns_plat_pcie_hpa_ep_of_data = { + .is_rc = false, +}; + +static const struct of_device_id cdns_plat_pcie_hpa_of_match[] = { + { + .compatible = "cdns,cdns-pcie-hpa-host", + .data = &cdns_plat_pcie_hpa_host_of_data, + }, + { + .compatible = "cdns,cdns-pcie-hpa-ep", + .data = &cdns_plat_pcie_hpa_ep_of_data, + }, + {}, +}; + +static struct platform_driver cdns_plat_pcie_hpa_driver = { + .driver = { + .name = "cdns-pcie-hpa", + .of_match_table = cdns_plat_pcie_hpa_of_match, + .pm = &cdns_pcie_pm_ops, + }, + .probe = cdns_plat_pcie_hpa_probe, + .shutdown = cdns_plat_pcie_hpa_shutdown, +}; +builtin_platform_driver(cdns_plat_pcie_hpa_driver); -- 2.49.0