diff options
Diffstat (limited to 'target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch')
-rw-r--r-- | target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch | 1157 |
1 files changed, 1157 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch b/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch new file mode 100644 index 0000000..bd45720 --- /dev/null +++ b/target/linux/ipq806x/patches/0124-ata-move-library-code-from-ahci_platform.c-to-libahc.patch @@ -0,0 +1,1157 @@ +From 04800db1047afbf6701379435bff1a6fa64215f7 Mon Sep 17 00:00:00 2001 +From: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Date: Tue, 25 Mar 2014 19:51:39 +0100 +Subject: [PATCH 124/182] 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 <hdegoede@redhat.com> +Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Tejun Heo <tj@kernel.org> + +Conflicts: + drivers/ata/Kconfig + drivers/ata/Makefile +--- + drivers/ata/Kconfig | 2 +- + drivers/ata/Makefile | 4 +- + drivers/ata/ahci_platform.c | 515 -------------------------------------- + drivers/ata/libahci_platform.c | 541 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 544 insertions(+), 518 deletions(-) + create mode 100644 drivers/ata/libahci_platform.c + +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index 868429a..dc950f3 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -99,7 +99,7 @@ config SATA_AHCI_PLATFORM + + 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. +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index 46518c6..366b743 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -4,13 +4,13 @@ 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_IMX) += ahci_imx.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 7bd6adf..ef67e79 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -12,28 +12,15 @@ + * any later version. + */ + +-#include <linux/clk.h> + #include <linux/kernel.h> +-#include <linux/gfp.h> + #include <linux/module.h> + #include <linux/pm.h> +-#include <linux/interrupt.h> + #include <linux/device.h> + #include <linux/platform_device.h> + #include <linux/libata.h> + #include <linux/ahci_platform.h> +-#include <linux/phy/phy.h> +-#include <linux/pm_runtime.h> + #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 0000000..7cb3a85 +--- /dev/null ++++ b/drivers/ata/libahci_platform.c +@@ -0,0 +1,541 @@ ++/* ++ * AHCI SATA platform library ++ * ++ * Copyright 2004-2005 Red Hat, Inc. ++ * Jeff Garzik <jgarzik@pobox.com> ++ * Copyright 2010 MontaVista Software, LLC. ++ * Anton Vorontsov <avorontsov@ru.mvista.com> ++ * ++ * 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 <linux/clk.h> ++#include <linux/kernel.h> ++#include <linux/gfp.h> ++#include <linux/module.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/libata.h> ++#include <linux/ahci_platform.h> ++#include <linux/phy/phy.h> ++#include <linux/pm_runtime.h> ++#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 <avorontsov@ru.mvista.com>"); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + |