mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-17 09:43:59 +08:00
ahci-platform: Add support for devices with more then 1 clock
The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the imx AHCI controller needs 3 clocks to be enabled. tj: Minor comment formatting updates. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
parent
039ece38da
commit
156c588794
@ -10,6 +10,7 @@ Required properties:
|
||||
|
||||
Optional properties:
|
||||
- dma-coherent : Present if dma operations are coherent
|
||||
- clocks : a list of phandle + clock specifier pairs
|
||||
|
||||
Example:
|
||||
sata@ffe08000 {
|
||||
|
@ -51,6 +51,7 @@
|
||||
|
||||
enum {
|
||||
AHCI_MAX_PORTS = 32,
|
||||
AHCI_MAX_CLKS = 3,
|
||||
AHCI_MAX_SG = 168, /* hardware max is 64K */
|
||||
AHCI_DMA_BOUNDARY = 0xffffffff,
|
||||
AHCI_MAX_CMDS = 32,
|
||||
@ -321,7 +322,7 @@ struct ahci_host_priv {
|
||||
u32 em_loc; /* enclosure management location */
|
||||
u32 em_buf_sz; /* EM buffer size in byte */
|
||||
u32 em_msg_type; /* EM message type */
|
||||
struct clk *clk; /* Only for platforms supporting clk */
|
||||
struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
|
||||
void *plat_data; /* Other platform data */
|
||||
/*
|
||||
* Optional ahci_start_engine override, if not set this gets set to the
|
||||
|
@ -86,6 +86,60 @@ static struct scsi_host_template ahci_platform_sht = {
|
||||
AHCI_SHT("ahci_platform"),
|
||||
};
|
||||
|
||||
/**
|
||||
* ahci_platform_enable_clks - Enable platform clocks
|
||||
* @hpriv: host private area to store config values
|
||||
*
|
||||
* This function enables all the clks found in hpriv->clks, starting at
|
||||
* index 0. If any clk fails to enable it disables all the clks already
|
||||
* enabled in reverse order, and then returns an error.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success otherwise a negative error code
|
||||
*/
|
||||
int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
|
||||
{
|
||||
int c, rc;
|
||||
|
||||
for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
|
||||
rc = clk_prepare_enable(hpriv->clks[c]);
|
||||
if (rc)
|
||||
goto disable_unprepare_clk;
|
||||
}
|
||||
return 0;
|
||||
|
||||
disable_unprepare_clk:
|
||||
while (--c >= 0)
|
||||
clk_disable_unprepare(hpriv->clks[c]);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
|
||||
|
||||
/**
|
||||
* ahci_platform_disable_clks - Disable platform clocks
|
||||
* @hpriv: host private area to store config values
|
||||
*
|
||||
* This function disables all the clks found in hpriv->clks, in reverse
|
||||
* order of ahci_platform_enable_clks (starting at the end of the array).
|
||||
*/
|
||||
void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
|
||||
{
|
||||
int c;
|
||||
|
||||
for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
|
||||
if (hpriv->clks[c])
|
||||
clk_disable_unprepare(hpriv->clks[c]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
|
||||
|
||||
static void ahci_put_clks(struct ahci_host_priv *hpriv)
|
||||
{
|
||||
int c;
|
||||
|
||||
for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
|
||||
clk_put(hpriv->clks[c]);
|
||||
}
|
||||
|
||||
static int ahci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -96,6 +150,7 @@ static int ahci_probe(struct platform_device *pdev)
|
||||
struct ahci_host_priv *hpriv;
|
||||
struct ata_host *host;
|
||||
struct resource *mem;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
int n_ports;
|
||||
int i;
|
||||
@ -130,17 +185,31 @@ static int ahci_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hpriv->clk = clk_get(dev, NULL);
|
||||
if (IS_ERR(hpriv->clk)) {
|
||||
dev_err(dev, "can't get clock\n");
|
||||
} else {
|
||||
rc = clk_prepare_enable(hpriv->clk);
|
||||
if (rc) {
|
||||
dev_err(dev, "clock prepare enable failed");
|
||||
goto free_clk;
|
||||
for (i = 0; i < AHCI_MAX_CLKS; i++) {
|
||||
/*
|
||||
* For now we must use clk_get(dev, NULL) for the first clock,
|
||||
* because some platforms (da850, spear13xx) are not yet
|
||||
* converted to use devicetree for clocks. For new platforms
|
||||
* this is equivalent to of_clk_get(dev->of_node, 0).
|
||||
*/
|
||||
if (i == 0)
|
||||
clk = clk_get(dev, NULL);
|
||||
else
|
||||
clk = of_clk_get(dev->of_node, i);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
rc = PTR_ERR(clk);
|
||||
if (rc == -EPROBE_DEFER)
|
||||
goto free_clk;
|
||||
break;
|
||||
}
|
||||
hpriv->clks[i] = clk;
|
||||
}
|
||||
|
||||
rc = ahci_enable_clks(dev, hpriv);
|
||||
if (rc)
|
||||
goto free_clk;
|
||||
|
||||
/*
|
||||
* Some platforms might need to prepare for mmio region access,
|
||||
* which could be done in the following init call. So, the mmio
|
||||
@ -221,11 +290,9 @@ pdata_exit:
|
||||
if (pdata && pdata->exit)
|
||||
pdata->exit(dev);
|
||||
disable_unprepare_clk:
|
||||
if (!IS_ERR(hpriv->clk))
|
||||
clk_disable_unprepare(hpriv->clk);
|
||||
ahci_disable_clks(hpriv);
|
||||
free_clk:
|
||||
if (!IS_ERR(hpriv->clk))
|
||||
clk_put(hpriv->clk);
|
||||
ahci_put_clks(hpriv);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -238,10 +305,8 @@ static void ahci_host_stop(struct ata_host *host)
|
||||
if (pdata && pdata->exit)
|
||||
pdata->exit(dev);
|
||||
|
||||
if (!IS_ERR(hpriv->clk)) {
|
||||
clk_disable_unprepare(hpriv->clk);
|
||||
clk_put(hpriv->clk);
|
||||
}
|
||||
ahci_disable_clks(hpriv);
|
||||
ahci_put_clks(hpriv);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -276,8 +341,7 @@ static int ahci_suspend(struct device *dev)
|
||||
if (pdata && pdata->suspend)
|
||||
return pdata->suspend(dev);
|
||||
|
||||
if (!IS_ERR(hpriv->clk))
|
||||
clk_disable_unprepare(hpriv->clk);
|
||||
ahci_disable_clks(hpriv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -289,13 +353,9 @@ static int ahci_resume(struct device *dev)
|
||||
struct ahci_host_priv *hpriv = host->private_data;
|
||||
int rc;
|
||||
|
||||
if (!IS_ERR(hpriv->clk)) {
|
||||
rc = clk_prepare_enable(hpriv->clk);
|
||||
if (rc) {
|
||||
dev_err(dev, "clock prepare enable failed");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
rc = ahci_enable_clks(dev, hpriv);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (pdata && pdata->resume) {
|
||||
rc = pdata->resume(dev);
|
||||
@ -316,8 +376,7 @@ static int ahci_resume(struct device *dev)
|
||||
return 0;
|
||||
|
||||
disable_unprepare_clk:
|
||||
if (!IS_ERR(hpriv->clk))
|
||||
clk_disable_unprepare(hpriv->clk);
|
||||
ahci_disable_clks(hpriv);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
struct device;
|
||||
struct ata_port_info;
|
||||
struct ahci_host_priv;
|
||||
|
||||
struct ahci_platform_data {
|
||||
int (*init)(struct device *dev, void __iomem *addr);
|
||||
@ -30,4 +31,7 @@ struct ahci_platform_data {
|
||||
unsigned int mask_port_map;
|
||||
};
|
||||
|
||||
int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
|
||||
void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
|
||||
|
||||
#endif /* _AHCI_PLATFORM_H */
|
||||
|
Loading…
Reference in New Issue
Block a user