diff options
Diffstat (limited to 'target/linux/sunxi/patches-3.14/190-ahci-libahci-changes.patch')
-rw-r--r-- | target/linux/sunxi/patches-3.14/190-ahci-libahci-changes.patch | 872 |
1 files changed, 0 insertions, 872 deletions
diff --git a/target/linux/sunxi/patches-3.14/190-ahci-libahci-changes.patch b/target/linux/sunxi/patches-3.14/190-ahci-libahci-changes.patch deleted file mode 100644 index c4d918d5da..0000000000 --- a/target/linux/sunxi/patches-3.14/190-ahci-libahci-changes.patch +++ /dev/null @@ -1,872 +0,0 @@ -From 3b5db9d024f173c30ef4060c31bb8e9fbd194cc1 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Mon, 2 Dec 2013 16:13:32 +0100 -Subject: [PATCH] libahci: Allow drivers to override start_engine - -Allwinner A10 and A20 ARM SoCs have an AHCI sata controller which needs a -special register to be poked before starting the DMA engine. - -This register gets reset on an ahci_stop_engine call, so there is no other -place then ahci_start_engine where this poking can be done. - -This commit allows drivers to override ahci_start_engine behavior for use by -the Allwinner AHCI driver (and potentially other drivers in the future). - -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - drivers/ata/ahci.c | 6 ++++-- - drivers/ata/ahci.h | 6 ++++++ - drivers/ata/libahci.c | 26 +++++++++++++++++++------- - drivers/ata/sata_highbank.c | 3 ++- - 4 files changed, 31 insertions(+), 10 deletions(-) - ---- a/drivers/ata/ahci.c -+++ b/drivers/ata/ahci.c -@@ -606,6 +606,7 @@ static int ahci_vt8251_hardreset(struct - unsigned long deadline) - { - struct ata_port *ap = link->ap; -+ struct ahci_host_priv *hpriv = ap->host->private_data; - bool online; - int rc; - -@@ -616,7 +617,7 @@ static int ahci_vt8251_hardreset(struct - rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), - deadline, &online, NULL); - -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - - DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); - -@@ -631,6 +632,7 @@ static int ahci_p5wdh_hardreset(struct a - { - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; -+ struct ahci_host_priv *hpriv = ap->host->private_data; - u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; - struct ata_taskfile tf; - bool online; -@@ -646,7 +648,7 @@ static int ahci_p5wdh_hardreset(struct a - rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), - deadline, &online, NULL); - -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - - /* The pseudo configuration device on SIMG4726 attached to - * ASUS P5W-DH Deluxe doesn't send signature FIS after ---- a/drivers/ata/ahci.h -+++ b/drivers/ata/ahci.h -@@ -37,6 +37,7 @@ - - #include <linux/clk.h> - #include <linux/libata.h> -+#include <linux/regulator/consumer.h> - - /* Enclosure Management Control */ - #define EM_CTRL_MSG_TYPE 0x000f0000 -@@ -51,6 +52,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, -@@ -322,8 +324,15 @@ 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 */ -+ struct regulator *target_pwr; /* Optional */ - void *plat_data; /* Other platform data */ -+ /* -+ * Optional ahci_start_engine override, if not set this gets set to the -+ * default ahci_start_engine during ahci_save_initial_config, this can -+ * be overridden anytime before the host is activated. -+ */ -+ void (*start_engine)(struct ata_port *ap); - }; - - extern int ahci_ignore_sss; ---- a/drivers/ata/libahci.c -+++ b/drivers/ata/libahci.c -@@ -394,6 +394,9 @@ static ssize_t ahci_show_em_supported(st - * - * If inconsistent, config values are fixed up by this function. - * -+ * If it is not set already this function sets hpriv->start_engine to -+ * ahci_start_engine. -+ * - * LOCKING: - * None. - */ -@@ -500,6 +503,9 @@ void ahci_save_initial_config(struct dev - hpriv->cap = cap; - hpriv->cap2 = cap2; - hpriv->port_map = port_map; -+ -+ if (!hpriv->start_engine) -+ hpriv->start_engine = ahci_start_engine; - } - EXPORT_SYMBOL_GPL(ahci_save_initial_config); - -@@ -766,7 +772,7 @@ static void ahci_start_port(struct ata_p - - /* enable DMA */ - if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - - /* turn on LEDs */ - if (ap->flags & ATA_FLAG_EM) { -@@ -1234,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap - - /* restart engine */ - out_restart: -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - return rc; - } - EXPORT_SYMBOL_GPL(ahci_kick_engine); -@@ -1426,6 +1432,7 @@ static int ahci_hardreset(struct ata_lin - const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; -+ struct ahci_host_priv *hpriv = ap->host->private_data; - u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; - struct ata_taskfile tf; - bool online; -@@ -1443,7 +1450,7 @@ static int ahci_hardreset(struct ata_lin - rc = sata_link_hardreset(link, timing, deadline, &online, - ahci_check_ready); - -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - - if (online) - *class = ahci_dev_classify(ap); -@@ -2007,10 +2014,12 @@ static void ahci_thaw(struct ata_port *a - - void ahci_error_handler(struct ata_port *ap) - { -+ struct ahci_host_priv *hpriv = ap->host->private_data; -+ - if (!(ap->pflags & ATA_PFLAG_FROZEN)) { - /* restart engine */ - ahci_stop_engine(ap); -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - } - - sata_pmp_error_handler(ap); -@@ -2031,6 +2040,7 @@ static void ahci_post_internal_cmd(struc - - static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) - { -+ struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - struct ata_device *dev = ap->link.device; - u32 devslp, dm, dito, mdat, deto; -@@ -2094,7 +2104,7 @@ static void ahci_set_aggressive_devslp(s - PORT_DEVSLP_ADSE); - writel(devslp, port_mmio + PORT_DEVSLP); - -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - - /* enable device sleep feature for the drive */ - err_mask = ata_dev_set_feature(dev, -@@ -2106,6 +2116,7 @@ static void ahci_set_aggressive_devslp(s - - static void ahci_enable_fbs(struct ata_port *ap) - { -+ struct ahci_host_priv *hpriv = ap->host->private_data; - struct ahci_port_priv *pp = ap->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 fbs; -@@ -2134,11 +2145,12 @@ static void ahci_enable_fbs(struct ata_p - } else - dev_err(ap->host->dev, "Failed to enable FBS\n"); - -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - } - - static void ahci_disable_fbs(struct ata_port *ap) - { -+ struct ahci_host_priv *hpriv = ap->host->private_data; - struct ahci_port_priv *pp = ap->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 fbs; -@@ -2166,7 +2178,7 @@ static void ahci_disable_fbs(struct ata_ - pp->fbs_enabled = false; - } - -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - } - - static void ahci_pmp_attach(struct ata_port *ap) ---- a/drivers/ata/sata_highbank.c -+++ b/drivers/ata/sata_highbank.c -@@ -403,6 +403,7 @@ static int ahci_highbank_hardreset(struc - static const unsigned long timing[] = { 5, 100, 500}; - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; -+ struct ahci_host_priv *hpriv = ap->host->private_data; - u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; - struct ata_taskfile tf; - bool online; -@@ -431,7 +432,7 @@ static int ahci_highbank_hardreset(struc - break; - } while (!online && retry--); - -- ahci_start_engine(ap); -+ hpriv->start_engine(ap); - - if (online) - *class = ahci_dev_classify(ap); ---- a/Documentation/devicetree/bindings/ata/ahci-platform.txt -+++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt -@@ -10,6 +10,8 @@ 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 { ---- a/drivers/ata/ahci_platform.c -+++ b/drivers/ata/ahci_platform.c -@@ -87,78 +87,252 @@ static struct scsi_host_template ahci_pl - AHCI_SHT("ahci_platform"), - }; - --static int ahci_probe(struct platform_device *pdev) -+/** -+ * 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. -+ * -+ * LOCKING: -+ * None. -+ * -+ * RETURNS: -+ * 0 on success otherwise a negative error code -+ */ -+int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) - { -- 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; -- int irq; -- int n_ports; -- int i; -- int rc; -+ int c, rc; - -- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- if (!mem) { -- dev_err(dev, "no mmio space\n"); -- return -EINVAL; -+ 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; - -- irq = platform_get_irq(pdev, 0); -- if (irq <= 0) { -- dev_err(dev, "no irq\n"); -- return -EINVAL; -- } -+disable_unprepare_clk: -+ while (--c >= 0) -+ clk_disable_unprepare(hpriv->clks[c]); -+ return rc; -+} -+EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); - -- if (pdata && pdata->ata_port_info) -- pi = *pdata->ata_port_info; -+/** -+ * 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). -+ * -+ * LOCKING: -+ * None. -+ */ -+void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) -+{ -+ int c; - -- hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); -- if (!hpriv) { -- dev_err(dev, "can't alloc ahci_host_priv\n"); -- return -ENOMEM; -+ 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) -+ * -+ * If resource enabling fails at any point the previous enabled -+ * resources are disabled in reverse order. -+ * -+ * LOCKING: -+ * None. -+ * -+ * 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; - } - -- hpriv->flags |= (unsigned long)pi.private_data; -+ 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 -+ * -+ * LOCKING: -+ * None. -+ */ -+void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) -+{ -+ ahci_platform_disable_clks(hpriv); - -- hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); -+ 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; -+ -+ 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 -+ * -+ * LOCKING: -+ * None. -+ * -+ * 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 (!hpriv->mmio) { -- dev_err(dev, "can't map %pR\n", mem); -- return -ENOMEM; -+ dev_err(dev, "no mmio space\n"); -+ goto err_out; - } - -- 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; -+ 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; - } - -- /* -- * 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_unprepare_clk; -- } -+ 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. -+ * -+ * LOCKING: -+ * None. -+ * -+ * 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; - -@@ -175,10 +349,8 @@ static int ahci_probe(struct platform_de - 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; - -@@ -193,7 +365,8 @@ static int ahci_probe(struct platform_de - 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 */ -@@ -207,13 +380,53 @@ static int ahci_probe(struct platform_de - - 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; - -@@ -221,12 +434,8 @@ static int ahci_probe(struct platform_de - pdata_exit: - if (pdata && pdata->exit) - pdata->exit(dev); --disable_unprepare_clk: -- if (!IS_ERR(hpriv->clk)) -- clk_disable_unprepare(hpriv->clk); --free_clk: -- if (!IS_ERR(hpriv->clk)) -- clk_put(hpriv->clk); -+disable_resources: -+ ahci_platform_disable_resources(hpriv); - return rc; - } - -@@ -239,21 +448,30 @@ static void ahci_host_stop(struct ata_ho - if (pdata && pdata->exit) - pdata->exit(dev); - -- if (!IS_ERR(hpriv->clk)) { -- clk_disable_unprepare(hpriv->clk); -- clk_put(hpriv->clk); -- } -+ ahci_platform_disable_resources(hpriv); - } - - #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. -+ * -+ * LOCKING: -+ * None. -+ * -+ * 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"); -@@ -270,61 +488,122 @@ static int ahci_suspend(struct device *d - 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. -+ * -+ * LOCKING: -+ * None. -+ * -+ * 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. -+ * -+ * LOCKING: -+ * None. -+ * -+ * 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) - return pdata->suspend(dev); - -- if (!IS_ERR(hpriv->clk)) -- clk_disable_unprepare(hpriv->clk); -+ ahci_platform_disable_resources(hpriv); - - 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. -+ * -+ * LOCKING: -+ * None. -+ * -+ * 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; - -- if (!IS_ERR(hpriv->clk)) { -- rc = clk_prepare_enable(hpriv->clk); -- if (rc) { -- dev_err(dev, "clock prepare enable failed"); -- return rc; -- } -- } -+ rc = ahci_platform_enable_resources(hpriv); -+ if (rc) -+ return rc; - - if (pdata && pdata->resume) { - rc = pdata->resume(dev); - if (rc) -- goto disable_unprepare_clk; -- } -- -- if (dev->power.power_state.event == PM_EVENT_SUSPEND) { -- rc = ahci_reset_controller(host); -- if (rc) -- goto disable_unprepare_clk; -- -- ahci_init_controller(host); -+ goto disable_resources; - } - -- ata_host_resume(host); -+ rc = ahci_platform_resume_host(dev); -+ if (rc) -+ goto disable_resources; - - return 0; - --disable_unprepare_clk: -- if (!IS_ERR(hpriv->clk)) -- clk_disable_unprepare(hpriv->clk); -+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_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", }, ---- a/include/linux/ahci_platform.h -+++ b/include/linux/ahci_platform.h -@@ -19,7 +19,15 @@ - - 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); -@@ -30,4 +38,21 @@ 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); -+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); -+ -+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 */ |