ARM: ahci: enable ahci sata on imx6q

Signed-off-by: RichardZhu <richard.zhu@linaro.org>
diff --git a/Documentation/devicetree/bindings/ata/fsl-imx-sata.txt b/Documentation/devicetree/bindings/ata/fsl-imx-sata.txt
new file mode 100644
index 0000000..c0e6986
--- /dev/null
+++ b/Documentation/devicetree/bindings/ata/fsl-imx-sata.txt
@@ -0,0 +1,18 @@
+* Freescale imx AHCI 1.5/3.0 Gb/s SATA nodes
+
+AHCI SATA nodes are defined to describe on-chip Serial ATA controllers.
+
+Required properties:
+- compatible        : compatible list, contains 1 entries, first is
+		 "fsl,CHIP-ahci", where CHIP is the processor
+		 (imx6q, etc.).
+- reg               : <registers mapping>
+- interrupts        : <interrupt mapping for SATA IRQ>
+
+Example:
+	ahci@0x02200000 { /* AHCI SATA */
+		compatible = "fsl,imx6q-ahci";
+		reg = <0x02200000 0x4000>;
+		interrupts = <0 39 0x04>;
+		status = "disabled";
+	};
diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts
index e6d2cb7..4ee2af9 100644
--- a/arch/arm/boot/dts/imx6q-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6q-sabrelite.dts
@@ -82,6 +82,10 @@
 			ipu = <0>;
 			di = <0>;
 		};
+
+		ahci@0x02200000 { /* AHCI SATA */
+			status = "okay";
+		};
 	};
 
 	leds {
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index c82bdee..d450515 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -601,5 +601,12 @@
 			interrupts = <0 7 0x04 0 8 0x04>;
 			revision = <4>;
 		};
+
+		ahci@0x02200000 { /* AHCI SATA */
+			compatible = "fsl,imx6q-ahci";
+			reg = <0x02200000 0x4000>;
+			interrupts = <0 39 0x04>;
+			status = "disabled";
+		};
 	};
 };
diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c
index 1029a3b..a54f8c9 100644
--- a/arch/arm/mach-imx/clock-imx6q.c
+++ b/arch/arm/mach-imx/clock-imx6q.c
@@ -24,6 +24,23 @@
 #include <mach/common.h>
 #include <mach/hardware.h>
 
+/* IOMUXC */
+#define MXC_IOMUXC_BASE		IMX_IO_ADDRESS(MX6Q_IOMUXC_BASE_ADDR)
+#define IOMUXC_GPR0		(MXC_IOMUXC_BASE + 0x00)
+#define IOMUXC_GPR1		(MXC_IOMUXC_BASE + 0x04)
+#define IOMUXC_GPR2		(MXC_IOMUXC_BASE + 0x08)
+#define IOMUXC_GPR3		(MXC_IOMUXC_BASE + 0x0C)
+#define IOMUXC_GPR4		(MXC_IOMUXC_BASE + 0x10)
+#define IOMUXC_GPR5		(MXC_IOMUXC_BASE + 0x14)
+#define IOMUXC_GPR6		(MXC_IOMUXC_BASE + 0x18)
+#define IOMUXC_GPR7		(MXC_IOMUXC_BASE + 0x1C)
+#define IOMUXC_GPR8		(MXC_IOMUXC_BASE + 0x20)
+#define IOMUXC_GPR9		(MXC_IOMUXC_BASE + 0x24)
+#define IOMUXC_GPR10		(MXC_IOMUXC_BASE + 0x28)
+#define IOMUXC_GPR11		(MXC_IOMUXC_BASE + 0x2C)
+#define IOMUXC_GPR12		(MXC_IOMUXC_BASE + 0x30)
+#define IOMUXC_GPR13		(MXC_IOMUXC_BASE + 0x34)
+
 #define PLL_BASE		IMX_IO_ADDRESS(MX6Q_ANATOP_BASE_ADDR)
 #define PLL1_SYS		(PLL_BASE + 0x000)
 #define PLL2_BUS		(PLL_BASE + 0x030)
@@ -1856,8 +1873,27 @@
 
 static int sata_clk_enable(struct clk *clk)
 {
+	int timeout = 0x100000;
 	u32 val;
 
+	/* Clear Power Down and Enable PLLs */
+	val = readl_relaxed(PLL8_ENET);
+	val &= ~BM_PLL_POWER_DOWN;
+	writel_relaxed(val, PLL8_ENET);
+
+	val = readl_relaxed(PLL8_ENET);
+	val |= BM_PLL_ENABLE;
+	writel_relaxed(val, PLL8_ENET);
+
+	/* Wait for PLL to lock */
+	while (!(readl_relaxed(PLL8_ENET) & BM_PLL_LOCK) && --timeout)
+		cpu_relax();
+
+	/* Disable the bypass */
+	val = readl_relaxed(PLL8_ENET);
+	val &= ~BM_PLL_BYPASS;
+	writel_relaxed(val, PLL8_ENET);
+
 	val = readl_relaxed(PLL8_ENET);
 	val |= BM_PLL_ENET_EN_SATA;
 	writel_relaxed(val, PLL8_ENET);
@@ -1876,14 +1912,62 @@
 	writel_relaxed(val, PLL8_ENET);
 }
 
-static struct clk sata_clk = {
-	__INIT_CLK_DEBUG(sata_clk)
-	.enable_reg = CCGR5,
-	.enable_shift = CG2,
-	.enable = sata_clk_enable,
-	.disable = sata_clk_disable,
-	.parent = &ipg_clk,
-	.secondary = &pll8_enet,
+static struct clk sata_clk[] = {
+	{
+		__INIT_CLK_DEBUG(sata_clk)
+		.enable_reg = CCGR5,
+		.enable_shift = CG2,
+		.enable = sata_clk_enable,
+		.disable = sata_clk_disable,
+		.parent = &ipg_clk,
+		.secondary = &sata_clk[1],
+	},
+	{
+		.parent = &mmdc_ch0_axi_clk,
+	},
+};
+
+static int ahci_phy_clk_enable(struct clk *clk)
+{
+	u32 val;
+
+	/* Set PHY Paremeters, two steps to configure the GPR13,
+	 * one write for rest of parameters, mask of first write is 0x07FFFFFD,
+	 * and the other one write for setting the mpll_clk_off_b
+	 *.rx_eq_val_0(iomuxc_gpr13[26:24]),
+	 *.los_lvl(iomuxc_gpr13[23:19]),
+	 *.rx_dpll_mode_0(iomuxc_gpr13[18:16]),
+	 *.mpll_ss_en(iomuxc_gpr13[14]),
+	 *.tx_atten_0(iomuxc_gpr13[13:11]),
+	 *.tx_boost_0(iomuxc_gpr13[10:7]),
+	 *.tx_lvl(iomuxc_gpr13[6:2]),
+	 *.mpll_ck_off(iomuxc_gpr13[1]),
+	 *.tx_edgerate_0(iomuxc_gpr13[0]),
+	 */
+	val = readl_relaxed(IOMUXC_GPR13);
+	writel_relaxed(((val & ~0x07FFFFFD) | 0x0593A044), IOMUXC_GPR13);
+
+	/* enable SATA_PHY PLL */
+	val = readl_relaxed(IOMUXC_GPR13);
+	writel_relaxed(((val & ~0x2) | 0x2), IOMUXC_GPR13);
+
+
+	return 0;
+}
+
+static void ahci_phy_clk_disable(struct clk *clk)
+{
+	/* disable SATA_PHY PLL */
+	writel_relaxed((readl_relaxed(IOMUXC_GPR13) & ~0x2), IOMUXC_GPR13);
+}
+
+static struct clk ahci_phy_clk = {
+	.enable = ahci_phy_clk_enable,
+	.disable = ahci_phy_clk_disable,
+};
+
+static struct clk ahci_dma_clk = {
+	.parent = &ahb_clk,
 };
 
 #define _REGISTER_CLOCK(d, n, c) \
@@ -1931,7 +2015,10 @@
 	_REGISTER_CLOCK("208c000.pwm", NULL, pwm4_clk),
 	_REGISTER_CLOCK(NULL, "gpmi_io_clk", gpmi_io_clk),
 	_REGISTER_CLOCK(NULL, "usboh3_clk", usboh3_clk),
-	_REGISTER_CLOCK(NULL, "sata_clk", sata_clk),
+	_REGISTER_CLOCK("imx6q-ahci", "ahci", sata_clk[0]),
+	_REGISTER_CLOCK("imx6q-ahci", "ahci_phy", ahci_phy_clk),
+	_REGISTER_CLOCK("imx6q-ahci", "ahci_dma", ahci_dma_clk),
+	_REGISTER_CLOCK(NULL, "cpu", arm_clk),
 	_REGISTER_CLOCK(NULL, "ipu1_clk", ipu1_clk),
 	_REGISTER_CLOCK(NULL, "ipu2_clk", ipu2_clk),
 	_REGISTER_CLOCK(NULL, "ipu1_di0_clk", ipu1_di0_clk),
diff --git a/arch/arm/mach-imx/devices-imx6q.h b/arch/arm/mach-imx/devices-imx6q.h
new file mode 100644
index 0000000..e41268d
--- /dev/null
+++ b/arch/arm/mach-imx/devices-imx6q.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/ahci_platform.h>
+
+extern struct ahci_platform_data imx_sata_pdata;
+
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 3f46730..b33b9f2 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -31,6 +31,7 @@
 #include <mach/iomux-mx6q.h>
 #include <mach/ipu-v3.h>
 #include <mach/mxc_vpu.h>
+#include "devices-imx6q.h"
 
 static iomux_v3_cfg_t imx6q_sabrelite_pads[] = {
 	/* DISPLAY */
@@ -122,6 +123,7 @@
 	OF_DEV_AUXDATA("fsl,ipuv3", MX6Q_IPU1_BASE_ADDR, "imx-ipuv3.0", &ipuv3_pdata),
 	OF_DEV_AUXDATA("fsl,ipuv3", MX6Q_IPU2_BASE_ADDR, "imx-ipuv3.1", &ipuv3_pdata),
 	OF_DEV_AUXDATA("fsl,vpu", MX6Q_VPU_BASE_ADDR, "mxc_vpu.0", &vpu_pdata),
+	OF_DEV_AUXDATA("fsl,imx6q-ahci", MX6Q_SATA_BASE_ADDR, "imx6q-ahci", &imx_sata_pdata),
 };
 
 static void __init imx6q_init_machine(void)
diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig
index f716125..2d12de3 100644
--- a/arch/arm/plat-mxc/devices/Kconfig
+++ b/arch/arm/plat-mxc/devices/Kconfig
@@ -100,7 +100,7 @@
 
 config IMX_HAVE_PLATFORM_AHCI
 	bool
-	default y if ARCH_MX53
+	default y if ARCH_MX53 || SOC_IMX6Q
 
 config IMX_HAVE_PLATFORM_IMX_VPU
 	bool
diff --git a/arch/arm/plat-mxc/devices/platform-ahci-imx.c b/arch/arm/plat-mxc/devices/platform-ahci-imx.c
index d8a56ae..c9267e1 100644
--- a/arch/arm/plat-mxc/devices/platform-ahci-imx.c
+++ b/arch/arm/plat-mxc/devices/platform-ahci-imx.c
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <asm/sizes.h>
@@ -42,8 +43,12 @@
 enum {
 	HOST_CAP = 0x00,
 	HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */
-	HOST_PORTS_IMPL	= 0x0c,
-	HOST_TIMER1MS = 0xe0, /* Timer 1-ms */
+	HOST_PORTS_IMPL	= 0x0C,
+	HOST_TIMER1MS = 0xE0, /* Timer 1-ms */
+
+	PORT_SATA_SR = 0x128, /*  Port0 PHY Control */
+	PORT_PHY_CTL = 0x178, /*  Port0 PHY Control */
+	PORT_PHY_CTL_PDDQ_LOC = 0x100000,
 };
 
 static struct clk *sata_clk, *sata_ref_clk;
@@ -52,7 +57,7 @@
 static int imx_sata_init(struct device *dev, void __iomem *addr)
 {
 	u32 tmpdata;
-	int ret = 0;
+	int ret = 0, iterations = 200;
 	struct clk *clk;
 
 	sata_clk = clk_get(dev, "ahci");
@@ -100,6 +105,23 @@
 	if (!(readl(addr + HOST_PORTS_IMPL) & 0x1))
 		writel((readl(addr + HOST_PORTS_IMPL) | 0x1),
 			addr + HOST_PORTS_IMPL);
+	/* Release resources when there is no device on the port */
+	do {
+		if ((readl(addr + PORT_SATA_SR) & 0xF) == 0)
+			usleep_range(2000, 3000);
+		else
+			break;
+
+		if (iterations == 0) {
+			/*  Enter into PDDQ mode, save power */
+			pr_info("No sata disk, enter into PDDQ mode.\n");
+			tmpdata = readl(addr + PORT_PHY_CTL);
+			writel(tmpdata | PORT_PHY_CTL_PDDQ_LOC,
+					addr + PORT_PHY_CTL);
+			ret = -ENODEV;
+			goto release_sata_ref_clk;
+		}
+	} while (iterations-- > 0);
 
 	return 0;
 
@@ -145,12 +167,14 @@
 			pdata, sizeof(*pdata),  DMA_BIT_MASK(32));
 }
 
