From 23158c9927aeb6adbf6bae51cb6ec43e9e81f95c Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Mon, 9 Jan 2012 16:16:13 +0800 Subject: ARM: ahci: enable ahci sata on imx6q Signed-off-by: RichardZhu --- .../devicetree/bindings/ata/fsl-imx-sata.txt | 18 ++++ arch/arm/boot/dts/imx6q-sabrelite.dts | 4 + arch/arm/boot/dts/imx6q.dtsi | 7 ++ arch/arm/mach-imx/clock-imx6q.c | 105 +++++++++++++++++++-- arch/arm/mach-imx/devices-imx6q.h | 24 +++++ arch/arm/mach-imx/mach-imx6q.c | 2 + arch/arm/plat-mxc/devices/Kconfig | 2 +- arch/arm/plat-mxc/devices/platform-ahci-imx.c | 40 ++++++-- arch/arm/plat-mxc/include/mach/mx6q.h | 5 + drivers/ata/ahci_platform.c | 27 ++++-- 10 files changed, 209 insertions(+), 25 deletions(-) create mode 100644 Documentation/devicetree/bindings/ata/fsl-imx-sata.txt create mode 100644 arch/arm/mach-imx/devices-imx6q.h 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 : +- interrupts : + +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 #include +/* 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 + +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 #include #include +#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 #include #include +#include #include #include #include @@ -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 #include #include +#include +#include +#include #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[] = { @@ -35,6 +39,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 = { -- cgit v1.2.3