From: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
To: "Lorenzo Pieralisi" <lpieralisi@kernel.org>,
"Krzysztof Wilczyński" <kw@linux.com>,
"Manivannan Sadhasivam" <manivannan.sadhasivam@linaro.org>,
"Rob Herring" <robh@kernel.org>,
"Bjorn Helgaas" <bhelgaas@google.com>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Bjorn Andersson" <andersson@kernel.org>,
"Konrad Dybcio" <konradybcio@kernel.org>,
cros-qcom-dts-watchers@chromium.org
Cc: linux-arm-msm@vger.kernel.org, linux-pci@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
quic_vbadigan@quicinc.com, quic_mrana@quicinc.com,
Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Subject: [PATCH 3/3] PCI: qcom: Add support for multi-root port
Date: Sat, 22 Mar 2025 08:30:45 +0530 [thread overview]
Message-ID: <20250322-perst-v1-3-e5e4da74a204@oss.qualcomm.com> (raw)
In-Reply-To: <20250322-perst-v1-0-e5e4da74a204@oss.qualcomm.com>
Move phy, perst handling to root port and provide a way to have multi-port
logic.
Currently, qcom controllers only support single port, and all properties
are present in the controller node itself. This is incorrect, as
properties like phy, perst, wake, etc. can vary per port and should be
present in the root port node.
pci-bus-common.yaml uses reset-gpios property for representing PERST, use
same property instead of perst-gpios.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/pci/controller/dwc/pcie-qcom.c | 149 +++++++++++++++++++++++++++------
1 file changed, 123 insertions(+), 26 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index e4d3366ead1f..6424dcfd3e1b 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -262,6 +262,11 @@ struct qcom_pcie_cfg {
bool no_l0s;
};
+struct qcom_pcie_port {
+ struct list_head list;
+ struct gpio_desc *reset;
+ struct phy *phy;
+};
struct qcom_pcie {
struct dw_pcie *pci;
void __iomem *parf; /* DT parf */
@@ -276,21 +281,36 @@ struct qcom_pcie {
struct dentry *debugfs;
bool suspended;
bool use_pm_opp;
+ struct list_head ports;
};
#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
{
- gpiod_set_value_cansleep(pcie->reset, 1);
+ struct qcom_pcie_port *port, *tmp;
+
+ if (list_empty(&pcie->ports))
+ gpiod_set_value_cansleep(pcie->reset, 1);
+ else
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+ gpiod_set_value_cansleep(port->reset, 1);
+
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
{
+ struct qcom_pcie_port *port, *tmp;
+
/* Ensure that PERST has been asserted for at least 100 ms */
msleep(100);
- gpiod_set_value_cansleep(pcie->reset, 0);
+ if (list_empty(&pcie->ports))
+ gpiod_set_value_cansleep(pcie->reset, 0);
+ else
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+ gpiod_set_value_cansleep(port->reset, 0);
+
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
@@ -1229,10 +1249,19 @@ static int qcom_pcie_link_up(struct dw_pcie *pci)
return !!(val & PCI_EXP_LNKSTA_DLLLA);
}
+static void qcom_pcie_port_phy_off(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_port *port, *tmp;
+
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+ phy_power_off(port->phy);
+}
+
static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct qcom_pcie *pcie = to_qcom_pcie(pci);
+ struct qcom_pcie_port *port, *tmp;
int ret;
qcom_ep_reset_assert(pcie);
@@ -1241,13 +1270,27 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
return ret;
- ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
- if (ret)
- goto err_deinit;
+ if (list_empty(&pcie->ports)) {
+ ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
+ if (ret)
+ goto err_deinit;
- ret = phy_power_on(pcie->phy);
- if (ret)
- goto err_deinit;
+ ret = phy_power_on(pcie->phy);
+ if (ret)
+ goto err_deinit;
+ } else {
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
+ if (ret)
+ goto err_deinit;
+
+ ret = phy_power_on(port->phy);
+ if (ret) {
+ qcom_pcie_port_phy_off(pcie);
+ goto err_deinit;
+ }
+ }
+ }
if (pcie->cfg->ops->post_init) {
ret = pcie->cfg->ops->post_init(pcie);
@@ -1268,7 +1311,10 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
err_assert_reset:
qcom_ep_reset_assert(pcie);
err_disable_phy:
- phy_power_off(pcie->phy);
+ if (list_empty(&pcie->ports))
+ phy_power_off(pcie->phy);
+ else
+ qcom_pcie_port_phy_off(pcie);
err_deinit:
pcie->cfg->ops->deinit(pcie);
@@ -1281,7 +1327,10 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)
struct qcom_pcie *pcie = to_qcom_pcie(pci);
qcom_ep_reset_assert(pcie);
- phy_power_off(pcie->phy);
+ if (list_empty(&pcie->ports))
+ phy_power_off(pcie->phy);
+ else
+ qcom_pcie_port_phy_off(pcie);
pcie->cfg->ops->deinit(pcie);
}
@@ -1579,11 +1628,41 @@ static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data)
return IRQ_HANDLED;
}
+static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node)
+{
+ struct device *dev = pcie->pci->dev;
+ struct qcom_pcie_port *port;
+ struct gpio_desc *reset;
+ struct phy *phy;
+
+ reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node),
+ "reset", GPIOD_OUT_HIGH, "PERST#");
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+
+ phy = devm_of_phy_get(dev, node, "pciephy");
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->reset = reset;
+ port->phy = phy;
+ INIT_LIST_HEAD(&port->list);
+ list_add_tail(&port->list, &pcie->ports);
+
+ return 0;
+}
+
static int qcom_pcie_probe(struct platform_device *pdev)
{
const struct qcom_pcie_cfg *pcie_cfg;
unsigned long max_freq = ULONG_MAX;
+ struct qcom_pcie_port *port, *tmp;
struct device *dev = &pdev->dev;
+ struct device_node *of_port;
struct dev_pm_opp *opp;
struct qcom_pcie *pcie;
struct dw_pcie_rp *pp;
@@ -1611,6 +1690,8 @@ static int qcom_pcie_probe(struct platform_device *pdev)
if (ret < 0)
goto err_pm_runtime_put;
+ INIT_LIST_HEAD(&pcie->ports);
+
pci->dev = dev;
pci->ops = &dw_pcie_ops;
pp = &pci->pp;
@@ -1619,12 +1700,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
pcie->cfg = pcie_cfg;
- pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
- if (IS_ERR(pcie->reset)) {
- ret = PTR_ERR(pcie->reset);
- goto err_pm_runtime_put;
- }
-
pcie->parf = devm_platform_ioremap_resource_byname(pdev, "parf");
if (IS_ERR(pcie->parf)) {
ret = PTR_ERR(pcie->parf);
@@ -1647,12 +1722,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
}
}
- pcie->phy = devm_phy_optional_get(dev, "pciephy");
- if (IS_ERR(pcie->phy)) {
- ret = PTR_ERR(pcie->phy);
- goto err_pm_runtime_put;
- }
-
/* OPP table is optional */
ret = devm_pm_opp_of_add_table(dev);
if (ret && ret != -ENODEV) {
@@ -1699,9 +1768,31 @@ static int qcom_pcie_probe(struct platform_device *pdev)
pp->ops = &qcom_pcie_dw_ops;
- ret = phy_init(pcie->phy);
- if (ret)
- goto err_pm_runtime_put;
+ for_each_child_of_node(dev->of_node, of_port) {
+ ret = qcom_pcie_parse_port(pcie, of_port);
+ of_node_put(of_port);
+ if (ret)
+ break;
+ }
+
+ /* Fallback to previous method */
+ if (ret) {
+ pcie->phy = devm_phy_optional_get(dev, "pciephy");
+ if (IS_ERR(pcie->phy)) {
+ ret = PTR_ERR(pcie->phy);
+ goto err_pm_runtime_put;
+ }
+
+ pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
+ if (IS_ERR(pcie->reset)) {
+ ret = PTR_ERR(pcie->reset);
+ goto err_pm_runtime_put;
+ }
+
+ ret = phy_init(pcie->phy);
+ if (ret)
+ goto err_pm_runtime_put;
+ }
platform_set_drvdata(pdev, pcie);
@@ -1746,10 +1837,16 @@ static int qcom_pcie_probe(struct platform_device *pdev)
err_host_deinit:
dw_pcie_host_deinit(pp);
err_phy_exit:
- phy_exit(pcie->phy);
+ if (list_empty(&pcie->ports))
+ phy_exit(pcie->phy);
+ else
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+ phy_exit(port->phy);
err_pm_runtime_put:
pm_runtime_put(dev);
pm_runtime_disable(dev);
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+ list_del(&port->list);
return ret;
}
--
2.34.1
prev parent reply other threads:[~2025-03-22 3:01 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-22 3:00 [PATCH 0/3] PCI: qcom: Move PERST# GPIO & phy retrieval from controller to PCIe bridge node Krishna Chaitanya Chundru
2025-03-22 3:00 ` [PATCH 1/3] dt-bindings: PCI: qcom: Move phy, wake & reset gpio's to root port Krishna Chaitanya Chundru
2025-03-24 16:39 ` Rob Herring
2025-03-25 4:41 ` Krishna Chaitanya Chundru
2025-04-01 8:23 ` Krishna Chaitanya Chundru
2025-03-22 3:00 ` [PATCH 2/3] arm64: qcom: sc7280: Move phy, perst to root port node Krishna Chaitanya Chundru
2025-03-24 16:41 ` Rob Herring
2025-03-24 19:15 ` Konrad Dybcio
2025-03-25 4:43 ` Krishna Chaitanya Chundru
2025-03-22 3:00 ` Krishna Chaitanya Chundru [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250322-perst-v1-3-e5e4da74a204@oss.qualcomm.com \
--to=krishna.chundru@oss.qualcomm.com \
--cc=andersson@kernel.org \
--cc=bhelgaas@google.com \
--cc=conor+dt@kernel.org \
--cc=cros-qcom-dts-watchers@chromium.org \
--cc=devicetree@vger.kernel.org \
--cc=konradybcio@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=kw@linux.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lpieralisi@kernel.org \
--cc=manivannan.sadhasivam@linaro.org \
--cc=quic_mrana@quicinc.com \
--cc=quic_vbadigan@quicinc.com \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox