From: Sreeraj S Kurup <sreekuttan2156239@gmail.com>
To: sreekuttan2156239@gmail.com
Cc: alim.akhtar@samsung.com, bhelgaas@google.com,
jingoohan1@gmail.com, krzk@kernel.org, kwilczynski@kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
linux-samsung-soc@vger.kernel.org, lpieralisi@kernel.org,
mani@kernel.org, peter.griffin@linaro.org, robh@kernel.org
Subject: [PATCH v7] PCI: exynos: Handle PCIe PHY initialization errors
Date: Sat, 31 Oct 2026 07:01:11 +0000 [thread overview]
Message-ID: <20261031070111.3130-1-sreekuttan2156239@gmail.com> (raw)
In-Reply-To: <20261029131006.2744-1-sreekuttan2156239@gmail.com>
Check the return values of phy_init() and phy_power_on() to avoid
continuing PCIe host initialization when PHY setup fails.
Track PHY initialization, PHY power, and regulator enable state
across probe, remove, suspend, and resume to ensure cleanup is
performed only when appropriate and to prevent unbalanced
reference counts.
Assert core reset on probe failure after PHY initialization has
been reached, and handle cleanup errors during teardown and
suspend.
Also check the return value of exynos_pcie_host_init() during
resume and abort the resume sequence if PHY initialization fails.
Signed-off-by: Sreeraj S Kurup <sreekuttan2156239@gmail.com>
---
v7:
- Guard PHY initialization and power-on against duplicate calls
- Guard regulator enable against duplicate calls during resume
- Preserve PHY and regulator state when cleanup operations fail
- Update commit message to describe regulator tracking and core
reset handling
v6:
- Prevent state/reference count desynchronization in exynos_pcie_phy_exit()
by ensuring both phy_power_off() and phy_exit() attempt cleanup even if one
fails
- Assert core reset on post-init probe failures in fail_probe if PHY/host
initialization was reached
v5:
- Avoid PM core state desynchronization in exynos_pcie_suspend_noirq()
by logging teardown errors and returning 0 instead of aborting suspend
on teardown failure
v4:
- Fix invalid return in void exynos_pcie_remove() and ensure regulator cleanup
- Add core reset deassertion on error in exynos_pcie_suspend_noirq()
- Prevent regulator leaks in exynos_pcie_resume_noirq() error recovery path
v3:
- Address review feedback on PHY and regulator state tracking
- Avoid powering down the controller when link detection fails
v2:
- Track PHY initialization and power state to prevent duplicate cleanup
- Handle host initialization failures during resume
drivers/pci/controller/dwc/pci-exynos.c | 131 +++++++++++++++++++++---
1 file changed, 117 insertions(+), 14 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..3e76a818a45c 100644
--- a/drivers/pci/controller/dwc/pci-exynos.c
+++ b/drivers/pci/controller/dwc/pci-exynos.c
@@ -11,6 +11,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -55,8 +56,35 @@ struct exynos_pcie {
struct clk_bulk_data *clks;
struct phy *phy;
struct regulator_bulk_data supplies[2];
+ bool phy_initialized;
+ bool phy_powered;
+ bool supplies_enabled;
};
+static int exynos_pcie_phy_exit(struct exynos_pcie *ep)
+{
+ int ret = 0;
+ int err;
+
+ if (ep->phy_powered) {
+ err = phy_power_off(ep->phy);
+ if (err)
+ ret = err;
+ else
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ err = phy_exit(ep->phy);
+ if (err)
+ ret = err;
+ else
+ ep->phy_initialized = false;
+ }
+
+ return ret;
+}
+
static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
{
writel(val, base + reg);
@@ -225,13 +253,27 @@ static int exynos_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct exynos_pcie *ep = to_exynos_pcie(pci);
+ int ret;
pp->bridge->ops = &exynos_pci_ops;
exynos_pcie_assert_core_reset(ep);
- phy_init(ep->phy);
- phy_power_on(ep->phy);
+ if (!ep->phy_initialized) {
+ ret = phy_init(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = true;
+ }
+
+ if (!ep->phy_powered) {
+ ret = phy_power_on(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_powered = true;
+ }
exynos_pcie_deassert_core_reset(ep);
exynos_pcie_enable_irq_pulse(ep);
@@ -314,6 +356,7 @@ static int exynos_pcie_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ep->supplies_enabled = true;
platform_set_drvdata(pdev, ep);
ret = exynos_add_pcie_port(ep, pdev);
@@ -323,8 +366,22 @@ static int exynos_pcie_probe(struct platform_device *pdev)
return 0;
fail_probe:
- phy_exit(ep->phy);
- regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (ep->phy_initialized || ep->phy_powered)
+ exynos_pcie_assert_core_reset(ep);
+
+ if (exynos_pcie_phy_exit(ep))
+ dev_err(&pdev->dev, "failed to exit PCIe PHY\n");
+
+ if (ep->supplies_enabled) {
+ int err;
+
+ err = regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (err)
+ dev_err(&pdev->dev, "failed to disable regulators: %d\n", err);
+ else
+ ep->supplies_enabled = false;
+
+ }
return ret;
}
@@ -332,22 +389,46 @@ static int exynos_pcie_probe(struct platform_device *pdev)
static void exynos_pcie_remove(struct platform_device *pdev)
{
struct exynos_pcie *ep = platform_get_drvdata(pdev);
+ int ret;
dw_pcie_host_deinit(&ep->pci.pp);
exynos_pcie_assert_core_reset(ep);
- phy_power_off(ep->phy);
- phy_exit(ep->phy);
- regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+
+ ret = exynos_pcie_phy_exit(ep);
+ if (ret)
+ dev_err(&pdev->dev, "failed to exit PCIe PHY: %d\n", ret);
+
+ if (ep->supplies_enabled) {
+ ret = regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (ret)
+ dev_err(&pdev->dev, "failed to disable regulators: %d\n", ret);
+ else
+ ep->supplies_enabled = false;
+ }
}
static int exynos_pcie_suspend_noirq(struct device *dev)
{
struct exynos_pcie *ep = dev_get_drvdata(dev);
+ int ret;
exynos_pcie_assert_core_reset(ep);
- phy_power_off(ep->phy);
- phy_exit(ep->phy);
- regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+
+ ret = exynos_pcie_phy_exit(ep);
+ if (ret) {
+ dev_err(dev, "failed to exit PCIe PHY during suspend: %d\n", ret);
+ return ret;
+ }
+
+ if (ep->supplies_enabled) {
+ ret = regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (ret) {
+ dev_err(dev, "failed to disable regulators during suspend: %d\n", ret);
+ return ret;
+ }
+
+ ep->supplies_enabled = false;
+ }
return 0;
}
@@ -358,13 +439,35 @@ static int exynos_pcie_resume_noirq(struct device *dev)
struct dw_pcie *pci = &ep->pci;
struct dw_pcie_rp *pp = &pci->pp;
int ret;
+ int err;
- ret = regulator_bulk_enable(ARRAY_SIZE(ep->supplies), ep->supplies);
- if (ret)
- return ret;
+ if (!ep->supplies_enabled) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (ret)
+ return ret;
+
+ ep->supplies_enabled = true;
+ }
/* exynos_pcie_host_init controls ep->phy */
- exynos_pcie_host_init(pp);
+ ret = exynos_pcie_host_init(pp);
+ if (ret) {
+ err = exynos_pcie_phy_exit(ep);
+ if (err)
+ dev_err(dev, "failed to exit PCIe PHY during resume error: %d\n", err);
+
+ if (ep->supplies_enabled) {
+ err = regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (err)
+ dev_err(dev, "failed to disable regulators: %d\n", err);
+ else
+ ep->supplies_enabled = false;
+ }
+
+ return ret;
+
+ }
+
dw_pcie_setup_rc(pp);
exynos_pcie_start_link(pci);
return dw_pcie_wait_for_link(pci);
--
2.55.0
next prev parent reply other threads:[~2026-10-31 7:01 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 13:10 [PATCH] PCI: exynos: Handle " Sreeraj S Kurup
2026-10-29 17:54 ` [PATCH v2] PCI: exynos: Handle PCIe " Sreeraj S Kurup
2026-10-30 15:17 ` [PATCH v3] " Sreeraj S Kurup
2026-10-30 16:36 ` [PATCH v4] " Sreeraj S Kurup
2026-10-30 17:23 ` [PATCH v5] " Sreeraj S Kurup
2026-10-30 18:09 ` [PATCH v6] " Sreeraj S Kurup
2026-10-31 7:01 ` Sreeraj S Kurup [this message]
2026-10-31 7:35 ` [PATCH v8] " Sreeraj S Kurup
2026-10-31 9:49 ` [PATCH v9] " Sreeraj S Kurup
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=20261031070111.3130-1-sreekuttan2156239@gmail.com \
--to=sreekuttan2156239@gmail.com \
--cc=alim.akhtar@samsung.com \
--cc=bhelgaas@google.com \
--cc=jingoohan1@gmail.com \
--cc=krzk@kernel.org \
--cc=kwilczynski@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-samsung-soc@vger.kernel.org \
--cc=lpieralisi@kernel.org \
--cc=mani@kernel.org \
--cc=peter.griffin@linaro.org \
--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
all inboxes | Powered by JetHome®