+struct ahci_platform_data imx_sata_pdata = {
+	.init = imx_sata_init,
+	.exit = imx_sata_exit,
+};
+
+#ifdef CONFIG_SOC_IMX53
 struct platform_device *__init imx53_add_ahci_imx(void)
 {
-	struct ahci_platform_data pdata = {
-		.init = imx_sata_init,
-		.exit = imx_sata_exit,
-	};
-
 	return imx_add_ahci_imx(&imx53_ahci_imx_data, &pdata);
 }
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/mx6q.h b/arch/arm/plat-mxc/include/mach/mx6q.h
index 9292e2e..67e3abd 100644
--- a/arch/arm/plat-mxc/include/mach/mx6q.h
+++ b/arch/arm/plat-mxc/include/mach/mx6q.h
@@ -43,4 +43,9 @@
 #define MX6Q_IPU1_BASE_ADDR         	0x02400000
 #define MX6Q_IPU2_BASE_ADDR         	0x02800000
 
+/*
+ * AHCI SATA
+ */
+#define MX6Q_SATA_BASE_ADDR		0x02200000
+
 #endif	/* __MACH_MX6Q_H__ */
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 43b8758..045b76d 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -21,11 +21,15 @@
 #include <linux/platform_device.h>
 #include <linux/libata.h>
 #include <linux/ahci_platform.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include "ahci.h"
 
 enum ahci_type {
 	AHCI,		/* standard platform ahci */
 	IMX53_AHCI,	/* ahci on i.mx53 */
+	IMX6Q_AHCI,	/* ahci on i.mx6q */
 };
 
 static struct platform_device_id ahci_devtype[] = {
@@ -36,6 +40,10 @@
 		.name = "imx53-ahci",
 		.driver_data = IMX53_AHCI,
 	}, {
+	}, {
+		.name = "imx6q-ahci",
+		.driver_data = IMX53_AHCI,
+	}, {
 		/* sentinel */
 	}
 };
@@ -62,12 +70,23 @@
 	AHCI_SHT("ahci_platform"),
 };
 
+static const struct of_device_id ahci_of_match[] = {
+	{ .compatible = "calxeda,hb-ahci",  .data = &ahci_devtype[AHCI],},
+	{ .compatible = "fsl,imx6q-ahci", .data = &ahci_devtype[IMX6Q_AHCI],},
+	{},
+};
+MODULE_DEVICE_TABLE(of, ahci_of_match);
+
 static int __init ahci_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct ahci_platform_data *pdata = dev_get_platdata(dev);
+	const struct of_device_id *of_id =
+			of_match_device(ahci_of_match, &pdev->dev);
+	const struct platform_device_id	*id_entry = of_id->data;
 	const struct platform_device_id *id = platform_get_device_id(pdev);
-	struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0];
+	struct ata_port_info pi = ahci_port_info[id ? id->driver_data : \
+		id_entry->driver_data];
 	const struct ata_port_info *ppi[] = { &pi, NULL };
 	struct ahci_host_priv *hpriv;
 	struct ata_host *host;
@@ -202,12 +221,6 @@
 	return 0;
 }
 
-static const struct of_device_id ahci_of_match[] = {
-	{ .compatible = "calxeda,hb-ahci", },
-	{},
-};
-MODULE_DEVICE_TABLE(of, ahci_of_match);
-
 static struct platform_driver ahci_driver = {
 	.remove = __devexit_p(ahci_remove),
 	.driver = {