1

ata: ahci_ceva: fix error handling for Xilinx GT PHY support

Platform clock and phy error resources are not cleaned up in Xilinx GT PHY
error path.

To fix introduce the function ceva_ahci_platform_enable_resources() which
is a customized version of ahci_platform_enable_resources() and inline with
SATA IP programming sequence it does:

- Assert SATA reset
- Program PS GTR phy
- Bring SATA by de-asserting the reset
- Wait for GT lane PLL to be locked

ceva_ahci_platform_enable_resources() is also used in the resume path
as the same SATA programming sequence (as in probe) should be followed.
Also cleanup the mixed usage of ahci_platform_enable_resources() and custom
implementation in the probe function as both are not required.

Fixes: 9a9d3abe24 ("ata: ahci: ceva: Update the driver to support xilinx GT phy")
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
This commit is contained in:
Radhey Shyam Pandey 2024-02-16 23:44:57 +05:30 committed by Niklas Cassel
parent 9815e39617
commit 26c8404e16

View File

@ -88,7 +88,6 @@ struct ceva_ahci_priv {
u32 axicc; u32 axicc;
bool is_cci_enabled; bool is_cci_enabled;
int flags; int flags;
struct reset_control *rst;
}; };
static unsigned int ceva_ahci_read_id(struct ata_device *dev, static unsigned int ceva_ahci_read_id(struct ata_device *dev,
@ -189,6 +188,60 @@ static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME), AHCI_SHT(DRV_NAME),
}; };
static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
{
int rc, i;
rc = ahci_platform_enable_regulators(hpriv);
if (rc)
return rc;
rc = ahci_platform_enable_clks(hpriv);
if (rc)
goto disable_regulator;
/* Assert the controller reset */
rc = ahci_platform_assert_rsts(hpriv);
if (rc)
goto disable_clks;
for (i = 0; i < hpriv->nports; i++) {
rc = phy_init(hpriv->phys[i]);
if (rc)
goto disable_rsts;
}
/* De-assert the controller reset */
ahci_platform_deassert_rsts(hpriv);
for (i = 0; i < hpriv->nports; i++) {
rc = phy_power_on(hpriv->phys[i]);
if (rc) {
phy_exit(hpriv->phys[i]);
goto disable_phys;
}
}
return 0;
disable_rsts:
ahci_platform_deassert_rsts(hpriv);
disable_phys:
while (--i >= 0) {
phy_power_off(hpriv->phys[i]);
phy_exit(hpriv->phys[i]);
}
disable_clks:
ahci_platform_disable_clks(hpriv);
disable_regulator:
ahci_platform_disable_regulators(hpriv);
return rc;
}
static int ceva_ahci_probe(struct platform_device *pdev) static int ceva_ahci_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
@ -203,47 +256,19 @@ static int ceva_ahci_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
cevapriv->ahci_pdev = pdev; cevapriv->ahci_pdev = pdev;
cevapriv->rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
NULL);
if (IS_ERR(cevapriv->rst))
dev_err_probe(&pdev->dev, PTR_ERR(cevapriv->rst),
"failed to get reset\n");
hpriv = ahci_platform_get_resources(pdev, 0); hpriv = ahci_platform_get_resources(pdev, 0);
if (IS_ERR(hpriv)) if (IS_ERR(hpriv))
return PTR_ERR(hpriv); return PTR_ERR(hpriv);
if (!cevapriv->rst) { hpriv->rsts = devm_reset_control_get_optional_exclusive(&pdev->dev,
rc = ahci_platform_enable_resources(hpriv); NULL);
if (rc) if (IS_ERR(hpriv->rsts))
return rc; return dev_err_probe(&pdev->dev, PTR_ERR(hpriv->rsts),
} else { "failed to get reset\n");
int i;
rc = ahci_platform_enable_clks(hpriv); rc = ceva_ahci_platform_enable_resources(hpriv);
if (rc) if (rc)
return rc; return rc;
/* Assert the controller reset */
reset_control_assert(cevapriv->rst);
for (i = 0; i < hpriv->nports; i++) {
rc = phy_init(hpriv->phys[i]);
if (rc)
return rc;
}
/* De-assert the controller reset */
reset_control_deassert(cevapriv->rst);
for (i = 0; i < hpriv->nports; i++) {
rc = phy_power_on(hpriv->phys[i]);
if (rc) {
phy_exit(hpriv->phys[i]);
return rc;
}
}
}
if (of_property_read_bool(np, "ceva,broken-gen2")) if (of_property_read_bool(np, "ceva,broken-gen2"))
cevapriv->flags = CEVA_FLAG_BROKEN_GEN2; cevapriv->flags = CEVA_FLAG_BROKEN_GEN2;
@ -252,52 +277,60 @@ static int ceva_ahci_probe(struct platform_device *pdev)
if (of_property_read_u8_array(np, "ceva,p0-cominit-params", if (of_property_read_u8_array(np, "ceva,p0-cominit-params",
(u8 *)&cevapriv->pp2c[0], 4) < 0) { (u8 *)&cevapriv->pp2c[0], 4) < 0) {
dev_warn(dev, "ceva,p0-cominit-params property not defined\n"); dev_warn(dev, "ceva,p0-cominit-params property not defined\n");
return -EINVAL; rc = -EINVAL;
goto disable_resources;
} }
if (of_property_read_u8_array(np, "ceva,p1-cominit-params", if (of_property_read_u8_array(np, "ceva,p1-cominit-params",
(u8 *)&cevapriv->pp2c[1], 4) < 0) { (u8 *)&cevapriv->pp2c[1], 4) < 0) {
dev_warn(dev, "ceva,p1-cominit-params property not defined\n"); dev_warn(dev, "ceva,p1-cominit-params property not defined\n");
return -EINVAL; rc = -EINVAL;
goto disable_resources;
} }
/* Read OOB timing value for COMWAKE from device-tree*/ /* Read OOB timing value for COMWAKE from device-tree*/
if (of_property_read_u8_array(np, "ceva,p0-comwake-params", if (of_property_read_u8_array(np, "ceva,p0-comwake-params",
(u8 *)&cevapriv->pp3c[0], 4) < 0) { (u8 *)&cevapriv->pp3c[0], 4) < 0) {
dev_warn(dev, "ceva,p0-comwake-params property not defined\n"); dev_warn(dev, "ceva,p0-comwake-params property not defined\n");
return -EINVAL; rc = -EINVAL;
goto disable_resources;
} }
if (of_property_read_u8_array(np, "ceva,p1-comwake-params", if (of_property_read_u8_array(np, "ceva,p1-comwake-params",
(u8 *)&cevapriv->pp3c[1], 4) < 0) { (u8 *)&cevapriv->pp3c[1], 4) < 0) {
dev_warn(dev, "ceva,p1-comwake-params property not defined\n"); dev_warn(dev, "ceva,p1-comwake-params property not defined\n");
return -EINVAL; rc = -EINVAL;
goto disable_resources;
} }
/* Read phy BURST timing value from device-tree */ /* Read phy BURST timing value from device-tree */
if (of_property_read_u8_array(np, "ceva,p0-burst-params", if (of_property_read_u8_array(np, "ceva,p0-burst-params",
(u8 *)&cevapriv->pp4c[0], 4) < 0) { (u8 *)&cevapriv->pp4c[0], 4) < 0) {
dev_warn(dev, "ceva,p0-burst-params property not defined\n"); dev_warn(dev, "ceva,p0-burst-params property not defined\n");
return -EINVAL; rc = -EINVAL;
goto disable_resources;
} }
if (of_property_read_u8_array(np, "ceva,p1-burst-params", if (of_property_read_u8_array(np, "ceva,p1-burst-params",
(u8 *)&cevapriv->pp4c[1], 4) < 0) { (u8 *)&cevapriv->pp4c[1], 4) < 0) {
dev_warn(dev, "ceva,p1-burst-params property not defined\n"); dev_warn(dev, "ceva,p1-burst-params property not defined\n");
return -EINVAL; rc = -EINVAL;
goto disable_resources;
} }
/* Read phy RETRY interval timing value from device-tree */ /* Read phy RETRY interval timing value from device-tree */
if (of_property_read_u16_array(np, "ceva,p0-retry-params", if (of_property_read_u16_array(np, "ceva,p0-retry-params",
(u16 *)&cevapriv->pp5c[0], 2) < 0) { (u16 *)&cevapriv->pp5c[0], 2) < 0) {
dev_warn(dev, "ceva,p0-retry-params property not defined\n"); dev_warn(dev, "ceva,p0-retry-params property not defined\n");
return -EINVAL; rc = -EINVAL;
goto disable_resources;
} }
if (of_property_read_u16_array(np, "ceva,p1-retry-params", if (of_property_read_u16_array(np, "ceva,p1-retry-params",
(u16 *)&cevapriv->pp5c[1], 2) < 0) { (u16 *)&cevapriv->pp5c[1], 2) < 0) {
dev_warn(dev, "ceva,p1-retry-params property not defined\n"); dev_warn(dev, "ceva,p1-retry-params property not defined\n");
return -EINVAL; rc = -EINVAL;
goto disable_resources;
} }
/* /*
@ -335,7 +368,7 @@ static int __maybe_unused ceva_ahci_resume(struct device *dev)
struct ahci_host_priv *hpriv = host->private_data; struct ahci_host_priv *hpriv = host->private_data;
int rc; int rc;
rc = ahci_platform_enable_resources(hpriv); rc = ceva_ahci_platform_enable_resources(hpriv);
if (rc) if (rc)
return rc; return rc;