Commit c0cdf2ac authored by Florian Fainelli's avatar Florian Fainelli Committed by Jens Axboe

ata: ahci_brcm: Fix AHCI resources management

The AHCI resources management within ahci_brcm.c is a little
convoluted, largely because it historically had a dedicated clock that
was managed within this file in the downstream tree. Once brough
upstream though, the clock was left to be managed by libahci_platform.c
which is entirely appropriate.

This patch series ensures that the AHCI resources are fetched and
enabled before any register access is done, thus avoiding bus errors on
platforms which clock gate the controller by default.

As a result we need to re-arrange the suspend() and resume() functions
in order to avoid accessing registers after the clocks have been turned
off respectively before the clocks have been turned on. Finally, we can
refactor brcm_ahci_get_portmask() in order to fetch the number of ports
from hpriv->mmio which is now accessible without jumping through hoops
like we used to do.

The commit pointed in the Fixes tag is both old and new enough not to
require major headaches for backporting of this patch.

Fixes: eba68f82 ("ata: ahci_brcmstb: rename to support across Broadcom SoC's")
Cc: stable@vger.kernel.org
Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 84b032db
...@@ -213,19 +213,12 @@ static void brcm_sata_phys_disable(struct brcm_ahci_priv *priv) ...@@ -213,19 +213,12 @@ static void brcm_sata_phys_disable(struct brcm_ahci_priv *priv)
brcm_sata_phy_disable(priv, i); brcm_sata_phy_disable(priv, i);
} }
static u32 brcm_ahci_get_portmask(struct platform_device *pdev, static u32 brcm_ahci_get_portmask(struct ahci_host_priv *hpriv,
struct brcm_ahci_priv *priv) struct brcm_ahci_priv *priv)
{ {
void __iomem *ahci;
struct resource *res;
u32 impl; u32 impl;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci"); impl = readl(hpriv->mmio + HOST_PORTS_IMPL);
ahci = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ahci))
return 0;
impl = readl(ahci + HOST_PORTS_IMPL);
if (fls(impl) > SATA_TOP_MAX_PHYS) if (fls(impl) > SATA_TOP_MAX_PHYS)
dev_warn(priv->dev, "warning: more ports than PHYs (%#x)\n", dev_warn(priv->dev, "warning: more ports than PHYs (%#x)\n",
...@@ -233,9 +226,6 @@ static u32 brcm_ahci_get_portmask(struct platform_device *pdev, ...@@ -233,9 +226,6 @@ static u32 brcm_ahci_get_portmask(struct platform_device *pdev,
else if (!impl) else if (!impl)
dev_info(priv->dev, "no ports found\n"); dev_info(priv->dev, "no ports found\n");
devm_iounmap(&pdev->dev, ahci);
devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
return impl; return impl;
} }
...@@ -347,11 +337,10 @@ static int brcm_ahci_suspend(struct device *dev) ...@@ -347,11 +337,10 @@ static int brcm_ahci_suspend(struct device *dev)
struct ata_host *host = dev_get_drvdata(dev); struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data; struct ahci_host_priv *hpriv = host->private_data;
struct brcm_ahci_priv *priv = hpriv->plat_data; struct brcm_ahci_priv *priv = hpriv->plat_data;
int ret;
ret = ahci_platform_suspend(dev);
brcm_sata_phys_disable(priv); brcm_sata_phys_disable(priv);
return ret;
return ahci_platform_suspend(dev);
} }
static int brcm_ahci_resume(struct device *dev) static int brcm_ahci_resume(struct device *dev)
...@@ -359,11 +348,44 @@ static int brcm_ahci_resume(struct device *dev) ...@@ -359,11 +348,44 @@ static int brcm_ahci_resume(struct device *dev)
struct ata_host *host = dev_get_drvdata(dev); struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data; struct ahci_host_priv *hpriv = host->private_data;
struct brcm_ahci_priv *priv = hpriv->plat_data; struct brcm_ahci_priv *priv = hpriv->plat_data;
int ret;
/* Make sure clocks are turned on before re-configuration */
ret = ahci_platform_enable_clks(hpriv);
if (ret)
return ret;
brcm_sata_init(priv); brcm_sata_init(priv);
brcm_sata_phys_enable(priv); brcm_sata_phys_enable(priv);
brcm_sata_alpm_init(hpriv); brcm_sata_alpm_init(hpriv);
return ahci_platform_resume(dev);
/* Since we had to enable clocks earlier on, we cannot use
* ahci_platform_resume() as-is since a second call to
* ahci_platform_enable_resources() would bump up the resources
* (regulators, clocks, PHYs) count artificially so we copy the part
* after ahci_platform_enable_resources().
*/
ret = ahci_platform_enable_phys(hpriv);
if (ret)
goto out_disable_phys;
ret = ahci_platform_resume_host(dev);
if (ret)
goto out_disable_platform_phys;
/* We resumed so update PM runtime state */
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
out_disable_platform_phys:
ahci_platform_disable_phys(hpriv);
out_disable_phys:
brcm_sata_phys_disable(priv);
ahci_platform_disable_clks(hpriv);
return ret;
} }
#endif #endif
...@@ -416,38 +438,63 @@ static int brcm_ahci_probe(struct platform_device *pdev) ...@@ -416,38 +438,63 @@ static int brcm_ahci_probe(struct platform_device *pdev)
priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE; priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE;
} }
hpriv = ahci_platform_get_resources(pdev, 0);
if (IS_ERR(hpriv)) {
ret = PTR_ERR(hpriv);
goto out_reset;
}
ret = ahci_platform_enable_clks(hpriv);
if (ret)
goto out_reset;
/* Must be first so as to configure endianness including that
* of the standard AHCI register space.
*/
brcm_sata_init(priv); brcm_sata_init(priv);
priv->port_mask = brcm_ahci_get_portmask(pdev, priv); /* Initializes priv->port_mask which is used below */
if (!priv->port_mask) priv->port_mask = brcm_ahci_get_portmask(hpriv, priv);
return -ENODEV; if (!priv->port_mask) {
ret = -ENODEV;
goto out_disable_clks;
}
/* Must be done before ahci_platform_enable_phys() */
brcm_sata_phys_enable(priv); brcm_sata_phys_enable(priv);
hpriv = ahci_platform_get_resources(pdev, 0);
if (IS_ERR(hpriv))
return PTR_ERR(hpriv);
hpriv->plat_data = priv; hpriv->plat_data = priv;
hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP; hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP;
brcm_sata_alpm_init(hpriv); brcm_sata_alpm_init(hpriv);
ret = ahci_platform_enable_resources(hpriv);
if (ret)
return ret;
if (priv->quirks & BRCM_AHCI_QUIRK_NO_NCQ) if (priv->quirks & BRCM_AHCI_QUIRK_NO_NCQ)
hpriv->flags |= AHCI_HFLAG_NO_NCQ; hpriv->flags |= AHCI_HFLAG_NO_NCQ;
hpriv->flags |= AHCI_HFLAG_NO_WRITE_TO_RO; hpriv->flags |= AHCI_HFLAG_NO_WRITE_TO_RO;
ret = ahci_platform_enable_phys(hpriv);
if (ret)
goto out_disable_phys;
ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info, ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info,
&ahci_platform_sht); &ahci_platform_sht);
if (ret) if (ret)
return ret; goto out_disable_platform_phys;
dev_info(dev, "Broadcom AHCI SATA3 registered\n"); dev_info(dev, "Broadcom AHCI SATA3 registered\n");
return 0; return 0;
out_disable_platform_phys:
ahci_platform_disable_phys(hpriv);
out_disable_phys:
brcm_sata_phys_disable(priv);
out_disable_clks:
ahci_platform_disable_clks(hpriv);
out_reset:
if (!IS_ERR_OR_NULL(priv->rcdev))
reset_control_assert(priv->rcdev);
return ret;
} }
static int brcm_ahci_remove(struct platform_device *pdev) static int brcm_ahci_remove(struct platform_device *pdev)
...@@ -457,12 +504,12 @@ static int brcm_ahci_remove(struct platform_device *pdev) ...@@ -457,12 +504,12 @@ static int brcm_ahci_remove(struct platform_device *pdev)
struct brcm_ahci_priv *priv = hpriv->plat_data; struct brcm_ahci_priv *priv = hpriv->plat_data;
int ret; int ret;
brcm_sata_phys_disable(priv);
ret = ata_platform_remove_one(pdev); ret = ata_platform_remove_one(pdev);
if (ret) if (ret)
return ret; return ret;
brcm_sata_phys_disable(priv);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment