162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Cadence PCIe platform driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2019, Cadence Design Systems 662306a36Sopenharmony_ci * Author: Tom Joseph <tjoseph@cadence.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/of_pci.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1362306a36Sopenharmony_ci#include "pcie-cadence.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define CDNS_PLAT_CPU_TO_BUS_ADDR 0x0FFFFFFF 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * struct cdns_plat_pcie - private data for this PCIe platform driver 1962306a36Sopenharmony_ci * @pcie: Cadence PCIe controller 2062306a36Sopenharmony_ci * @is_rc: Set to 1 indicates the PCIe controller mode is Root Complex, 2162306a36Sopenharmony_ci * if 0 it is in Endpoint mode. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistruct cdns_plat_pcie { 2462306a36Sopenharmony_ci struct cdns_pcie *pcie; 2562306a36Sopenharmony_ci bool is_rc; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct cdns_plat_pcie_of_data { 2962306a36Sopenharmony_ci bool is_rc; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic const struct of_device_id cdns_plat_pcie_of_match[]; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie *pcie, u64 cpu_addr) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return cpu_addr & CDNS_PLAT_CPU_TO_BUS_ADDR; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic const struct cdns_pcie_ops cdns_plat_ops = { 4062306a36Sopenharmony_ci .cpu_addr_fixup = cdns_plat_cpu_addr_fixup, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int cdns_plat_pcie_probe(struct platform_device *pdev) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci const struct cdns_plat_pcie_of_data *data; 4662306a36Sopenharmony_ci struct cdns_plat_pcie *cdns_plat_pcie; 4762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 4862306a36Sopenharmony_ci struct pci_host_bridge *bridge; 4962306a36Sopenharmony_ci struct cdns_pcie_ep *ep; 5062306a36Sopenharmony_ci struct cdns_pcie_rc *rc; 5162306a36Sopenharmony_ci int phy_count; 5262306a36Sopenharmony_ci bool is_rc; 5362306a36Sopenharmony_ci int ret; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci data = of_device_get_match_data(dev); 5662306a36Sopenharmony_ci if (!data) 5762306a36Sopenharmony_ci return -EINVAL; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci is_rc = data->is_rc; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci pr_debug(" Started %s with is_rc: %d\n", __func__, is_rc); 6262306a36Sopenharmony_ci cdns_plat_pcie = devm_kzalloc(dev, sizeof(*cdns_plat_pcie), GFP_KERNEL); 6362306a36Sopenharmony_ci if (!cdns_plat_pcie) 6462306a36Sopenharmony_ci return -ENOMEM; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci platform_set_drvdata(pdev, cdns_plat_pcie); 6762306a36Sopenharmony_ci if (is_rc) { 6862306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_HOST)) 6962306a36Sopenharmony_ci return -ENODEV; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); 7262306a36Sopenharmony_ci if (!bridge) 7362306a36Sopenharmony_ci return -ENOMEM; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci rc = pci_host_bridge_priv(bridge); 7662306a36Sopenharmony_ci rc->pcie.dev = dev; 7762306a36Sopenharmony_ci rc->pcie.ops = &cdns_plat_ops; 7862306a36Sopenharmony_ci cdns_plat_pcie->pcie = &rc->pcie; 7962306a36Sopenharmony_ci cdns_plat_pcie->is_rc = is_rc; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie); 8262306a36Sopenharmony_ci if (ret) { 8362306a36Sopenharmony_ci dev_err(dev, "failed to init phy\n"); 8462306a36Sopenharmony_ci return ret; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci pm_runtime_enable(dev); 8762306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev); 8862306a36Sopenharmony_ci if (ret < 0) { 8962306a36Sopenharmony_ci dev_err(dev, "pm_runtime_get_sync() failed\n"); 9062306a36Sopenharmony_ci goto err_get_sync; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ret = cdns_pcie_host_setup(rc); 9462306a36Sopenharmony_ci if (ret) 9562306a36Sopenharmony_ci goto err_init; 9662306a36Sopenharmony_ci } else { 9762306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_EP)) 9862306a36Sopenharmony_ci return -ENODEV; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); 10162306a36Sopenharmony_ci if (!ep) 10262306a36Sopenharmony_ci return -ENOMEM; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ep->pcie.dev = dev; 10562306a36Sopenharmony_ci ep->pcie.ops = &cdns_plat_ops; 10662306a36Sopenharmony_ci cdns_plat_pcie->pcie = &ep->pcie; 10762306a36Sopenharmony_ci cdns_plat_pcie->is_rc = is_rc; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie); 11062306a36Sopenharmony_ci if (ret) { 11162306a36Sopenharmony_ci dev_err(dev, "failed to init phy\n"); 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci pm_runtime_enable(dev); 11662306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev); 11762306a36Sopenharmony_ci if (ret < 0) { 11862306a36Sopenharmony_ci dev_err(dev, "pm_runtime_get_sync() failed\n"); 11962306a36Sopenharmony_ci goto err_get_sync; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ret = cdns_pcie_ep_setup(ep); 12362306a36Sopenharmony_ci if (ret) 12462306a36Sopenharmony_ci goto err_init; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci err_init: 13062306a36Sopenharmony_ci err_get_sync: 13162306a36Sopenharmony_ci pm_runtime_put_sync(dev); 13262306a36Sopenharmony_ci pm_runtime_disable(dev); 13362306a36Sopenharmony_ci cdns_pcie_disable_phy(cdns_plat_pcie->pcie); 13462306a36Sopenharmony_ci phy_count = cdns_plat_pcie->pcie->phy_count; 13562306a36Sopenharmony_ci while (phy_count--) 13662306a36Sopenharmony_ci device_link_del(cdns_plat_pcie->pcie->link[phy_count]); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void cdns_plat_pcie_shutdown(struct platform_device *pdev) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 14462306a36Sopenharmony_ci struct cdns_pcie *pcie = dev_get_drvdata(dev); 14562306a36Sopenharmony_ci int ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = pm_runtime_put_sync(dev); 14862306a36Sopenharmony_ci if (ret < 0) 14962306a36Sopenharmony_ci dev_dbg(dev, "pm_runtime_put_sync failed\n"); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci pm_runtime_disable(dev); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci cdns_pcie_disable_phy(pcie); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic const struct cdns_plat_pcie_of_data cdns_plat_pcie_host_of_data = { 15762306a36Sopenharmony_ci .is_rc = true, 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic const struct cdns_plat_pcie_of_data cdns_plat_pcie_ep_of_data = { 16162306a36Sopenharmony_ci .is_rc = false, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic const struct of_device_id cdns_plat_pcie_of_match[] = { 16562306a36Sopenharmony_ci { 16662306a36Sopenharmony_ci .compatible = "cdns,cdns-pcie-host", 16762306a36Sopenharmony_ci .data = &cdns_plat_pcie_host_of_data, 16862306a36Sopenharmony_ci }, 16962306a36Sopenharmony_ci { 17062306a36Sopenharmony_ci .compatible = "cdns,cdns-pcie-ep", 17162306a36Sopenharmony_ci .data = &cdns_plat_pcie_ep_of_data, 17262306a36Sopenharmony_ci }, 17362306a36Sopenharmony_ci {}, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct platform_driver cdns_plat_pcie_driver = { 17762306a36Sopenharmony_ci .driver = { 17862306a36Sopenharmony_ci .name = "cdns-pcie", 17962306a36Sopenharmony_ci .of_match_table = cdns_plat_pcie_of_match, 18062306a36Sopenharmony_ci .pm = &cdns_pcie_pm_ops, 18162306a36Sopenharmony_ci }, 18262306a36Sopenharmony_ci .probe = cdns_plat_pcie_probe, 18362306a36Sopenharmony_ci .shutdown = cdns_plat_pcie_shutdown, 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_cibuiltin_platform_driver(cdns_plat_pcie_driver); 186