From: Qiang Yu Date: Fri, 8 May 2026 09:54:19 +0000 (-0700) Subject: PCI: qcom: Handle mixed PERST#/PHY DT configuration X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1a23bcb452d95f099e530414504c0d99ee076b3f;p=thirdparty%2Flinux.git 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 Signed-off-by: Qiang Yu [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 Signed-off-by: Bjorn Helgaas Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260508-mix_perst_phy_dts-v1-1-9eff6ee9b51a@oss.qualcomm.com --- 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);