mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-17 17:53:56 +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:
|
Optional properties:
|
||||||
- dma-coherent : Present if dma operations are coherent
|
- dma-coherent : Present if dma operations are coherent
|
||||||
|
- clocks : a list of phandle + clock specifier pairs
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
sata@ffe08000 {
|
sata@ffe08000 {
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
AHCI_MAX_PORTS = 32,
|
AHCI_MAX_PORTS = 32,
|
||||||
|
AHCI_MAX_CLKS = 3,
|
||||||
AHCI_MAX_SG = 168, /* hardware max is 64K */
|
AHCI_MAX_SG = 168, /* hardware max is 64K */
|
||||||
AHCI_DMA_BOUNDARY = 0xffffffff,
|
AHCI_DMA_BOUNDARY = 0xffffffff,
|
||||||
AHCI_MAX_CMDS = 32,
|
AHCI_MAX_CMDS = 32,
|
||||||
@ -321,7 +322,7 @@ struct ahci_host_priv {
|
|||||||
u32 em_loc; /* enclosure management location */
|
u32 em_loc; /* enclosure management location */
|
||||||
u32 em_buf_sz; /* EM buffer size in byte */
|
u32 em_buf_sz; /* EM buffer size in byte */
|
||||||
u32 em_msg_type; /* EM message type */
|
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 */
|
void *plat_data; /* Other platform data */
|
||||||
/*
|
/*
|
||||||
* Optional ahci_start_engine override, if not set this gets set to the
|
* 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_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)
|
static int ahci_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
@ -96,6 +150,7 @@ static int ahci_probe(struct platform_device *pdev)
|
|||||||
struct ahci_host_priv *hpriv;
|
struct ahci_host_priv *hpriv;
|
||||||
struct ata_host *host;
|
struct ata_host *host;
|
||||||
struct resource *mem;
|
struct resource *mem;
|
||||||
|
struct clk *clk;
|
||||||
int irq;
|
int irq;
|
||||||
int n_ports;
|
int n_ports;
|
||||||
int i;
|
int i;
|
||||||
@ -130,17 +185,31 @@ static int ahci_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
hpriv->clk = clk_get(dev, NULL);
|
for (i = 0; i < AHCI_MAX_CLKS; i++) {
|
||||||
if (IS_ERR(hpriv->clk)) {
|
/*
|
||||||
dev_err(dev, "can't get clock\n");
|
* For now we must use clk_get(dev, NULL) for the first clock,
|
||||||
} else {
|
* because some platforms (da850, spear13xx) are not yet
|
||||||
rc = clk_prepare_enable(hpriv->clk);
|
* converted to use devicetree for clocks. For new platforms
|
||||||
if (rc) {
|
* this is equivalent to of_clk_get(dev->of_node, 0).
|
||||||
dev_err(dev, "clock prepare enable failed");
|
*/
|
||||||
goto free_clk;
|
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,
|
* Some platforms might need to prepare for mmio region access,
|
||||||
* which could be done in the following init call. So, the mmio
|
* which could be done in the following init call. So, the mmio
|
||||||
@ -221,11 +290,9 @@ pdata_exit:
|
|||||||
if (pdata && pdata->exit)
|
if (pdata && pdata->exit)
|
||||||
pdata->exit(dev);
|
pdata->exit(dev);
|
||||||
disable_unprepare_clk:
|
disable_unprepare_clk:
|
||||||
if (!IS_ERR(hpriv->clk))
|
ahci_disable_clks(hpriv);
|
||||||
clk_disable_unprepare(hpriv->clk);
|
|
||||||
free_clk:
|
free_clk:
|
||||||
if (!IS_ERR(hpriv->clk))
|
ahci_put_clks(hpriv);
|
||||||
clk_put(hpriv->clk);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,10 +305,8 @@ static void ahci_host_stop(struct ata_host *host)
|
|||||||
if (pdata && pdata->exit)
|
if (pdata && pdata->exit)
|
||||||
pdata->exit(dev);
|
pdata->exit(dev);
|
||||||
|
|
||||||
if (!IS_ERR(hpriv->clk)) {
|
ahci_disable_clks(hpriv);
|
||||||
clk_disable_unprepare(hpriv->clk);
|
ahci_put_clks(hpriv);
|
||||||
clk_put(hpriv->clk);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
@ -276,8 +341,7 @@ static int ahci_suspend(struct device *dev)
|
|||||||
if (pdata && pdata->suspend)
|
if (pdata && pdata->suspend)
|
||||||
return pdata->suspend(dev);
|
return pdata->suspend(dev);
|
||||||
|
|
||||||
if (!IS_ERR(hpriv->clk))
|
ahci_disable_clks(hpriv);
|
||||||
clk_disable_unprepare(hpriv->clk);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -289,13 +353,9 @@ static int 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;
|
||||||
|
|
||||||
if (!IS_ERR(hpriv->clk)) {
|
rc = ahci_enable_clks(dev, hpriv);
|
||||||
rc = clk_prepare_enable(hpriv->clk);
|
if (rc)
|
||||||
if (rc) {
|
return rc;
|
||||||
dev_err(dev, "clock prepare enable failed");
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pdata && pdata->resume) {
|
if (pdata && pdata->resume) {
|
||||||
rc = pdata->resume(dev);
|
rc = pdata->resume(dev);
|
||||||
@ -316,8 +376,7 @@ static int ahci_resume(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
disable_unprepare_clk:
|
disable_unprepare_clk:
|
||||||
if (!IS_ERR(hpriv->clk))
|
ahci_disable_clks(hpriv);
|
||||||
clk_disable_unprepare(hpriv->clk);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
struct device;
|
struct device;
|
||||||
struct ata_port_info;
|
struct ata_port_info;
|
||||||
|
struct ahci_host_priv;
|
||||||
|
|
||||||
struct ahci_platform_data {
|
struct ahci_platform_data {
|
||||||
int (*init)(struct device *dev, void __iomem *addr);
|
int (*init)(struct device *dev, void __iomem *addr);
|
||||||
@ -30,4 +31,7 @@ struct ahci_platform_data {
|
|||||||
unsigned int mask_port_map;
|
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 */
|
#endif /* _AHCI_PLATFORM_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user