aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Zhu <richard.zhu@linaro.org>2012-01-09 16:16:13 +0800
committerEric Miao <eric.miao@linaro.org>2012-01-11 22:02:11 +0800
commit23158c9927aeb6adbf6bae51cb6ec43e9e81f95c (patch)
treed0d861f30cb0739714ac1ec4339743e5efe6c010
parent925e814d9b46346af50dc3c7cf7a6106be5c9723 (diff)
downloadlinux-linaro-topic/lt-3.2-imx6-sata.tar.gz
ARM: ahci: enable ahci sata on imx6qtopic/lt-3.2-imx6-sata
Signed-off-by: RichardZhu <richard.zhu@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/ata/fsl-imx-sata.txt18
-rw-r--r--arch/arm/boot/dts/imx6q-sabrelite.dts4
-rw-r--r--arch/arm/boot/dts/imx6q.dtsi7
-rw-r--r--arch/arm/mach-imx/clock-imx6q.c105
-rw-r--r--arch/arm/mach-imx/devices-imx6q.h24
-rw-r--r--arch/arm/mach-imx/mach-imx6q.c2
-rw-r--r--arch/arm/plat-mxc/devices/Kconfig2
-rw-r--r--arch/arm/plat-mxc/devices/platform-ahci-imx.c40
-rw-r--r--arch/arm/plat-mxc/include/mach/mx6q.h5
-rw-r--r--drivers/ata/ahci_platform.c27
10 files changed, 209 insertions, 25 deletions
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 00000000000..c0e69862522
--- /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 e6d2cb73243..4ee2af9275c 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 c82bdee711c..d450515f48d 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 1029a3b2f6a..a54f8c9ad14 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 struct clk pcie_clk = {
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 @@ static void sata_clk_disable(struct clk *clk)
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 @@ static struct clk_lookup lookups[] = {
_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 00000000000..e41268d650e
--- /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 3f46730ac06..b33b9f2807e 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 @@ static const struct of_dev_auxdata imx6q_auxdata_lookup[] __initconst = {
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 f7161253297..2d12de3d211 100644
--- a/arch/arm/plat-mxc/devices/Kconfig
+++ b/arch/arm/plat-mxc/devices/Kconfig
@@ -100,7 +100,7 @@ config IMX_HAVE_PLATFORM_IMX_IIM
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 d8a56aee521..c9267e1be00 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 @@ const struct imx_ahci_imx_data imx53_ahci_imx_data __initconst =
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 struct clk *sata_clk, *sata_ref_clk;
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 @@ static int imx_sata_init(struct device *dev, void __iomem *addr)
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 @@ struct platform_device *__init imx_add_ahci_imx(
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 9292e2e4ecc..67e3abd8930 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 43b875810d1..045b76ded35 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 @@ static struct platform_device_id ahci_devtype[] = {
.name = "imx53-ahci",
.driver_data = IMX53_AHCI,
}, {
+ }, {
+ .name = "imx6q-ahci",
+ .driver_data = IMX53_AHCI,
+ }, {
/* sentinel */
}
};
@@ -62,12 +70,23 @@ static struct scsi_host_template ahci_platform_sht = {
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 @@ static int __devexit ahci_remove(struct platform_device *pdev)
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 = {