diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 6441cbecfa1..9be471200a0 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -35,6 +35,7 @@ #ifndef _AHCI_H #define _AHCI_H +#include #include /* Enclosure Management Control */ @@ -316,6 +317,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 */ }; extern int ahci_ignore_sss; diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index dc187c74664..b1ae48054dc 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -12,6 +12,7 @@ * any later version. */ +#include #include #include #include @@ -118,6 +119,17 @@ static int __init 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; + } + } + /* * Some platforms might need to prepare for mmio region access, * which could be done in the following init call. So, the mmio @@ -127,7 +139,7 @@ static int __init ahci_probe(struct platform_device *pdev) if (pdata && pdata->init) { rc = pdata->init(dev, hpriv->mmio); if (rc) - return rc; + goto disable_unprepare_clk; } ahci_save_initial_config(dev, hpriv, @@ -153,7 +165,7 @@ static int __init ahci_probe(struct platform_device *pdev) host = ata_host_alloc_pinfo(dev, ppi, n_ports); if (!host) { rc = -ENOMEM; - goto err0; + goto pdata_exit; } host->private_data = hpriv; @@ -183,7 +195,7 @@ static int __init ahci_probe(struct platform_device *pdev) rc = ahci_reset_controller(host); if (rc) - goto err0; + goto pdata_exit; ahci_init_controller(host); ahci_print_info(host, "platform"); @@ -191,12 +203,18 @@ static int __init ahci_probe(struct platform_device *pdev) rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, &ahci_platform_sht); if (rc) - goto err0; + goto pdata_exit; return 0; -err0: +pdata_exit: if (pdata && pdata->exit) pdata->exit(dev); +disable_unprepare_clk: + if (!IS_ERR(hpriv->clk)) + clk_disable_unprepare(hpriv->clk); +free_clk: + if (!IS_ERR(hpriv->clk)) + clk_put(hpriv->clk); return rc; } @@ -205,12 +223,18 @@ static int __devexit ahci_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; struct ahci_platform_data *pdata = dev_get_platdata(dev); struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; ata_host_detach(host); if (pdata && pdata->exit) pdata->exit(dev); + if (!IS_ERR(hpriv->clk)) { + clk_disable_unprepare(hpriv->clk); + clk_put(hpriv->clk); + } + return 0; } @@ -245,6 +269,10 @@ 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); + return 0; } @@ -252,18 +280,27 @@ static int ahci_resume(struct device *dev) { struct ahci_platform_data *pdata = dev_get_platdata(dev); struct ata_host *host = dev_get_drvdata(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; + } + } + if (pdata && pdata->resume) { rc = pdata->resume(dev); if (rc) - return rc; + goto disable_unprepare_clk; } if (dev->power.power_state.event == PM_EVENT_SUSPEND) { rc = ahci_reset_controller(host); if (rc) - return rc; + goto disable_unprepare_clk; ahci_init_controller(host); } @@ -271,6 +308,12 @@ static int ahci_resume(struct device *dev) ata_host_resume(host); return 0; + +disable_unprepare_clk: + if (!IS_ERR(hpriv->clk)) + clk_disable_unprepare(hpriv->clk); + + return rc; } #endif