summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQiang Yu <qiang.yu@oss.qualcomm.com>2026-05-08 02:54:19 -0700
committerManivannan Sadhasivam <mani@kernel.org>2026-05-26 20:10:46 +0530
commit1a23bcb452d95f099e530414504c0d99ee076b3f (patch)
tree2d90ea4d7ce019b68b2615508bdce957f5680881
parent2cc0e7454c7891345f92e96b2f812b808be7fbdb (diff)
downloadlinux-1a23bcb452d95f099e530414504c0d99ee076b3f.tar.gz
linux-1a23bcb452d95f099e530414504c0d99ee076b3f.zip
PCI: qcom: Handle mixed PERST#/PHY DT configuration
The driver currently supports two PERST# and PHY DT configurations. In one case, PHY and PERST# are described in the RC node. In the other case, they are described in the RP node. A mixed setup is not supported. One common example is PHY on the RP node while PERST# remains on the RC node. In that case the driver goes through the RP parse path, does not find PERST# on RP, and does not report an error because PERST# is optional. Probe can then succeed silently while PERST# is left uncontrolled, and PCIe endpoints fail to work later. This silent probe success makes debugging difficult. Handle this mixed case in the RP parse path by checking whether PERST# is present on RC and, if so, using the RC PERST# GPIO for RP ports while keeping RP parsing for PHY. Emit a warning to indicate mixed DT content so it can be fixed. This keeps mixed systems functional and makes the configuration issue visible instead of failing later at endpoint bring-up. Suggested-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com> Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com> [mani: folded the fix: https://lore.kernel.org/linux-pci/20260526-fix_perst_gpio_handling-v1-1-9170507bb4e9@oss.qualcomm.com] Signed-off-by: Manivannan Sadhasivam <mani@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> Link: https://patch.msgid.link/20260508-mix_perst_phy_dts-v1-1-9eff6ee9b51a@oss.qualcomm.com
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c23
1 files changed, 17 insertions, 6 deletions
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index bc5a31efc0d0..9173369cca87 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -289,6 +289,7 @@ struct qcom_pcie {
const struct qcom_pcie_cfg *cfg;
struct dentry *debugfs;
struct list_head ports;
+ struct gpio_desc *reset;
bool use_pm_opp;
};
@@ -1798,6 +1799,13 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
struct gpio_desc *reset;
int ret;
+ if (pcie->reset) {
+ dev_warn_once(dev,
+ "Reusing PERST# from Root Complex node. DT needs to be fixed!\n");
+ reset = pcie->reset;
+ goto skip_perst_parsing;
+ }
+
if (!of_find_property(np, "reset-gpios", NULL))
goto parse_child_node;
@@ -1816,6 +1824,7 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
return PTR_ERR(reset);
}
+skip_perst_parsing:
perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
if (!perst)
return -ENOMEM;
@@ -1873,6 +1882,13 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
struct device *dev = pcie->pci->dev;
int ret = -ENODEV;
+ if (of_find_property(dev->of_node, "perst-gpios", NULL)) {
+ pcie->reset = devm_gpiod_get_optional(dev, "perst",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(pcie->reset))
+ return PTR_ERR(pcie->reset);
+ }
+
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
continue;
@@ -1899,7 +1915,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
struct device *dev = pcie->pci->dev;
struct qcom_pcie_perst *perst;
struct qcom_pcie_port *port;
- struct gpio_desc *reset;
struct phy *phy;
int ret;
@@ -1907,10 +1922,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
if (IS_ERR(phy))
return PTR_ERR(phy);
- reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
- if (IS_ERR(reset))
- return PTR_ERR(reset);
-
ret = phy_init(phy);
if (ret)
return ret;
@@ -1927,7 +1938,7 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
- perst->desc = reset;
+ perst->desc = pcie->reset;
INIT_LIST_HEAD(&port->perst);
INIT_LIST_HEAD(&perst->list);
list_add_tail(&perst->list, &port->perst);