mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
PCI: qcom: Add support for Qualcomm SA8255p based PCIe Root Complex
Add functionality to enable resource management (like clocks, regulators, PHY) through firmware and enumerate ECAM compliant Root Complex on SA8255p SoC, where the PCIe Root Complex is firmware managed and configured into ECAM compliant mode. Signed-off-by: Mayank Rana <mayank.rana@oss.qualcomm.com> [mani: minor code cleanups and commit message rewording] Signed-off-by: Manivannan Sadhasivam <mani@kernel.org> [bhelgaas: add "ECAM" in comment] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Link: https://patch.msgid.link/20250616224259.3549811-5-mayank.rana@oss.qualcomm.com
This commit is contained in:
parent
d7c7c051e8
commit
7d944c0f14
|
|
@ -296,6 +296,7 @@ config PCIE_QCOM
|
|||
select PCIE_DW_HOST
|
||||
select CRC8
|
||||
select PCIE_QCOM_COMMON
|
||||
select PCI_HOST_COMMON
|
||||
help
|
||||
Say Y here to enable PCIe controller support on Qualcomm SoCs. The
|
||||
PCIe controller uses the DesignWare core plus Qualcomm-specific
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@
|
|||
#include <linux/limits.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
@ -34,6 +36,7 @@
|
|||
#include <linux/units.h>
|
||||
|
||||
#include "../../pci.h"
|
||||
#include "../pci-host-common.h"
|
||||
#include "pcie-designware.h"
|
||||
#include "pcie-qcom-common.h"
|
||||
|
||||
|
|
@ -255,10 +258,12 @@ struct qcom_pcie_ops {
|
|||
* @ops: qcom PCIe ops structure
|
||||
* @override_no_snoop: Override NO_SNOOP attribute in TLP to enable cache
|
||||
* snooping
|
||||
* @firmware_managed: Set if the Root Complex is firmware managed
|
||||
*/
|
||||
struct qcom_pcie_cfg {
|
||||
const struct qcom_pcie_ops *ops;
|
||||
bool override_no_snoop;
|
||||
bool firmware_managed;
|
||||
bool no_l0s;
|
||||
};
|
||||
|
||||
|
|
@ -1426,6 +1431,10 @@ static const struct qcom_pcie_cfg cfg_sc8280xp = {
|
|||
.no_l0s = true,
|
||||
};
|
||||
|
||||
static const struct qcom_pcie_cfg cfg_fw_managed = {
|
||||
.firmware_managed = true,
|
||||
};
|
||||
|
||||
static const struct dw_pcie_ops dw_pcie_ops = {
|
||||
.link_up = qcom_pcie_link_up,
|
||||
.start_link = qcom_pcie_start_link,
|
||||
|
|
@ -1579,6 +1588,49 @@ static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void qcom_pci_free_msi(void *ptr)
|
||||
{
|
||||
struct dw_pcie_rp *pp = (struct dw_pcie_rp *)ptr;
|
||||
|
||||
if (pp && pp->has_msi_ctrl)
|
||||
dw_pcie_free_msi(pp);
|
||||
}
|
||||
|
||||
static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg)
|
||||
{
|
||||
struct device *dev = cfg->parent;
|
||||
struct dw_pcie_rp *pp;
|
||||
struct dw_pcie *pci;
|
||||
int ret;
|
||||
|
||||
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
||||
if (!pci)
|
||||
return -ENOMEM;
|
||||
|
||||
pci->dev = dev;
|
||||
pp = &pci->pp;
|
||||
pci->dbi_base = cfg->win;
|
||||
pp->num_vectors = MSI_DEF_NUM_VECTORS;
|
||||
|
||||
ret = dw_pcie_msi_host_init(pp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pp->has_msi_ctrl = true;
|
||||
dw_pcie_msi_init(pp);
|
||||
|
||||
return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp);
|
||||
}
|
||||
|
||||
static const struct pci_ecam_ops pci_qcom_ecam_ops = {
|
||||
.init = qcom_pcie_ecam_host_init,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static int qcom_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct qcom_pcie_cfg *pcie_cfg;
|
||||
|
|
@ -1593,24 +1645,62 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
|||
char *name;
|
||||
|
||||
pcie_cfg = of_device_get_match_data(dev);
|
||||
if (!pcie_cfg || !pcie_cfg->ops) {
|
||||
dev_err(dev, "Invalid platform data\n");
|
||||
return -EINVAL;
|
||||
if (!pcie_cfg) {
|
||||
dev_err(dev, "No platform data\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
||||
if (!pci)
|
||||
return -ENOMEM;
|
||||
if (!pcie_cfg->firmware_managed && !pcie_cfg->ops) {
|
||||
dev_err(dev, "No platform ops\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto err_pm_runtime_put;
|
||||
|
||||
if (pcie_cfg->firmware_managed) {
|
||||
struct pci_host_bridge *bridge;
|
||||
struct pci_config_window *cfg;
|
||||
|
||||
bridge = devm_pci_alloc_host_bridge(dev, 0);
|
||||
if (!bridge) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pm_runtime_put;
|
||||
}
|
||||
|
||||
/* Parse and map our ECAM configuration space area */
|
||||
cfg = pci_host_common_ecam_create(dev, bridge,
|
||||
&pci_qcom_ecam_ops);
|
||||
if (IS_ERR(cfg)) {
|
||||
ret = PTR_ERR(cfg);
|
||||
goto err_pm_runtime_put;
|
||||
}
|
||||
|
||||
bridge->sysdata = cfg;
|
||||
bridge->ops = (struct pci_ops *)&pci_qcom_ecam_ops.pci_ops;
|
||||
bridge->msi_domain = true;
|
||||
|
||||
ret = pci_host_probe(bridge);
|
||||
if (ret)
|
||||
goto err_pm_runtime_put;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pm_runtime_put;
|
||||
}
|
||||
|
||||
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
||||
if (!pci) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pm_runtime_put;
|
||||
}
|
||||
|
||||
pci->dev = dev;
|
||||
pci->ops = &dw_pcie_ops;
|
||||
pp = &pci->pp;
|
||||
|
|
@ -1756,9 +1846,13 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
static int qcom_pcie_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct qcom_pcie *pcie = dev_get_drvdata(dev);
|
||||
struct qcom_pcie *pcie;
|
||||
int ret = 0;
|
||||
|
||||
pcie = dev_get_drvdata(dev);
|
||||
if (!pcie)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Set minimum bandwidth required to keep data path functional during
|
||||
* suspend.
|
||||
|
|
@ -1812,9 +1906,13 @@ static int qcom_pcie_suspend_noirq(struct device *dev)
|
|||
|
||||
static int qcom_pcie_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct qcom_pcie *pcie = dev_get_drvdata(dev);
|
||||
struct qcom_pcie *pcie;
|
||||
int ret;
|
||||
|
||||
pcie = dev_get_drvdata(dev);
|
||||
if (!pcie)
|
||||
return 0;
|
||||
|
||||
if (pm_suspend_target_state != PM_SUSPEND_MEM) {
|
||||
ret = icc_enable(pcie->icc_cpu);
|
||||
if (ret) {
|
||||
|
|
@ -1849,6 +1947,7 @@ static const struct of_device_id qcom_pcie_match[] = {
|
|||
{ .compatible = "qcom,pcie-ipq9574", .data = &cfg_2_9_0 },
|
||||
{ .compatible = "qcom,pcie-msm8996", .data = &cfg_2_3_2 },
|
||||
{ .compatible = "qcom,pcie-qcs404", .data = &cfg_2_4_0 },
|
||||
{ .compatible = "qcom,pcie-sa8255p", .data = &cfg_fw_managed },
|
||||
{ .compatible = "qcom,pcie-sa8540p", .data = &cfg_sc8280xp },
|
||||
{ .compatible = "qcom,pcie-sa8775p", .data = &cfg_1_34_0},
|
||||
{ .compatible = "qcom,pcie-sc7280", .data = &cfg_1_9_0 },
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user