diff options
Diffstat (limited to 'target/linux/sunxi/patches-3.13/160-5-ahci-platform-devs-with-more-than-1-clock.patch')
-rw-r--r-- | target/linux/sunxi/patches-3.13/160-5-ahci-platform-devs-with-more-than-1-clock.patch | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-3.13/160-5-ahci-platform-devs-with-more-than-1-clock.patch b/target/linux/sunxi/patches-3.13/160-5-ahci-platform-devs-with-more-than-1-clock.patch new file mode 100644 index 0000000..11d9a2c --- /dev/null +++ b/target/linux/sunxi/patches-3.13/160-5-ahci-platform-devs-with-more-than-1-clock.patch @@ -0,0 +1,255 @@ +From bdad8090b5efdb24fa9db00d8e2891927b68dcec Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Thu, 16 Jan 2014 14:32:35 +0100 +Subject: [PATCH] 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. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + .../devicetree/bindings/ata/ahci-platform.txt | 1 + + drivers/ata/ahci.h | 3 +- + drivers/ata/ahci_platform.c | 119 ++++++++++++++++----- + include/linux/ahci_platform.h | 4 + + 4 files changed, 99 insertions(+), 28 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 89de156..3ced07d 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 64d1a99d..c12862b 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 4b231ba..609975d 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -87,6 +87,66 @@ struct ata_port_operations ahci_platform_ops = { + 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. ++ * ++ * LOCKING: ++ * None. ++ * ++ * 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). ++ * ++ * LOCKING: ++ * None. ++ */ ++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; +@@ -97,6 +157,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; +@@ -131,17 +192,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 +@@ -222,11 +297,9 @@ static int ahci_probe(struct platform_device *pdev) + 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; + } + +@@ -239,10 +312,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 +@@ -277,8 +348,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; + } +@@ -290,13 +360,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); +@@ -317,8 +383,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 73a2500..769d065 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 */ +-- +1.8.5.5 + |