From 1bc18086231c130895b87ec049be8ddcdab552b8 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 21 Jan 2014 16:22:51 -0500 Subject: ata: delete non-required instances of include None of these files are actually using any __init type directives and hence don't need to include . Most are just a left over from __devinit and __cpuinit removal, or simply due to code getting copied from one driver to the next. Cc: linux-ide@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 4b231baceb09..9302d8143393 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 156c5887948cd191417f18026aab9ce26e5a95da Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 22 Feb 2014 16:53:31 +0100 Subject: 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 Signed-off-by: Tejun Heo --- .../devicetree/bindings/ata/ahci-platform.txt | 1 + drivers/ata/ahci.h | 3 +- drivers/ata/ahci_platform.c | 113 ++++++++++++++++----- include/linux/ahci_platform.h | 4 + 4 files changed, 93 insertions(+), 28 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt index 89de1564950c..3ced07dc8f56 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.txt +++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt @@ -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 { diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 64d1a99de5e4..c12862bd48ee 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -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 diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 9302d8143393..8d5a9a7ce958 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -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; } diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 73a25005d88a..769d06525320 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -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 */ -- cgit v1.2.3 From 4b3e603a298db26c6c37e8b08adcce24d014df13 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 22 Feb 2014 16:53:32 +0100 Subject: ahci-platform: Add support for an optional regulator for sata-target power Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- .../devicetree/bindings/ata/ahci-platform.txt | 1 + drivers/ata/ahci.h | 2 ++ drivers/ata/ahci_platform.c | 36 ++++++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt index 3ced07dc8f56..1ac807fd34e6 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.txt +++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt @@ -11,6 +11,7 @@ Required properties: Optional properties: - dma-coherent : Present if dma operations are coherent - clocks : a list of phandle + clock specifier pairs +- target-supply : regulator for SATA target power Example: sata@ffe08000 { diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index c12862bd48ee..bf8100c51142 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -37,6 +37,7 @@ #include #include +#include /* Enclosure Management Control */ #define EM_CTRL_MSG_TYPE 0x000f0000 @@ -323,6 +324,7 @@ struct ahci_host_priv { u32 em_buf_sz; /* EM buffer size in byte */ u32 em_msg_type; /* EM message type */ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ void *plat_data; /* Other platform data */ /* * Optional ahci_start_engine override, if not set this gets set to the diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 8d5a9a7ce958..4befd78133ad 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -185,6 +185,14 @@ static int ahci_probe(struct platform_device *pdev) return -ENOMEM; } + hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); + if (IS_ERR(hpriv->target_pwr)) { + rc = PTR_ERR(hpriv->target_pwr); + if (rc == -EPROBE_DEFER) + return -EPROBE_DEFER; + hpriv->target_pwr = NULL; + } + for (i = 0; i < AHCI_MAX_CLKS; i++) { /* * For now we must use clk_get(dev, NULL) for the first clock, @@ -206,9 +214,15 @@ static int ahci_probe(struct platform_device *pdev) hpriv->clks[i] = clk; } + if (hpriv->target_pwr) { + rc = regulator_enable(hpriv->target_pwr); + if (rc) + goto free_clk; + } + rc = ahci_enable_clks(dev, hpriv); if (rc) - goto free_clk; + goto disable_regulator; /* * Some platforms might need to prepare for mmio region access, @@ -291,6 +305,9 @@ pdata_exit: pdata->exit(dev); disable_unprepare_clk: ahci_disable_clks(hpriv); +disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); free_clk: ahci_put_clks(hpriv); return rc; @@ -307,6 +324,9 @@ static void ahci_host_stop(struct ata_host *host) ahci_disable_clks(hpriv); ahci_put_clks(hpriv); + + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); } #ifdef CONFIG_PM_SLEEP @@ -343,6 +363,9 @@ static int ahci_suspend(struct device *dev) ahci_disable_clks(hpriv); + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); + return 0; } @@ -353,9 +376,15 @@ static int ahci_resume(struct device *dev) struct ahci_host_priv *hpriv = host->private_data; int rc; + if (hpriv->target_pwr) { + rc = regulator_enable(hpriv->target_pwr); + if (rc) + return rc; + } + rc = ahci_enable_clks(dev, hpriv); if (rc) - return rc; + goto disable_regulator; if (pdata && pdata->resume) { rc = pdata->resume(dev); @@ -377,6 +406,9 @@ static int ahci_resume(struct device *dev) disable_unprepare_clk: ahci_disable_clks(hpriv); +disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); return rc; } -- cgit v1.2.3 From 96a01ba52c60fdd74dd6e8cf06645d06515b1396 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 22 Feb 2014 16:53:33 +0100 Subject: ahci-platform: Add enable_ / disable_resources helper functions tj: Minor comment formatting updates. Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 106 +++++++++++++++++++++++++++--------------- include/linux/ahci_platform.h | 2 + 2 files changed, 71 insertions(+), 37 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 4befd78133ad..a32df31013cb 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -132,6 +132,62 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) } EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); +/** + * ahci_platform_enable_resources - Enable platform resources + * @hpriv: host private area to store config values + * + * This function enables all ahci_platform managed resources in the + * following order: + * 1) Regulator + * 2) Clocks (through ahci_platform_enable_clks) + * + * If resource enabling fails at any point the previous enabled resources + * are disabled in reverse order. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) +{ + int rc; + + if (hpriv->target_pwr) { + rc = regulator_enable(hpriv->target_pwr); + if (rc) + return rc; + } + + rc = ahci_platform_enable_clks(hpriv); + if (rc) + goto disable_regulator; + + return 0; + +disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); + +/** + * ahci_platform_disable_resources - Disable platform resources + * @hpriv: host private area to store config values + * + * This function disables all ahci_platform managed resources in the + * following order: + * 1) Clocks (through ahci_platform_disable_clks) + * 2) Regulator + */ +void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) +{ + ahci_platform_disable_clks(hpriv); + + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); +} +EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); + static void ahci_put_clks(struct ahci_host_priv *hpriv) { int c; @@ -214,15 +270,9 @@ static int ahci_probe(struct platform_device *pdev) hpriv->clks[i] = clk; } - if (hpriv->target_pwr) { - rc = regulator_enable(hpriv->target_pwr); - if (rc) - goto free_clk; - } - - rc = ahci_enable_clks(dev, hpriv); + rc = ahci_platform_enable_resources(hpriv); if (rc) - goto disable_regulator; + goto free_clk; /* * Some platforms might need to prepare for mmio region access, @@ -233,7 +283,7 @@ static int ahci_probe(struct platform_device *pdev) if (pdata && pdata->init) { rc = pdata->init(dev, hpriv->mmio); if (rc) - goto disable_unprepare_clk; + goto disable_resources; } ahci_save_initial_config(dev, hpriv, @@ -303,11 +353,8 @@ static int ahci_probe(struct platform_device *pdev) pdata_exit: if (pdata && pdata->exit) pdata->exit(dev); -disable_unprepare_clk: - ahci_disable_clks(hpriv); -disable_regulator: - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); +disable_resources: + ahci_platform_disable_resources(hpriv); free_clk: ahci_put_clks(hpriv); return rc; @@ -322,11 +369,8 @@ static void ahci_host_stop(struct ata_host *host) if (pdata && pdata->exit) pdata->exit(dev); - ahci_disable_clks(hpriv); + ahci_platform_disable_resources(hpriv); ahci_put_clks(hpriv); - - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); } #ifdef CONFIG_PM_SLEEP @@ -361,10 +405,7 @@ static int ahci_suspend(struct device *dev) if (pdata && pdata->suspend) return pdata->suspend(dev); - ahci_disable_clks(hpriv); - - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); + ahci_platform_disable_resources(hpriv); return 0; } @@ -376,26 +417,20 @@ static int ahci_resume(struct device *dev) struct ahci_host_priv *hpriv = host->private_data; int rc; - if (hpriv->target_pwr) { - rc = regulator_enable(hpriv->target_pwr); - if (rc) - return rc; - } - - rc = ahci_enable_clks(dev, hpriv); + rc = ahci_platform_enable_resources(hpriv); if (rc) - goto disable_regulator; + return rc; if (pdata && pdata->resume) { rc = pdata->resume(dev); if (rc) - goto disable_unprepare_clk; + goto disable_resources; } if (dev->power.power_state.event == PM_EVENT_SUSPEND) { rc = ahci_reset_controller(host); if (rc) - goto disable_unprepare_clk; + goto disable_resources; ahci_init_controller(host); } @@ -404,11 +439,8 @@ static int ahci_resume(struct device *dev) return 0; -disable_unprepare_clk: - ahci_disable_clks(hpriv); -disable_regulator: - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); +disable_resources: + ahci_platform_disable_resources(hpriv); return rc; } diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 769d06525320..b674b01ce9bc 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -33,5 +33,7 @@ struct ahci_platform_data { int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); +int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); +void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); #endif /* _AHCI_PLATFORM_H */ -- cgit v1.2.3 From 23b07d4cb3c0c850055cf968af44019b8da185fb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 22 Feb 2014 16:53:34 +0100 Subject: ahci-platform: "Library-ise" ahci_probe functionality ahci_probe consists of 3 steps: 1) Get resources (get mmio, clks, regulator) 2) Enable resources, handled by ahci_platform_enable_resouces 3) The more or less standard ahci-host controller init sequence This commit refactors step 1 and 3 into separate functions, so the platform drivers for AHCI implementations which need a specific order in step 2, and / or need to do some custom register poking at some time, can re-use ahci-platform.c code without needing to copy and paste it. Note that ahci_platform_init_host's prototype takes the 3 non function members of ahci_platform_data as arguments, the idea is that drivers using the new exported utility functions will not use ahci_platform_data at all, and hopefully in the future ahci_platform_data can go away entirely. tj: Minor comment formatting updates. Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 188 +++++++++++++++++++++++++++--------------- include/linux/ahci_platform.h | 14 ++++ 2 files changed, 137 insertions(+), 65 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index a32df31013cb..19e9eaafb1f2 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -188,64 +188,60 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) } EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); -static void ahci_put_clks(struct ahci_host_priv *hpriv) +static void ahci_platform_put_resources(struct device *dev, void *res) { + struct ahci_host_priv *hpriv = res; 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) +/** + * ahci_platform_get_resources - Get platform resources + * @pdev: platform device to get resources for + * + * This function allocates an ahci_host_priv struct, and gets the following + * resources, storing a reference to them inside the returned struct: + * + * 1) mmio registers (IORESOURCE_MEM 0, mandatory) + * 2) regulator for controlling the targets power (optional) + * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, + * or for non devicetree enabled platforms a single clock + * + * RETURNS: + * The allocated ahci_host_priv on success, otherwise an ERR_PTR value + */ +struct ahci_host_priv *ahci_platform_get_resources( + struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct ahci_platform_data *pdata = dev_get_platdata(dev); - const struct platform_device_id *id = platform_get_device_id(pdev); - struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; - const struct ata_port_info *ppi[] = { &pi, NULL }; struct ahci_host_priv *hpriv; - struct ata_host *host; - struct resource *mem; struct clk *clk; - int irq; - int n_ports; - int i; - int rc; + int i, rc = -ENOMEM; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(dev, "no mmio space\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "no irq\n"); - return -EINVAL; - } + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); - if (pdata && pdata->ata_port_info) - pi = *pdata->ata_port_info; - - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) { - dev_err(dev, "can't alloc ahci_host_priv\n"); - return -ENOMEM; - } + hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), + GFP_KERNEL); + if (!hpriv) + goto err_out; - hpriv->flags |= (unsigned long)pi.private_data; + devres_add(dev, hpriv); - hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); + hpriv->mmio = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); if (!hpriv->mmio) { - dev_err(dev, "can't map %pR\n", mem); - return -ENOMEM; + dev_err(dev, "no mmio space\n"); + goto err_out; } hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); if (IS_ERR(hpriv->target_pwr)) { rc = PTR_ERR(hpriv->target_pwr); if (rc == -EPROBE_DEFER) - return -EPROBE_DEFER; + goto err_out; hpriv->target_pwr = NULL; } @@ -264,33 +260,59 @@ static int ahci_probe(struct platform_device *pdev) if (IS_ERR(clk)) { rc = PTR_ERR(clk); if (rc == -EPROBE_DEFER) - goto free_clk; + goto err_out; break; } hpriv->clks[i] = clk; } - rc = ahci_platform_enable_resources(hpriv); - if (rc) - goto free_clk; + devres_remove_group(dev, NULL); + return hpriv; - /* - * Some platforms might need to prepare for mmio region access, - * which could be done in the following init call. So, the mmio - * region shouldn't be accessed before init (if provided) has - * returned successfully. - */ - if (pdata && pdata->init) { - rc = pdata->init(dev, hpriv->mmio); - if (rc) - goto disable_resources; - } +err_out: + devres_release_group(dev, NULL); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(ahci_platform_get_resources); + +/** + * ahci_platform_init_host - Bring up an ahci-platform host + * @pdev: platform device pointer for the host + * @hpriv: ahci-host private data for the host + * @pi_template: template for the ata_port_info to use + * @force_port_map: param passed to ahci_save_initial_config + * @mask_port_map: param passed to ahci_save_initial_config + * + * This function does all the usual steps needed to bring up an + * ahci-platform host, note any necessary resources (ie clks, phy, etc.) + * must be initialized / enabled before calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_init_host(struct platform_device *pdev, + struct ahci_host_priv *hpriv, + const struct ata_port_info *pi_template, + unsigned int force_port_map, + unsigned int mask_port_map) +{ + struct device *dev = &pdev->dev; + struct ata_port_info pi = *pi_template; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ata_host *host; + int i, irq, n_ports, rc; - ahci_save_initial_config(dev, hpriv, - pdata ? pdata->force_port_map : 0, - pdata ? pdata->mask_port_map : 0); + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "no irq\n"); + return -EINVAL; + } /* prepare host */ + hpriv->flags |= (unsigned long)pi.private_data; + + ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); + if (hpriv->cap & HOST_CAP_NCQ) pi.flags |= ATA_FLAG_NCQ; @@ -307,10 +329,8 @@ static int ahci_probe(struct platform_device *pdev) n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); host = ata_host_alloc_pinfo(dev, ppi, n_ports); - if (!host) { - rc = -ENOMEM; - goto pdata_exit; - } + if (!host) + return -ENOMEM; host->private_data = hpriv; @@ -325,7 +345,8 @@ static int ahci_probe(struct platform_device *pdev) for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - ata_port_desc(ap, "mmio %pR", mem); + ata_port_desc(ap, "mmio %pR", + platform_get_resource(pdev, IORESOURCE_MEM, 0)); ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); /* set enclosure management message type */ @@ -339,13 +360,53 @@ static int ahci_probe(struct platform_device *pdev) rc = ahci_reset_controller(host); if (rc) - goto pdata_exit; + return rc; ahci_init_controller(host); ahci_print_info(host, "platform"); - rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, - &ahci_platform_sht); + return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, + &ahci_platform_sht); +} +EXPORT_SYMBOL_GPL(ahci_platform_init_host); + +static int ahci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); + const struct platform_device_id *id = platform_get_device_id(pdev); + const struct ata_port_info *pi_template; + struct ahci_host_priv *hpriv; + int rc; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + /* + * Some platforms might need to prepare for mmio region access, + * which could be done in the following init call. So, the mmio + * region shouldn't be accessed before init (if provided) has + * returned successfully. + */ + if (pdata && pdata->init) { + rc = pdata->init(dev, hpriv->mmio); + if (rc) + goto disable_resources; + } + + if (pdata && pdata->ata_port_info) + pi_template = pdata->ata_port_info; + else + pi_template = &ahci_port_info[id ? id->driver_data : 0]; + + rc = ahci_platform_init_host(pdev, hpriv, pi_template, + pdata ? pdata->force_port_map : 0, + pdata ? pdata->mask_port_map : 0); if (rc) goto pdata_exit; @@ -355,8 +416,6 @@ pdata_exit: pdata->exit(dev); disable_resources: ahci_platform_disable_resources(hpriv); -free_clk: - ahci_put_clks(hpriv); return rc; } @@ -370,7 +429,6 @@ static void ahci_host_stop(struct ata_host *host) pdata->exit(dev); ahci_platform_disable_resources(hpriv); - ahci_put_clks(hpriv); } #ifdef CONFIG_PM_SLEEP diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index b674b01ce9bc..b80c51c20f76 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -20,7 +20,14 @@ struct device; struct ata_port_info; struct ahci_host_priv; +struct platform_device; +/* + * Note ahci_platform_data is deprecated, it is only kept around for use + * by the old da850 and spear13xx ahci code. + * New drivers should instead declare their own platform_driver struct, and + * use ahci_platform* functions in their own probe, suspend and resume methods. + */ struct ahci_platform_data { int (*init)(struct device *dev, void __iomem *addr); void (*exit)(struct device *dev); @@ -35,5 +42,12 @@ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); +struct ahci_host_priv *ahci_platform_get_resources( + struct platform_device *pdev); +int ahci_platform_init_host(struct platform_device *pdev, + struct ahci_host_priv *hpriv, + const struct ata_port_info *pi_template, + unsigned int force_port_map, + unsigned int mask_port_map); #endif /* _AHCI_PLATFORM_H */ -- cgit v1.2.3 From 648cb6fd83b97f0f772db783a280af300fa9f2bc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 22 Feb 2014 16:53:35 +0100 Subject: ahci-platform: "Library-ise" suspend / resume functionality Split suspend / resume code into host suspend / resume functionality and resource enable / disabling phases, and export the new suspend_ / resume_host functions. tj: Minor comment formatting updates. Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 97 ++++++++++++++++++++++++++++++++++++------- include/linux/ahci_platform.h | 5 +++ 2 files changed, 87 insertions(+), 15 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 19e9eaafb1f2..01f7bbe65572 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -432,14 +432,23 @@ static void ahci_host_stop(struct ata_host *host) } #ifdef CONFIG_PM_SLEEP -static int ahci_suspend(struct device *dev) +/** + * ahci_platform_suspend_host - Suspend an ahci-platform host + * @dev: device pointer for the host + * + * This function does all the usual steps needed to suspend an + * ahci-platform host, note any necessary resources (ie clks, phy, etc.) + * must be disabled after calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_suspend_host(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; void __iomem *mmio = hpriv->mmio; u32 ctl; - int rc; if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { dev_err(dev, "firmware update required for suspend/resume\n"); @@ -456,7 +465,58 @@ static int ahci_suspend(struct device *dev) writel(ctl, mmio + HOST_CTL); readl(mmio + HOST_CTL); /* flush */ - rc = ata_host_suspend(host, PMSG_SUSPEND); + return ata_host_suspend(host, PMSG_SUSPEND); +} +EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); + +/** + * ahci_platform_resume_host - Resume an ahci-platform host + * @dev: device pointer for the host + * + * This function does all the usual steps needed to resume an ahci-platform + * host, note any necessary resources (ie clks, phy, etc.) must be + * initialized / enabled before calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_resume_host(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + int rc; + + if (dev->power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + } + + ata_host_resume(host); + + return 0; +} +EXPORT_SYMBOL_GPL(ahci_platform_resume_host); + +/** + * ahci_platform_suspend - Suspend an ahci-platform device + * @dev: the platform device to suspend + * + * This function suspends the host associated with the device, followed by + * disabling all the resources of the device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_suspend(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; + + rc = ahci_platform_suspend_host(dev); if (rc) return rc; @@ -467,8 +527,19 @@ static int ahci_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(ahci_platform_suspend); -static int ahci_resume(struct device *dev) +/** + * ahci_platform_resume - Resume an ahci-platform device + * @dev: the platform device to resume + * + * This function enables all the resources of the device followed by + * resuming the host associated with the device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_resume(struct device *dev) { struct ahci_platform_data *pdata = dev_get_platdata(dev); struct ata_host *host = dev_get_drvdata(dev); @@ -485,15 +556,9 @@ static int ahci_resume(struct device *dev) goto disable_resources; } - if (dev->power.power_state.event == PM_EVENT_SUSPEND) { - rc = ahci_reset_controller(host); - if (rc) - goto disable_resources; - - ahci_init_controller(host); - } - - ata_host_resume(host); + rc = ahci_platform_resume_host(dev); + if (rc) + goto disable_resources; return 0; @@ -502,9 +567,11 @@ disable_resources: return rc; } +EXPORT_SYMBOL_GPL(ahci_platform_resume); #endif -static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); +static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, + ahci_platform_resume); static const struct of_device_id ahci_of_match[] = { { .compatible = "snps,spear-ahci", }, diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index b80c51c20f76..542f2686eb1d 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -50,4 +50,9 @@ int ahci_platform_init_host(struct platform_device *pdev, unsigned int force_port_map, unsigned int mask_port_map); +int ahci_platform_suspend_host(struct device *dev); +int ahci_platform_resume_host(struct device *dev); +int ahci_platform_suspend(struct device *dev); +int ahci_platform_resume(struct device *dev); + #endif /* _AHCI_PLATFORM_H */ -- cgit v1.2.3 From c431147184c05849f1d7e14f8fd6254e1026319d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Sat, 22 Feb 2014 16:53:38 +0100 Subject: ata: ahci_platform: Add DT compatible for Synopsis DWC AHCI controller Add compatible string "snps,dwc-ahci", which should be used for Synopsis Designware SATA cores. e.g. on TI OMAP5 and DRA7 platforms. Signed-off-by: Roger Quadros Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 01f7bbe65572..968e7d94af17 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -577,6 +577,7 @@ static const struct of_device_id ahci_of_match[] = { { .compatible = "snps,spear-ahci", }, { .compatible = "snps,exynos5440-ahci", }, { .compatible = "ibm,476gtr-ahci", }, + { .compatible = "snps,dwc-ahci", }, {}, }; MODULE_DEVICE_TABLE(of, ahci_of_match); -- cgit v1.2.3 From 21b5faeec229d4f70a7f60a7b0b065c98198f491 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Sat, 22 Feb 2014 16:53:40 +0100 Subject: ata: ahci_platform: Manage SATA PHY Some platforms have a PHY hooked up to the SATA controller. The PHY needs to be initialized and powered up for SATA to work. We do that using the PHY framework. tj: Minor comment formatting updates. CC: Balaji T K Signed-off-by: Roger Quadros Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci.h | 2 ++ drivers/ata/ahci_platform.c | 47 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index bf8100c51142..3ab7ac9bb6a7 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -37,6 +37,7 @@ #include #include +#include #include /* Enclosure Management Control */ @@ -325,6 +326,7 @@ struct ahci_host_priv { u32 em_msg_type; /* EM message type */ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ struct regulator *target_pwr; /* Optional */ + struct phy *phy; /* If platform uses phy */ void *plat_data; /* Other platform data */ /* * Optional ahci_start_engine override, if not set this gets set to the diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 968e7d94af17..243dde36cd98 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "ahci.h" static void ahci_host_stop(struct ata_host *host); @@ -140,6 +141,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); * following order: * 1) Regulator * 2) Clocks (through ahci_platform_enable_clks) + * 3) Phy * * If resource enabling fails at any point the previous enabled resources * are disabled in reverse order. @@ -161,8 +163,23 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) if (rc) goto disable_regulator; + if (hpriv->phy) { + rc = phy_init(hpriv->phy); + if (rc) + goto disable_clks; + + rc = phy_power_on(hpriv->phy); + if (rc) { + phy_exit(hpriv->phy); + goto disable_clks; + } + } + return 0; +disable_clks: + ahci_platform_disable_clks(hpriv); + disable_regulator: if (hpriv->target_pwr) regulator_disable(hpriv->target_pwr); @@ -176,11 +193,17 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); * * This function disables all ahci_platform managed resources in the * following order: - * 1) Clocks (through ahci_platform_disable_clks) - * 2) Regulator + * 1) Phy + * 2) Clocks (through ahci_platform_disable_clks) + * 3) Regulator */ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) { + if (hpriv->phy) { + phy_power_off(hpriv->phy); + phy_exit(hpriv->phy); + } + ahci_platform_disable_clks(hpriv); if (hpriv->target_pwr) @@ -208,6 +231,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res) * 2) regulator for controlling the targets power (optional) * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, * or for non devicetree enabled platforms a single clock + * 4) phy (optional) * * RETURNS: * The allocated ahci_host_priv on success, otherwise an ERR_PTR value @@ -266,6 +290,25 @@ struct ahci_host_priv *ahci_platform_get_resources( hpriv->clks[i] = clk; } + hpriv->phy = devm_phy_get(dev, "sata-phy"); + if (IS_ERR(hpriv->phy)) { + rc = PTR_ERR(hpriv->phy); + switch (rc) { + case -ENODEV: + case -ENOSYS: + /* continue normally */ + hpriv->phy = NULL; + break; + + case -EPROBE_DEFER: + goto err_out; + + default: + dev_err(dev, "couldn't get sata-phy\n"); + goto err_out; + } + } + devres_remove_group(dev, NULL); return hpriv; -- cgit v1.2.3 From e708e46edac8ab2f31e7ee991aa3c5b87638e658 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Sat, 22 Feb 2014 16:53:41 +0100 Subject: ata: ahci_platform: runtime resume the device before use On OMAP platforms the device needs to be runtime resumed before it can be accessed. The OMAP HWMOD framework takes care of enabling the module and its resources based on the device's runtime PM state. In this patch we runtime resume during .probe() and runtime suspend after .remove(). We also update the runtime PM state during .resume(). CC: Balaji T K Signed-off-by: Roger Quadros Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci.h | 1 + drivers/ata/ahci_platform.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 3ab7ac9bb6a7..51af275b3388 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -324,6 +324,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 */ + bool got_runtime_pm; /* Did we do pm_runtime_get? */ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ struct regulator *target_pwr; /* Optional */ struct phy *phy; /* If platform uses phy */ diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 243dde36cd98..fc32863d6a5a 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "ahci.h" static void ahci_host_stop(struct ata_host *host); @@ -216,6 +217,11 @@ static void ahci_platform_put_resources(struct device *dev, void *res) struct ahci_host_priv *hpriv = res; int c; + if (hpriv->got_runtime_pm) { + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + } + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) clk_put(hpriv->clks[c]); } @@ -309,6 +315,10 @@ struct ahci_host_priv *ahci_platform_get_resources( } } + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + hpriv->got_runtime_pm = true; + devres_remove_group(dev, NULL); return hpriv; @@ -603,6 +613,11 @@ int ahci_platform_resume(struct device *dev) if (rc) goto disable_resources; + /* We resumed so update PM runtime state */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; disable_resources: -- cgit v1.2.3 From f1df8641e27b7edb978bdc7aaf50c235bc9e8be9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 22 Feb 2014 17:22:53 +0100 Subject: ahci_platform: Drop support for ahci-strict platform device type I've done a grep over the entire kernel tree and nothing is using this (anymore?). Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index fc32863d6a5a..d3d2bad516b4 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -31,7 +31,6 @@ static void ahci_host_stop(struct ata_host *host); enum ahci_type { AHCI, /* standard platform ahci */ IMX53_AHCI, /* ahci on i.mx53 */ - STRICT_AHCI, /* delayed DMA engine start */ }; static struct platform_device_id ahci_devtype[] = { @@ -41,9 +40,6 @@ static struct platform_device_id ahci_devtype[] = { }, { .name = "imx53-ahci", .driver_data = IMX53_AHCI, - }, { - .name = "strict-ahci", - .driver_data = STRICT_AHCI, }, { /* sentinel */ } @@ -75,13 +71,6 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_platform_retry_srst_ops, }, - [STRICT_AHCI] = { - AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_platform_ops, - }, }; static struct scsi_host_template ahci_platform_sht = { -- cgit v1.2.3 From c093e1d36e317c5ac2dc788f407119259fc260fe Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 22 Feb 2014 17:22:54 +0100 Subject: ahci_platform: Drop support for imx53-ahci platform device type Since the 3.13 release the ahci_imx driver has proper devicetree enabled support for ahci on imx53 and that is used instead of the old board file created imx53-ahci platform device. Note this patch also complete drops the id-table, an id-table is not needed for a single id platform driver, the name field in the driver struct suffices. And the code already has an explicit "MODULE_ALIAS("platform:ahci");" so the id-table is not needed for that either. Cc: Marek Vasut Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 46 ++++++--------------------------------------- 1 file changed, 6 insertions(+), 40 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index d3d2bad516b4..8fab4bf9042a 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -28,49 +28,17 @@ static void ahci_host_stop(struct ata_host *host); -enum ahci_type { - AHCI, /* standard platform ahci */ - IMX53_AHCI, /* ahci on i.mx53 */ -}; - -static struct platform_device_id ahci_devtype[] = { - { - .name = "ahci", - .driver_data = AHCI, - }, { - .name = "imx53-ahci", - .driver_data = IMX53_AHCI, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, ahci_devtype); - struct ata_port_operations ahci_platform_ops = { .inherits = &ahci_ops, .host_stop = ahci_host_stop, }; EXPORT_SYMBOL_GPL(ahci_platform_ops); -static struct ata_port_operations ahci_platform_retry_srst_ops = { - .inherits = &ahci_pmp_retry_srst_ops, - .host_stop = ahci_host_stop, -}; - -static const struct ata_port_info ahci_port_info[] = { - /* by features */ - [AHCI] = { - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_platform_ops, - }, - [IMX53_AHCI] = { - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_platform_retry_srst_ops, - }, +static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, }; static struct scsi_host_template ahci_platform_sht = { @@ -416,7 +384,6 @@ static int ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_platform_data *pdata = dev_get_platdata(dev); - const struct platform_device_id *id = platform_get_device_id(pdev); const struct ata_port_info *pi_template; struct ahci_host_priv *hpriv; int rc; @@ -444,7 +411,7 @@ static int ahci_probe(struct platform_device *pdev) if (pdata && pdata->ata_port_info) pi_template = pdata->ata_port_info; else - pi_template = &ahci_port_info[id ? id->driver_data : 0]; + pi_template = &ahci_port_info; rc = ahci_platform_init_host(pdev, hpriv, pi_template, pdata ? pdata->force_port_map : 0, @@ -638,7 +605,6 @@ static struct platform_driver ahci_driver = { .of_match_table = ahci_of_match, .pm = &ahci_pm_ops, }, - .id_table = ahci_devtype, }; module_platform_driver(ahci_driver); -- cgit v1.2.3 From 6ef95e87763fd69889655f4f14e499377a54d066 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 22 Feb 2014 17:22:55 +0100 Subject: ahci_platform: Drop unused ahci_platform_data members These members are not used anywhere, and in the future we want ahci_platform_data to go away entirely so there is no reason to keep these around. Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 10 +--------- include/linux/ahci_platform.h | 3 --- 2 files changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 8fab4bf9042a..db24d2a08051 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -384,7 +384,6 @@ static int ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_platform_data *pdata = dev_get_platdata(dev); - const struct ata_port_info *pi_template; struct ahci_host_priv *hpriv; int rc; @@ -408,14 +407,7 @@ static int ahci_probe(struct platform_device *pdev) goto disable_resources; } - if (pdata && pdata->ata_port_info) - pi_template = pdata->ata_port_info; - else - pi_template = &ahci_port_info; - - rc = ahci_platform_init_host(pdev, hpriv, pi_template, - pdata ? pdata->force_port_map : 0, - pdata ? pdata->mask_port_map : 0); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, 0, 0); if (rc) goto pdata_exit; diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 542f2686eb1d..1f16d502600c 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -33,9 +33,6 @@ struct ahci_platform_data { void (*exit)(struct device *dev); int (*suspend)(struct device *dev); int (*resume)(struct device *dev); - const struct ata_port_info *ata_port_info; - unsigned int force_port_map; - unsigned int mask_port_map; }; int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); -- cgit v1.2.3 From 5434b203156ef245b7847128c446c5b54f12a6d4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 14 Mar 2014 18:22:09 +0100 Subject: ata: ahci_platform: fix devm_ioremap_resource() return value checking devm_ioremap_resource() returns a pointer to the remapped memory or an ERR_PTR() encoded error code on failure. Fix the check inside ahci_platform_get_resources() accordingly. Also while at it remove a needless line break. Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index db24d2a08051..70fbf664a73c 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -199,8 +199,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res) * RETURNS: * The allocated ahci_host_priv on success, otherwise an ERR_PTR value */ -struct ahci_host_priv *ahci_platform_get_resources( - struct platform_device *pdev) +struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; @@ -219,8 +218,9 @@ struct ahci_host_priv *ahci_platform_get_resources( hpriv->mmio = devm_ioremap_resource(dev, platform_get_resource(pdev, IORESOURCE_MEM, 0)); - if (!hpriv->mmio) { + if (IS_ERR(hpriv->mmio)) { dev_err(dev, "no mmio space\n"); + rc = PTR_ERR(hpriv->mmio); goto err_out; } -- cgit v1.2.3 From 83b03fd67b9b3fa3795871169f3c08c35b3d6ea8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 25 Mar 2014 19:51:38 +0100 Subject: ata: ahci_platform: fix ahci_platform_data->suspend method handling Looking at ST SPEAr1340 AHCI code (the only user of the deprecated pdata->suspend and pdata->resume) it is obvious the we should return after calling pdata->suspend() only if the function have returned non-zero return value. The code has been broken since commit 1e70c2 ("ata/ahci_platform: Add clock framework support"). Fix it. Cc: Viresh Kumar Cc: Shiraz Hashim Acked-by: Hans de Goede Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tejun Heo --- drivers/ata/ahci_platform.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 70fbf664a73c..7bd6adf54b3e 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -521,12 +521,19 @@ int ahci_platform_suspend(struct device *dev) if (rc) return rc; - if (pdata && pdata->suspend) - return pdata->suspend(dev); + if (pdata && pdata->suspend) { + rc = pdata->suspend(dev); + if (rc) + goto resume_host; + } ahci_platform_disable_resources(hpriv); return 0; + +resume_host: + ahci_platform_resume_host(dev); + return rc; } EXPORT_SYMBOL_GPL(ahci_platform_suspend); -- cgit v1.2.3 From fd990556f0fa25446c6bfa9cf4c9e49d387d4472 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 25 Mar 2014 19:51:39 +0100 Subject: ata: move library code from ahci_platform.c to libahci_platform.c Move AHCI platform library code from ahci_platform.c to libahci_platform.c and fix dependencies for ahci_st, ahci_imx and ahci_sunxi drivers. Acked-by: Hans de Goede Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tejun Heo --- drivers/ata/Kconfig | 7 +- drivers/ata/Makefile | 10 +- drivers/ata/ahci_platform.c | 515 --------------------------------------- drivers/ata/libahci_platform.c | 541 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 549 insertions(+), 524 deletions(-) create mode 100644 drivers/ata/libahci_platform.c (limited to 'drivers/ata/ahci_platform.c') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 9f3e3269c09a..12767ad098e0 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -99,7 +99,6 @@ config SATA_AHCI_PLATFORM config AHCI_ST tristate "ST AHCI SATA support" - depends on SATA_AHCI_PLATFORM depends on ARCH_STI help This option enables support for ST AHCI SATA controller. @@ -108,7 +107,7 @@ config AHCI_ST config AHCI_IMX tristate "Freescale i.MX AHCI SATA support" - depends on SATA_AHCI_PLATFORM && MFD_SYSCON + depends on MFD_SYSCON help This option enables support for the Freescale i.MX SoC's onboard AHCI SATA. @@ -117,7 +116,7 @@ config AHCI_IMX config AHCI_SUNXI tristate "Allwinner sunxi AHCI SATA support" - depends on ARCH_SUNXI && SATA_AHCI_PLATFORM + depends on ARCH_SUNXI help This option enables support for the Allwinner sunxi SoC's onboard AHCI SATA. @@ -126,7 +125,7 @@ config AHCI_SUNXI config AHCI_XGENE tristate "APM X-Gene 6.0Gbps AHCI SATA host controller support" - depends on SATA_AHCI_PLATFORM && (ARM64 || COMPILE_TEST) + depends on ARM64 || COMPILE_TEST select PHY_XGENE help This option enables support for APM X-Gene SoC SATA host controller. diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 095d4610d5c3..5fa79abc6a53 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -4,16 +4,16 @@ obj-$(CONFIG_ATA) += libata.o # non-SFF interface obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o -obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o +obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o obj-$(CONFIG_SATA_FSL) += sata_fsl.o obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o -obj-$(CONFIG_AHCI_IMX) += ahci_imx.o -obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o -obj-$(CONFIG_AHCI_ST) += ahci_st.o -obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o +obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_ST) += ahci_st.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o libahci.o libahci_platform.o # SFF w/ custom DMA obj-$(CONFIG_PDC_ADMA) += pdc_adma.o diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 7bd6adf54b3e..ef67e79944f9 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -12,28 +12,15 @@ * any later version. */ -#include #include -#include #include #include -#include #include #include #include #include -#include -#include #include "ahci.h" -static void ahci_host_stop(struct ata_host *host); - -struct ata_port_operations ahci_platform_ops = { - .inherits = &ahci_ops, - .host_stop = ahci_host_stop, -}; -EXPORT_SYMBOL_GPL(ahci_platform_ops); - static const struct ata_port_info ahci_port_info = { .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, @@ -41,345 +28,6 @@ static const struct ata_port_info ahci_port_info = { .port_ops = &ahci_platform_ops, }; -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); - -/** - * ahci_platform_enable_resources - Enable platform resources - * @hpriv: host private area to store config values - * - * This function enables all ahci_platform managed resources in the - * following order: - * 1) Regulator - * 2) Clocks (through ahci_platform_enable_clks) - * 3) Phy - * - * If resource enabling fails at any point the previous enabled resources - * are disabled in reverse order. - * - * RETURNS: - * 0 on success otherwise a negative error code - */ -int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) -{ - int rc; - - if (hpriv->target_pwr) { - rc = regulator_enable(hpriv->target_pwr); - if (rc) - return rc; - } - - rc = ahci_platform_enable_clks(hpriv); - if (rc) - goto disable_regulator; - - if (hpriv->phy) { - rc = phy_init(hpriv->phy); - if (rc) - goto disable_clks; - - rc = phy_power_on(hpriv->phy); - if (rc) { - phy_exit(hpriv->phy); - goto disable_clks; - } - } - - return 0; - -disable_clks: - ahci_platform_disable_clks(hpriv); - -disable_regulator: - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); - return rc; -} -EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); - -/** - * ahci_platform_disable_resources - Disable platform resources - * @hpriv: host private area to store config values - * - * This function disables all ahci_platform managed resources in the - * following order: - * 1) Phy - * 2) Clocks (through ahci_platform_disable_clks) - * 3) Regulator - */ -void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) -{ - if (hpriv->phy) { - phy_power_off(hpriv->phy); - phy_exit(hpriv->phy); - } - - ahci_platform_disable_clks(hpriv); - - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); -} -EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); - -static void ahci_platform_put_resources(struct device *dev, void *res) -{ - struct ahci_host_priv *hpriv = res; - int c; - - if (hpriv->got_runtime_pm) { - pm_runtime_put_sync(dev); - pm_runtime_disable(dev); - } - - for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) - clk_put(hpriv->clks[c]); -} - -/** - * ahci_platform_get_resources - Get platform resources - * @pdev: platform device to get resources for - * - * This function allocates an ahci_host_priv struct, and gets the following - * resources, storing a reference to them inside the returned struct: - * - * 1) mmio registers (IORESOURCE_MEM 0, mandatory) - * 2) regulator for controlling the targets power (optional) - * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, - * or for non devicetree enabled platforms a single clock - * 4) phy (optional) - * - * RETURNS: - * The allocated ahci_host_priv on success, otherwise an ERR_PTR value - */ -struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct ahci_host_priv *hpriv; - struct clk *clk; - int i, rc = -ENOMEM; - - if (!devres_open_group(dev, NULL, GFP_KERNEL)) - return ERR_PTR(-ENOMEM); - - hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), - GFP_KERNEL); - if (!hpriv) - goto err_out; - - devres_add(dev, hpriv); - - hpriv->mmio = devm_ioremap_resource(dev, - platform_get_resource(pdev, IORESOURCE_MEM, 0)); - if (IS_ERR(hpriv->mmio)) { - dev_err(dev, "no mmio space\n"); - rc = PTR_ERR(hpriv->mmio); - goto err_out; - } - - hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); - if (IS_ERR(hpriv->target_pwr)) { - rc = PTR_ERR(hpriv->target_pwr); - if (rc == -EPROBE_DEFER) - goto err_out; - hpriv->target_pwr = NULL; - } - - 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 err_out; - break; - } - hpriv->clks[i] = clk; - } - - hpriv->phy = devm_phy_get(dev, "sata-phy"); - if (IS_ERR(hpriv->phy)) { - rc = PTR_ERR(hpriv->phy); - switch (rc) { - case -ENODEV: - case -ENOSYS: - /* continue normally */ - hpriv->phy = NULL; - break; - - case -EPROBE_DEFER: - goto err_out; - - default: - dev_err(dev, "couldn't get sata-phy\n"); - goto err_out; - } - } - - pm_runtime_enable(dev); - pm_runtime_get_sync(dev); - hpriv->got_runtime_pm = true; - - devres_remove_group(dev, NULL); - return hpriv; - -err_out: - devres_release_group(dev, NULL); - return ERR_PTR(rc); -} -EXPORT_SYMBOL_GPL(ahci_platform_get_resources); - -/** - * ahci_platform_init_host - Bring up an ahci-platform host - * @pdev: platform device pointer for the host - * @hpriv: ahci-host private data for the host - * @pi_template: template for the ata_port_info to use - * @force_port_map: param passed to ahci_save_initial_config - * @mask_port_map: param passed to ahci_save_initial_config - * - * This function does all the usual steps needed to bring up an - * ahci-platform host, note any necessary resources (ie clks, phy, etc.) - * must be initialized / enabled before calling this. - * - * RETURNS: - * 0 on success otherwise a negative error code - */ -int ahci_platform_init_host(struct platform_device *pdev, - struct ahci_host_priv *hpriv, - const struct ata_port_info *pi_template, - unsigned int force_port_map, - unsigned int mask_port_map) -{ - struct device *dev = &pdev->dev; - struct ata_port_info pi = *pi_template; - const struct ata_port_info *ppi[] = { &pi, NULL }; - struct ata_host *host; - int i, irq, n_ports, rc; - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "no irq\n"); - return -EINVAL; - } - - /* prepare host */ - hpriv->flags |= (unsigned long)pi.private_data; - - ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); - - if (hpriv->cap & HOST_CAP_NCQ) - pi.flags |= ATA_FLAG_NCQ; - - if (hpriv->cap & HOST_CAP_PMP) - pi.flags |= ATA_FLAG_PMP; - - ahci_set_em_messages(hpriv, &pi); - - /* CAP.NP sometimes indicate the index of the last enabled - * port, at other times, that of the last possible port, so - * determining the maximum port number requires looking at - * both CAP.NP and port_map. - */ - n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); - - host = ata_host_alloc_pinfo(dev, ppi, n_ports); - if (!host) - return -ENOMEM; - - host->private_data = hpriv; - - if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) - host->flags |= ATA_HOST_PARALLEL_SCAN; - else - dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); - - if (pi.flags & ATA_FLAG_EM) - ahci_reset_em(host); - - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - - ata_port_desc(ap, "mmio %pR", - platform_get_resource(pdev, IORESOURCE_MEM, 0)); - ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); - - /* set enclosure management message type */ - if (ap->flags & ATA_FLAG_EM) - ap->em_message_type = hpriv->em_msg_type; - - /* disabled/not-implemented port */ - if (!(hpriv->port_map & (1 << i))) - ap->ops = &ata_dummy_port_ops; - } - - rc = ahci_reset_controller(host); - if (rc) - return rc; - - ahci_init_controller(host); - ahci_print_info(host, "platform"); - - return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, - &ahci_platform_sht); -} -EXPORT_SYMBOL_GPL(ahci_platform_init_host); - static int ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -420,169 +68,6 @@ disable_resources: return rc; } -static void ahci_host_stop(struct ata_host *host) -{ - struct device *dev = host->dev; - struct ahci_platform_data *pdata = dev_get_platdata(dev); - struct ahci_host_priv *hpriv = host->private_data; - - if (pdata && pdata->exit) - pdata->exit(dev); - - ahci_platform_disable_resources(hpriv); -} - -#ifdef CONFIG_PM_SLEEP -/** - * ahci_platform_suspend_host - Suspend an ahci-platform host - * @dev: device pointer for the host - * - * This function does all the usual steps needed to suspend an - * ahci-platform host, note any necessary resources (ie clks, phy, etc.) - * must be disabled after calling this. - * - * RETURNS: - * 0 on success otherwise a negative error code - */ -int ahci_platform_suspend_host(struct device *dev) -{ - struct ata_host *host = dev_get_drvdata(dev); - struct ahci_host_priv *hpriv = host->private_data; - void __iomem *mmio = hpriv->mmio; - u32 ctl; - - if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { - dev_err(dev, "firmware update required for suspend/resume\n"); - return -EIO; - } - - /* - * AHCI spec rev1.1 section 8.3.3: - * Software must disable interrupts prior to requesting a - * transition of the HBA to D3 state. - */ - ctl = readl(mmio + HOST_CTL); - ctl &= ~HOST_IRQ_EN; - writel(ctl, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - - return ata_host_suspend(host, PMSG_SUSPEND); -} -EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); - -/** - * ahci_platform_resume_host - Resume an ahci-platform host - * @dev: device pointer for the host - * - * This function does all the usual steps needed to resume an ahci-platform - * host, note any necessary resources (ie clks, phy, etc.) must be - * initialized / enabled before calling this. - * - * RETURNS: - * 0 on success otherwise a negative error code - */ -int ahci_platform_resume_host(struct device *dev) -{ - struct ata_host *host = dev_get_drvdata(dev); - int rc; - - if (dev->power.power_state.event == PM_EVENT_SUSPEND) { - rc = ahci_reset_controller(host); - if (rc) - return rc; - - ahci_init_controller(host); - } - - ata_host_resume(host); - - return 0; -} -EXPORT_SYMBOL_GPL(ahci_platform_resume_host); - -/** - * ahci_platform_suspend - Suspend an ahci-platform device - * @dev: the platform device to suspend - * - * This function suspends the host associated with the device, followed by - * disabling all the resources of the device. - * - * RETURNS: - * 0 on success otherwise a negative error code - */ -int ahci_platform_suspend(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; - - rc = ahci_platform_suspend_host(dev); - if (rc) - return rc; - - if (pdata && pdata->suspend) { - rc = pdata->suspend(dev); - if (rc) - goto resume_host; - } - - ahci_platform_disable_resources(hpriv); - - return 0; - -resume_host: - ahci_platform_resume_host(dev); - return rc; -} -EXPORT_SYMBOL_GPL(ahci_platform_suspend); - -/** - * ahci_platform_resume - Resume an ahci-platform device - * @dev: the platform device to resume - * - * This function enables all the resources of the device followed by - * resuming the host associated with the device. - * - * RETURNS: - * 0 on success otherwise a negative error code - */ -int ahci_platform_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; - - rc = ahci_platform_enable_resources(hpriv); - if (rc) - return rc; - - if (pdata && pdata->resume) { - rc = pdata->resume(dev); - if (rc) - goto disable_resources; - } - - rc = ahci_platform_resume_host(dev); - if (rc) - goto disable_resources; - - /* We resumed so update PM runtime state */ - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - return 0; - -disable_resources: - ahci_platform_disable_resources(hpriv); - - return rc; -} -EXPORT_SYMBOL_GPL(ahci_platform_resume); -#endif - static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, ahci_platform_resume); diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c new file mode 100644 index 000000000000..7cb3a85719c0 --- /dev/null +++ b/drivers/ata/libahci_platform.c @@ -0,0 +1,541 @@ +/* + * AHCI SATA platform library + * + * Copyright 2004-2005 Red Hat, Inc. + * Jeff Garzik + * Copyright 2010 MontaVista Software, LLC. + * Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ahci.h" + +static void ahci_host_stop(struct ata_host *host); + +struct ata_port_operations ahci_platform_ops = { + .inherits = &ahci_ops, + .host_stop = ahci_host_stop, +}; +EXPORT_SYMBOL_GPL(ahci_platform_ops); + +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); + +/** + * ahci_platform_enable_resources - Enable platform resources + * @hpriv: host private area to store config values + * + * This function enables all ahci_platform managed resources in the + * following order: + * 1) Regulator + * 2) Clocks (through ahci_platform_enable_clks) + * 3) Phy + * + * If resource enabling fails at any point the previous enabled resources + * are disabled in reverse order. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) +{ + int rc; + + if (hpriv->target_pwr) { + rc = regulator_enable(hpriv->target_pwr); + if (rc) + return rc; + } + + rc = ahci_platform_enable_clks(hpriv); + if (rc) + goto disable_regulator; + + if (hpriv->phy) { + rc = phy_init(hpriv->phy); + if (rc) + goto disable_clks; + + rc = phy_power_on(hpriv->phy); + if (rc) { + phy_exit(hpriv->phy); + goto disable_clks; + } + } + + return 0; + +disable_clks: + ahci_platform_disable_clks(hpriv); + +disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); + +/** + * ahci_platform_disable_resources - Disable platform resources + * @hpriv: host private area to store config values + * + * This function disables all ahci_platform managed resources in the + * following order: + * 1) Phy + * 2) Clocks (through ahci_platform_disable_clks) + * 3) Regulator + */ +void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) +{ + if (hpriv->phy) { + phy_power_off(hpriv->phy); + phy_exit(hpriv->phy); + } + + ahci_platform_disable_clks(hpriv); + + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); +} +EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); + +static void ahci_platform_put_resources(struct device *dev, void *res) +{ + struct ahci_host_priv *hpriv = res; + int c; + + if (hpriv->got_runtime_pm) { + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + } + + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); +} + +/** + * ahci_platform_get_resources - Get platform resources + * @pdev: platform device to get resources for + * + * This function allocates an ahci_host_priv struct, and gets the following + * resources, storing a reference to them inside the returned struct: + * + * 1) mmio registers (IORESOURCE_MEM 0, mandatory) + * 2) regulator for controlling the targets power (optional) + * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, + * or for non devicetree enabled platforms a single clock + * 4) phy (optional) + * + * RETURNS: + * The allocated ahci_host_priv on success, otherwise an ERR_PTR value + */ +struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct clk *clk; + int i, rc = -ENOMEM; + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); + + hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), + GFP_KERNEL); + if (!hpriv) + goto err_out; + + devres_add(dev, hpriv); + + hpriv->mmio = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (IS_ERR(hpriv->mmio)) { + dev_err(dev, "no mmio space\n"); + rc = PTR_ERR(hpriv->mmio); + goto err_out; + } + + hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); + if (IS_ERR(hpriv->target_pwr)) { + rc = PTR_ERR(hpriv->target_pwr); + if (rc == -EPROBE_DEFER) + goto err_out; + hpriv->target_pwr = NULL; + } + + 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 err_out; + break; + } + hpriv->clks[i] = clk; + } + + hpriv->phy = devm_phy_get(dev, "sata-phy"); + if (IS_ERR(hpriv->phy)) { + rc = PTR_ERR(hpriv->phy); + switch (rc) { + case -ENODEV: + case -ENOSYS: + /* continue normally */ + hpriv->phy = NULL; + break; + + case -EPROBE_DEFER: + goto err_out; + + default: + dev_err(dev, "couldn't get sata-phy\n"); + goto err_out; + } + } + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + hpriv->got_runtime_pm = true; + + devres_remove_group(dev, NULL); + return hpriv; + +err_out: + devres_release_group(dev, NULL); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(ahci_platform_get_resources); + +/** + * ahci_platform_init_host - Bring up an ahci-platform host + * @pdev: platform device pointer for the host + * @hpriv: ahci-host private data for the host + * @pi_template: template for the ata_port_info to use + * @force_port_map: param passed to ahci_save_initial_config + * @mask_port_map: param passed to ahci_save_initial_config + * + * This function does all the usual steps needed to bring up an + * ahci-platform host, note any necessary resources (ie clks, phy, etc.) + * must be initialized / enabled before calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_init_host(struct platform_device *pdev, + struct ahci_host_priv *hpriv, + const struct ata_port_info *pi_template, + unsigned int force_port_map, + unsigned int mask_port_map) +{ + struct device *dev = &pdev->dev; + struct ata_port_info pi = *pi_template; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ata_host *host; + int i, irq, n_ports, rc; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "no irq\n"); + return -EINVAL; + } + + /* prepare host */ + hpriv->flags |= (unsigned long)pi.private_data; + + ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); + + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + + if (hpriv->cap & HOST_CAP_PMP) + pi.flags |= ATA_FLAG_PMP; + + ahci_set_em_messages(hpriv, &pi); + + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) + return -ENOMEM; + + host->private_data = hpriv; + + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) + host->flags |= ATA_HOST_PARALLEL_SCAN; + else + dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); + + if (pi.flags & ATA_FLAG_EM) + ahci_reset_em(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + ata_port_desc(ap, "mmio %pR", + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + } + + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + + return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, + &ahci_platform_sht); +} +EXPORT_SYMBOL_GPL(ahci_platform_init_host); + +static void ahci_host_stop(struct ata_host *host) +{ + struct device *dev = host->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + + if (pdata && pdata->exit) + pdata->exit(dev); + + ahci_platform_disable_resources(hpriv); +} + +#ifdef CONFIG_PM_SLEEP +/** + * ahci_platform_suspend_host - Suspend an ahci-platform host + * @dev: device pointer for the host + * + * This function does all the usual steps needed to suspend an + * ahci-platform host, note any necessary resources (ie clks, phy, etc.) + * must be disabled after calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_suspend_host(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 ctl; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(dev, "firmware update required for suspend/resume\n"); + return -EIO; + } + + /* + * AHCI spec rev1.1 section 8.3.3: + * Software must disable interrupts prior to requesting a + * transition of the HBA to D3 state. + */ + ctl = readl(mmio + HOST_CTL); + ctl &= ~HOST_IRQ_EN; + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + + return ata_host_suspend(host, PMSG_SUSPEND); +} +EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); + +/** + * ahci_platform_resume_host - Resume an ahci-platform host + * @dev: device pointer for the host + * + * This function does all the usual steps needed to resume an ahci-platform + * host, note any necessary resources (ie clks, phy, etc.) must be + * initialized / enabled before calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_resume_host(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + int rc; + + if (dev->power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + } + + ata_host_resume(host); + + return 0; +} +EXPORT_SYMBOL_GPL(ahci_platform_resume_host); + +/** + * ahci_platform_suspend - Suspend an ahci-platform device + * @dev: the platform device to suspend + * + * This function suspends the host associated with the device, followed by + * disabling all the resources of the device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_suspend(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; + + rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + + if (pdata && pdata->suspend) { + rc = pdata->suspend(dev); + if (rc) + goto resume_host; + } + + ahci_platform_disable_resources(hpriv); + + return 0; + +resume_host: + ahci_platform_resume_host(dev); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +/** + * ahci_platform_resume - Resume an ahci-platform device + * @dev: the platform device to resume + * + * This function enables all the resources of the device followed by + * resuming the host associated with the device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_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; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); + if (rc) + goto disable_resources; + } + + rc = ahci_platform_resume_host(dev); + if (rc) + goto disable_resources; + + /* We resumed so update PM runtime state */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_resume); +#endif + +MODULE_DESCRIPTION("AHCI SATA platform library"); +MODULE_AUTHOR("Anton Vorontsov "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3