* [PATCH] PCI: exynos: Handle PHY initialization errors
@ 2026-10-29 13:10 Sreeraj S Kurup
2026-10-29 17:54 ` [PATCH v2] PCI: exynos: Handle PCIe " Sreeraj S Kurup
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-29 13:10 UTC (permalink / raw)
To: linux-pci
Cc: jingoohan1, lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk,
peter.griffin, alim.akhtar, linux-arm-kernel, linux-samsung-soc,
linux-kernel, Sreeraj S Kurup
Check the return values of phy_init() and phy_power_on() to
avoid continuing PCIe host initialization when PHY setup fails.
If phy_power_on() fails, call phy_exit() to release the PHY
resources initialized by phy_init() before returning the error.
Signed-off-by: Sreeraj S Kurup <sreekuttan2156239@gmail.com>
---
drivers/pci/controller/dwc/pci-exynos.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..5ad07c0be8e5 100644
--- a/drivers/pci/controller/dwc/pci-exynos.c
+++ b/drivers/pci/controller/dwc/pci-exynos.c
@@ -225,13 +225,21 @@ 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);
+ ret = phy_init(ep->phy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(ep->phy);
+ if (ret) {
+ phy_exit(ep->phy);
+ return ret;
+ }
exynos_pcie_deassert_core_reset(ep);
exynos_pcie_enable_irq_pulse(ep);
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] PCI: exynos: Handle PCIe PHY initialization errors
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors Sreeraj S Kurup
@ 2026-10-29 17:54 ` Sreeraj S Kurup
2026-10-30 15:17 ` [PATCH v3] " Sreeraj S Kurup
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-29 17:54 UTC (permalink / raw)
To: sreekuttan2156239
Cc: alim.akhtar, bhelgaas, jingoohan1, krzk, kwilczynski,
linux-arm-kernel, linux-kernel, linux-pci, linux-samsung-soc,
lpieralisi, mani, peter.griffin, robh
Check the return values of "phy_init()" and "phy_power_on()" to
avoid continuing PCIe host initialization when PHY setup fails.
Track the PHY initialization and power state to ensure cleanup is
performed only when appropriate, avoiding double calls to
"phy_exit()" on probe failure.
Also check the return value of "exynos_pcie_host_init()" during
resume and abort the resume sequence if initialization fails,
disabling the regulators before returning the error.
Signed-off-by: Sreeraj S Kurup <sreekuttan2156239@gmail.com>
---
v2:
- Track PHY initialization and power state to prevent duplicate cleanup.
- Handle host initialization failures during resume.
drivers/pci/controller/dwc/pci-exynos.c | 77 ++++++++++++++++++++++---
1 file changed, 68 insertions(+), 9 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..f6b87c26ab36 100644
--- a/drivers/pci/controller/dwc/pci-exynos.c
+++ b/drivers/pci/controller/dwc/pci-exynos.c
@@ -55,6 +55,8 @@ struct exynos_pcie {
struct clk_bulk_data *clks;
struct phy *phy;
struct regulator_bulk_data supplies[2];
+ bool phy_initialized;
+ bool phy_powered;
};
static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
@@ -225,13 +227,23 @@ 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);
+ ret = phy_init(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = true;
+
+ 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);
@@ -323,7 +335,16 @@ static int exynos_pcie_probe(struct platform_device *pdev)
return 0;
fail_probe:
- phy_exit(ep->phy);
+ if (ep->phy_powered) {
+ phy_power_off(ep->phy);
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ phy_exit(ep->phy);
+ ep->phy_initialized = false;
+ }
+
regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
return ret;
@@ -335,8 +356,17 @@ static void exynos_pcie_remove(struct platform_device *pdev)
dw_pcie_host_deinit(&ep->pci.pp);
exynos_pcie_assert_core_reset(ep);
- phy_power_off(ep->phy);
- phy_exit(ep->phy);
+
+ if (ep->phy_powered) {
+ phy_power_off(ep->phy);
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ phy_exit(ep->phy);
+ ep->phy_initialized = false;
+ }
+
regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
}
@@ -345,8 +375,17 @@ static int exynos_pcie_suspend_noirq(struct device *dev)
struct exynos_pcie *ep = dev_get_drvdata(dev);
exynos_pcie_assert_core_reset(ep);
- phy_power_off(ep->phy);
- phy_exit(ep->phy);
+
+ if (ep->phy_powered) {
+ phy_power_off(ep->phy);
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ phy_exit(ep->phy);
+ ep->phy_initialized = false;
+ }
+
regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
return 0;
@@ -364,10 +403,30 @@ static int exynos_pcie_resume_noirq(struct device *dev)
return ret;
/* exynos_pcie_host_init controls ep->phy */
- exynos_pcie_host_init(pp);
+ ret = exynos_pcie_host_init(pp);
+ if (ret)
+ goto err_cleanup;
+
dw_pcie_setup_rc(pp);
exynos_pcie_start_link(pci);
- return dw_pcie_wait_for_link(pci);
+ ret = dw_pcie_wait_for_link(pci);
+ if (ret)
+ goto err_cleanup;
+
+ return 0;
+
+err_cleanup:
+ if (ep->phy_powered) {
+ phy_power_off(ep->phy);
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ phy_exit(ep->phy);
+ ep->phy_initialized = false;
+ }
+ regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ return ret;
}
static const struct dev_pm_ops exynos_pcie_pm_ops = {
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3] PCI: exynos: Handle PCIe PHY initialization errors
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors Sreeraj S Kurup
2026-10-29 17:54 ` [PATCH v2] PCI: exynos: Handle PCIe " Sreeraj S Kurup
@ 2026-10-30 15:17 ` Sreeraj S Kurup
2026-10-30 16:36 ` [PATCH v4] " Sreeraj S Kurup
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-30 15:17 UTC (permalink / raw)
To: sreekuttan2156239
Cc: alim.akhtar, bhelgaas, jingoohan1, krzk, kwilczynski,
linux-arm-kernel, linux-kernel, linux-pci, linux-samsung-soc,
lpieralisi, mani, peter.griffin, robh
Check the return values of "phy_init()" and "phy_power_on()" to
avoid continuing PCIe host initialization when PHY setup fails.
Track the PHY initialization and power state to ensure cleanup is
performed only when appropriate, avoiding double calls to
"phy_exit()" on probe failure.
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>
---
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 | 111 +++++++++++++++++++++---
1 file changed, 100 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..d7504b0e5d96 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,34 @@ 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;
+
+ if (ep->phy_powered) {
+ ret = phy_power_off(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ ret = phy_exit(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = false;
+ }
+
+ return 0;
+}
+
static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
{
writel(val, base + reg);
@@ -225,13 +252,23 @@ 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);
+ ret = phy_init(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = true;
+
+ 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 +351,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 +361,19 @@ 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 (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 +381,42 @@ 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)
+ return 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)
+ return ret;
+
+ if (ep->supplies_enabled) {
+ ret = regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (ret)
+ return ret;
+
+ ep->supplies_enabled = false;
+ }
return 0;
}
@@ -358,13 +427,33 @@ 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;
+ 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)
+ return err;
+
+ if (ep->supplies_enabled) {
+ err = regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (err)
+ return err;
+
+ 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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4] PCI: exynos: Handle PCIe PHY initialization errors
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors 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 ` Sreeraj S Kurup
2026-10-30 17:23 ` [PATCH v5] " Sreeraj S Kurup
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-30 16:36 UTC (permalink / raw)
To: sreekuttan2156239
Cc: alim.akhtar, bhelgaas, jingoohan1, krzk, kwilczynski,
linux-arm-kernel, linux-kernel, linux-pci, linux-samsung-soc,
lpieralisi, mani, peter.griffin, robh
Check the return values of "phy_init()" and "phy_power_on()" to
avoid continuing PCIe host initialization when PHY setup fails.
Track the PHY initialization and power state to ensure cleanup is
performed only when appropriate, avoiding double calls to
"phy_exit()" on probe failure.
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>
---
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 | 115 +++++++++++++++++++++---
1 file changed, 104 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..5b8d8f1ce8dc 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,34 @@ 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;
+
+ if (ep->phy_powered) {
+ ret = phy_power_off(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ ret = phy_exit(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = false;
+ }
+
+ return 0;
+}
+
static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
{
writel(val, base + reg);
@@ -225,13 +252,23 @@ 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);
+ ret = phy_init(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = true;
+
+ 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 +351,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 +361,19 @@ 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 (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,24 +381,48 @@ 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)
+ goto err_deassert_reset;
+
+ if (ep->supplies_enabled) {
+ ret = regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies);
+ if (ret)
+ goto err_deassert_reset;
+
+ ep->supplies_enabled = false;
+ }
return 0;
+
+err_deassert_reset:
+ exynos_pcie_deassert_core_reset(ep);
+ return ret;
}
static int exynos_pcie_resume_noirq(struct device *dev)
@@ -358,13 +431,33 @@ 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;
+ 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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v5] PCI: exynos: Handle PCIe PHY initialization errors
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors Sreeraj S Kurup
` (2 preceding siblings ...)
2026-10-30 16:36 ` [PATCH v4] " Sreeraj S Kurup
@ 2026-10-30 17:23 ` Sreeraj S Kurup
2026-10-30 18:09 ` [PATCH v6] " Sreeraj S Kurup
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-30 17:23 UTC (permalink / raw)
To: sreekuttan2156239
Cc: alim.akhtar, bhelgaas, jingoohan1, krzk, kwilczynski,
linux-arm-kernel, linux-kernel, linux-pci, linux-samsung-soc,
lpieralisi, mani, peter.griffin, robh
Check the return values of "phy_init()" and "phy_power_on()" to
avoid continuing PCIe host initialization when PHY setup fails.
Track the PHY initialization and power state to ensure cleanup is
performed only when appropriate, avoiding double calls to
"phy_exit()" on probe failure.
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>
---
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 | 111 +++++++++++++++++++++---
1 file changed, 100 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..d82bdfea1f17 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,34 @@ 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;
+
+ if (ep->phy_powered) {
+ ret = phy_power_off(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ ret = phy_exit(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = false;
+ }
+
+ return 0;
+}
+
static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
{
writel(val, base + reg);
@@ -225,13 +252,23 @@ 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);
+ ret = phy_init(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = true;
+
+ 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 +351,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 +361,19 @@ 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 (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 +381,42 @@ 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);
+
+ 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);
+ else
+ ep->supplies_enabled = false;
+ }
return 0;
}
@@ -358,13 +427,33 @@ 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;
+ 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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v6] PCI: exynos: Handle PCIe PHY initialization errors
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors Sreeraj S Kurup
` (3 preceding siblings ...)
2026-10-30 17:23 ` [PATCH v5] " Sreeraj S Kurup
@ 2026-10-30 18:09 ` Sreeraj S Kurup
2026-10-31 7:01 ` [PATCH v7] " Sreeraj S Kurup
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-30 18:09 UTC (permalink / raw)
To: sreekuttan2156239
Cc: alim.akhtar, bhelgaas, jingoohan1, krzk, kwilczynski,
linux-arm-kernel, linux-kernel, linux-pci, linux-samsung-soc,
lpieralisi, mani, peter.griffin, robh
Check the return values of "phy_init()" and "phy_power_on()" to
avoid continuing PCIe host initialization when PHY setup fails.
Track the PHY initialization and power state to ensure cleanup is
performed only when appropriate, avoiding double calls to
"phy_exit()" on probe failure.
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>
---
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 | 115 +++++++++++++++++++++---
1 file changed, 104 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..0085c3ed0c2f 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,23 @@ 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);
+ ret = phy_init(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = true;
+
+ 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 +352,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 +362,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 +385,42 @@ 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);
+
+ 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);
+ else
+ ep->supplies_enabled = false;
+ }
return 0;
}
@@ -358,13 +431,33 @@ 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;
+ 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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v7] PCI: exynos: Handle PCIe PHY initialization errors
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors Sreeraj S Kurup
` (4 preceding siblings ...)
2026-10-30 18:09 ` [PATCH v6] " Sreeraj S Kurup
@ 2026-10-31 7:01 ` Sreeraj S Kurup
2026-10-31 7:35 ` [PATCH v8] " Sreeraj S Kurup
2026-10-31 9:49 ` [PATCH v9] " Sreeraj S Kurup
7 siblings, 0 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-31 7:01 UTC (permalink / raw)
To: sreekuttan2156239
Cc: alim.akhtar, bhelgaas, jingoohan1, krzk, kwilczynski,
linux-arm-kernel, linux-kernel, linux-pci, linux-samsung-soc,
lpieralisi, mani, peter.griffin, robh
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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v8] PCI: exynos: Handle PCIe PHY initialization errors
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors Sreeraj S Kurup
` (5 preceding siblings ...)
2026-10-31 7:01 ` [PATCH v7] " Sreeraj S Kurup
@ 2026-10-31 7:35 ` Sreeraj S Kurup
2026-10-31 9:49 ` [PATCH v9] " Sreeraj S Kurup
7 siblings, 0 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-31 7:35 UTC (permalink / raw)
To: sreekuttan2156239
Cc: alim.akhtar, bhelgaas, jingoohan1, krzk, kwilczynski,
linux-arm-kernel, linux-kernel, linux-pci, linux-samsung-soc,
lpieralisi, mani, peter.griffin, robh
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>
---
v8:
- Fix state desynchronization in exynos_pcie_phy_exit() by returning
early if phy_power_off() fails, preventing calls to phy_exit() on a
still-powered PHY
- Prevent PM core desynchronization and orphaned controller states
during suspend-abort sequences in exynos_pcie_suspend_noirq() by
logging teardown errors and returning 0 instead of aborting system
suspend
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 | 126 +++++++++++++++++++++---
1 file changed, 112 insertions(+), 14 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..45163b1dad81 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,34 @@ 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;
+
+ if (ep->phy_powered) {
+ ret = phy_power_off(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ ret = phy_exit(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = false;
+ }
+
+ return 0;
+}
+
static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
{
writel(val, base + reg);
@@ -225,13 +252,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 +355,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 +365,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 +388,42 @@ 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);
+
+ 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);
+
+ ep->supplies_enabled = false;
+ }
return 0;
}
@@ -358,13 +434,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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v9] PCI: exynos: Handle PCIe PHY initialization errors
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors Sreeraj S Kurup
` (6 preceding siblings ...)
2026-10-31 7:35 ` [PATCH v8] " Sreeraj S Kurup
@ 2026-10-31 9:49 ` Sreeraj S Kurup
7 siblings, 0 replies; 9+ messages in thread
From: Sreeraj S Kurup @ 2026-10-31 9:49 UTC (permalink / raw)
To: sreekuttan2156239
Cc: alim.akhtar, bhelgaas, jingoohan1, krzk, kwilczynski,
linux-arm-kernel, linux-kernel, linux-pci, linux-samsung-soc,
lpieralisi, mani, peter.griffin, robh
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>
---
v9:
- Fix regulator state tracking in exynos_pcie_suspend_noirq() by updating
ep->supplies_enabled only when regulator_bulk_disable() succeeds, preventing
reference count leaks on failure.
v8:
- Fix state desynchronization in exynos_pcie_phy_exit() by returning
early if phy_power_off() fails, preventing calls to phy_exit() on a
still-powered PHY
- Prevent PM core desynchronization and orphaned controller states
during suspend-abort sequences in exynos_pcie_suspend_noirq() by
logging teardown errors and returning 0 instead of aborting system
suspend
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 | 126 +++++++++++++++++++++---
1 file changed, 112 insertions(+), 14 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index e3c05ade381b..857814cd107e 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,34 @@ 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;
+
+ if (ep->phy_powered) {
+ ret = phy_power_off(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_powered = false;
+ }
+
+ if (ep->phy_initialized) {
+ ret = phy_exit(ep->phy);
+ if (ret)
+ return ret;
+
+ ep->phy_initialized = false;
+ }
+
+ return 0;
+}
+
static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
{
writel(val, base + reg);
@@ -225,13 +252,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 +355,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 +365,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 +388,42 @@ 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);
+
+ 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);
+ else
+ ep->supplies_enabled = false;
+ }
return 0;
}
@@ -358,13 +434,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
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-10-31 9:49 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-29 13:10 [PATCH] PCI: exynos: Handle PHY initialization errors 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 ` [PATCH v7] " Sreeraj S Kurup
2026-10-31 7:35 ` [PATCH v8] " Sreeraj S Kurup
2026-10-31 9:49 ` [PATCH v9] " Sreeraj S Kurup
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®