[PATCH] ARM: EXYNOS: Add USB HSIC device

Dongjin Kim tobetter at gmail.com
Mon Aug 6 09:57:40 EDT 2012


This patch support to control USB HSIC of EXYNOS4,
edited based on Samsung's GT-i9100 ICS Opensource Update7.

Change-Id: Ifba33c6a5166abf3644794eee6abe528bd71f521
Signed-off-by: Dongjin Kim <dongjin.kim at agreeyamobility.net>
---
 arch/arm/mach-exynos/common.c                    |    5 +
 arch/arm/mach-exynos/include/mach/regs-pmu.h     |   12 +
 arch/arm/mach-exynos/include/mach/regs-usb-phy.h |   97 +++++
 arch/arm/mach-exynos/setup-usb-phy.c             |  493 ++++++++++++++++------
 drivers/usb/host/Kconfig                         |   14 +
 5 files changed, 501 insertions(+), 120 deletions(-)

diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c
index 4eb39cd..94d58af 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/common.c
@@ -179,6 +179,11 @@ static struct map_desc exynos4_iodesc[] __initdata = {
 		.length		= SZ_4K,
 		.type		= MT_DEVICE,
 	}, {
+		.virtual	= (unsigned long)S5P_VA_GPIO2,
+		.pfn		= __phys_to_pfn(EXYNOS4_PA_GPIO2),
+		.length		= SZ_4K,
+		.type		= MT_DEVICE,
+	}, {
 		.virtual	= (unsigned long)S5P_VA_DMC0,
 		.pfn		= __phys_to_pfn(EXYNOS4_PA_DMC0),
 		.length		= SZ_64K,
diff --git a/arch/arm/mach-exynos/include/mach/regs-pmu.h b/arch/arm/mach-exynos/include/mach/regs-pmu.h
index 0bb21e2..d98c2fe 100644
--- a/arch/arm/mach-exynos/include/mach/regs-pmu.h
+++ b/arch/arm/mach-exynos/include/mach/regs-pmu.h
@@ -185,6 +185,15 @@
 #define S5P_PMU_LCD1_CONF		S5P_PMUREG(0x3CA0)
 
 /* Only for EXYNOS4x12 */
+#define S5P_USB_PHY_CONTROL			S5P_PMUREG(0x0704)
+#define S5P_USB_PHY_ENABLE			(0x1 << 0)
+
+#define S5P_HSIC_1_PHY_CONTROL			S5P_PMUREG(0x0708)
+#define S5P_HSIC_1_PHY_ENABLE			(0x1 << 0)
+
+#define S5P_HSIC_2_PHY_CONTROL			S5P_PMUREG(0x070C)
+#define S5P_HSIC_2_PHY_ENABLE			(0x1 << 0)
+
 #define S5P_ISP_ARM_LOWPWR			S5P_PMUREG(0x1050)
 #define S5P_DIS_IRQ_ISP_ARM_LOCAL_LOWPWR	S5P_PMUREG(0x1054)
 #define S5P_DIS_IRQ_ISP_ARM_CENTRAL_LOWPWR	S5P_PMUREG(0x1058)
@@ -242,6 +251,9 @@
 
 #define EXYNOS5_SYS_WDTRESET					(1 << 20)
 
+#define EXYNOS5_USBDEV_PHY_CONTROL			S5P_PMUREG(0x0704)
+#define EXYNOS5_USBHOST_PHY_CONTROL			S5P_PMUREG(0x0708)
+
 #define EXYNOS5_ARM_CORE0_SYS_PWR_REG				S5P_PMUREG(0x1000)
 #define EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG		S5P_PMUREG(0x1004)
 #define EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG		S5P_PMUREG(0x1008)
diff --git a/arch/arm/mach-exynos/include/mach/regs-usb-phy.h b/arch/arm/mach-exynos/include/mach/regs-usb-phy.h
index 0727773..79021a0 100644
--- a/arch/arm/mach-exynos/include/mach/regs-usb-phy.h
+++ b/arch/arm/mach-exynos/include/mach/regs-usb-phy.h
@@ -43,6 +43,43 @@
 #define EXYNOS4210_CLKSEL_12M		(0x2 << 0)
 #define EXYNOS4210_CLKSEL_24M		(0x3 << 0)
 
+#define EXYNOS4210_HSIC1_NORMAL_MASK            (0x3 << 11)
+#define EXYNOS4210_HSIC1_SLEEP                  (1 << 12)
+#define EXYNOS4210_HSIC1_FORCE_SUSPEND          (1 << 11)
+#define EXYNOS4210_HSIC0_NORMAL_MASK            (0x3 << 9)
+#define EXYNOS4210_HSIC0_SLEEP                  (1 << 10)
+#define EXYNOS4210_HSIC0_FORCE_SUSPEND          (1 << 9)
+
+#define EXYNOS4210_HOST_LINK_PORT_SWRST_MASK    (0xf << 6)
+#define EXYNOS4210_HOST_LINK_PORT2_SWRST        (1 << 9)
+#define EXYNOS4210_HOST_LINK_PORT1_SWRST        (1 << 8)
+#define EXYNOS4210_HOST_LINK_PORT0_SWRST        (1 << 7)
+#define EXYNOS4210_HOST_LINK_ALL_SWRST          (1 << 6)
+#define EXYNOS4210_PHY1_SWRST_MASK              (0x7 << 3)
+#define EXYNOS4210_PHY1_HSIC_SWRST              (1 << 5)
+#define EXYNOS4210_PHY1_STD_SWRST               (1 << 4)
+#define EXYNOS4210_PHY1_ALL_SWRST               (1 << 3)
+
+#define EXYNOS4X12_HSIC1_NORMAL_MASK		(0x7 << 12)
+#define EXYNOS4X12_HSIC1_SLEEP			(1 << 14)
+#define EXYNOS4X12_HSIC1_ANALOG_POWERDOWN	(1 << 13)
+#define EXYNOS4X12_HSIC1_FORCE_SUSPEND		(1 << 12)
+#define EXYNOS4X12_HSIC0_NORMAL_MASK		(0x7 << 9)
+#define EXYNOS4X12_HSIC0_SLEEP			(1 << 11)
+#define EXYNOS4X12_HSIC0_ANALOG_POWERDOWN	(1 << 10)
+#define EXYNOS4X12_HSIC0_FORCE_SUSPEND		(1 << 9)
+
+#define EXYNOS4X12_HOST_LINK_PORT_SWRST_MASK	(0xf << 7)
+#define EXYNOS4X12_HOST_LINK_PORT2_SWRST	(1 << 10)
+#define EXYNOS4X12_HOST_LINK_PORT1_SWRST	(1 << 9)
+#define EXYNOS4X12_HOST_LINK_PORT0_SWRST	(1 << 8)
+#define EXYNOS4X12_HOST_LINK_ALL_SWRST		(1 << 7)
+#define EXYNOS4X12_PHY1_SWRST_MASK		(0xf << 3)
+#define EXYNOS4X12_PHY1_HSIC1_SWRST		(1 << 6)
+#define EXYNOS4X12_PHY1_HSIC0_SWRST		(1 << 5)
+#define EXYNOS4X12_PHY1_SWRST			(1 << 4)
+#define EXYNOS4X12_HOST_PHY_SWRST		(1 << 3)
+
 #define EXYNOS4X12_CLKSEL_MASK		(0x7 << 0)
 #define EXYNOS4X12_CLKSEL_9600K		(0x0 << 0)
 #define EXYNOS4X12_CLKSEL_10M		(0x1 << 0)
@@ -71,4 +108,64 @@
 #define EXYNOS4_PHY1CON			EXYNOS4_HSOTG_PHYREG(0x34)
 #define FPENABLEN			(1 << 0)
 
+/* For Exynos5 */
+#define EXYNOS5_PHY_HOST_CTRL0                  EXYNOS4_HSOTG_PHYREG(0x00)
+#define HOST_CTRL0_PHYSWRSTALL                  (0x1 << 31)
+#define HOST_CTRL0_REFCLKSEL(val)               (val << 19)
+#define EXYNOS5_CLKSEL_50M                      (0x7)
+#define EXYNOS5_CLKSEL_24M                      (0x5)
+#define EXYNOS5_CLKSEL_20M                      (0x4)
+#define EXYNOS5_CLKSEL_19200K                   (0x3)
+#define EXYNOS5_CLKSEL_12M                      (0x2)
+#define EXYNOS5_CLKSEL_10M                      (0x1)
+#define EXYNOS5_CLKSEL_9600K                    (0x0)
+#define HOST_CTRL0_CLKSEL_SHIFT                 (16)
+#define HOST_CTRL0_FSEL_MASK                    (0x7 << 16)
+
+#define HOST_CTRL0_COMMONON_N                   (0x1 << 9)
+#define HOST_CTRL0_SIDDQ                        (0x1 << 6)
+#define HOST_CTRL0_FORCESLEEP                   (0x1 << 5)
+#define HOST_CTRL0_FORCESUSPEND                 (0x1 << 4)
+#define HOST_CTRL0_WORDINTERFACE                (0x1 << 3)
+#define HOST_CTRL0_UTMISWRST                    (0x1 << 2)
+#define HOST_CTRL0_LINKSWRST                    (0x1 << 1)
+#define HOST_CTRL0_PHYSWRST                     (0x1 << 0)
+
+#define EXYNOS5_PHY_HOST_TUNE0                  EXYNOS4_HSOTG_PHYREG(0x04)
+#define EXYNOS5_PHY_HOST_TEST0                  EXYNOS4_HSOTG_PHYREG(0x08)
+
+#define EXYNOS5_PHY_HSIC_CTRL1                  EXYNOS4_HSOTG_PHYREG(0x10)
+#define EXYNOS5_PHY_HSIC_CTRL2                  EXYNOS4_HSOTG_PHYREG(0x20)
+#define HSIC_CTRL_REFCLKSEL(val)                ((val&0x3) << 23)
+#define HSIC_CTRL_REFCLKDIV(val)                ((val&0x7f) << 16)
+#define HSIC_CTRL_SIDDQ                         (0x1 << 6)
+#define HSIC_CTRL_FORCESLEEP                    (0x1 << 5)
+#define HSIC_CTRL_FORCESUSPEND                  (0x1 << 4)
+#define HSIC_CTRL_WORDINTERFACE                 (0x1 << 3)
+#define HSIC_CTRL_UTMISWRST                     (0x1 << 2)
+#define HSIC_CTRL_PHYSWRST                      (0x1 << 0)
+
+#define EXYNOS5_PHY_HOST_EHCICTRL               EXYNOS4_HSOTG_PHYREG(0x30)
+#define EHCICTRL_ENAINCRXALIGN                  (0x1 << 29)
+#define EHCICTRL_ENAINCR4                       (0x1 << 28)
+#define EHCICTRL_ENAINCR8                       (0x1 << 27)
+#define EHCICTRL_ENAINCR16                      (0x1 << 26)
+
+#define EXYNOS5_PHY_HOST_OHCICTRL               EXYNOS4_HSOTG_PHYREG(0x34)
+
+#define EXYNOS5_PHY_OTG_SYS                     EXYNOS4_HSOTG_PHYREG(0x38)
+#define OTG_SYS_PHYLINK_SW_RESET                (0x1 << 14)
+#define OTG_SYS_LINK_SW_RST_UOTG                (0x1 << 13)
+#define OTG_SYS_PHY0_SW_RST                     (0x1 << 12)
+#define OTG_SYS_REF_CLK_SEL(val)                ((val&0x3) << 9)
+#define OTG_SYS_REF_CLK_SEL_MASK                (0x3 << 9)
+#define OTG_SYS_IP_PULLUP_UOTG                  (0x1 << 8)
+#define OTG_SYS_COMMON_ON                       (0x1 << 7)
+#define OTG_SYS_CLKSEL_SHIFT                    (4)
+#define OTG_SYS_CTRL0_FSEL_MASK                 (0x7 << 4)
+#define OTG_SYS_FORCE_SLEEP                     (0x1 << 3)
+#define OTG_SYS_OTGDISABLE                      (0x1 << 2)
+#define OTG_SYS_SIDDQ_UOTG                      (0x1 << 1)
+#define OTG_SYS_FORCE_SUSPEND                   (0x1 << 0)
+
 #endif /* __PLAT_S5P_REGS_USB_PHY_H */
diff --git a/arch/arm/mach-exynos/setup-usb-phy.c b/arch/arm/mach-exynos/setup-usb-phy.c
index b81cc56..c725745 100644
--- a/arch/arm/mach-exynos/setup-usb-phy.c
+++ b/arch/arm/mach-exynos/setup-usb-phy.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Yulgon Kim <yulgon.kim at samsung.com>
  * Author: Joonyoung Shim <jy0922.shim at samsung.com>
  *
  *  This program is free software; you can redistribute  it and/or modify it
@@ -19,205 +20,457 @@
 #include <plat/cpu.h>
 #include <plat/usb-phy.h>
 
+#define ETC6PUD		(S5P_VA_GPIO2 + 0x228)
+#define EXYNOS4_USB_CFG	(S3C_VA_SYS + 0x21C)
+
+#define PHY_ENABLE	(1 << 0)
+#define PHY_DISABLE	(0)
+
+enum usb_host_type {
+	HOST_PHY_EHCI	= (0x1 << 0),
+	HOST_PHY_OHCI	= (0x1 << 1),
+};
+
+enum usb_phy_type {
+	USB_PHY		= (0x1 << 0),
+	USB_PHY0	= (0x1 << 0),
+	USB_PHY1	= (0x1 << 1),
+	USB_PHY_HSIC0	= (0x1 << 1),
+	USB_PHY_HSIC1	= (0x1 << 2),
+};
+
 static atomic_t host_usage;
+static DEFINE_MUTEX(phy_lock);
+static struct clk *phy_clk;
 
 static int exynos4_usb_host_phy_is_on(void)
 {
 	return (readl(EXYNOS4_PHYPWR) & PHY1_STD_ANALOG_POWERDOWN) ? 0 : 1;
 }
 
-static void exynos4210_usb_phy_clkset(struct platform_device *pdev)
+static int exynos4_usb_phy20_is_on(void)
 {
-	struct clk *xusbxti_clk;
-	u32 phyclk;
+	return exynos4_usb_host_phy_is_on();
+}
+
+static int exynos_usb_phy_clock_enable(struct platform_device *pdev)
+{
+	int err;
+
+	if (!phy_clk) {
+		if (soc_is_exynos4210() ||
+				soc_is_exynos4212() || soc_is_exynos4412())
+			phy_clk = clk_get(&pdev->dev, "otg");
+		else
+			phy_clk = clk_get(&pdev->dev, "usbhost");
+
+		if (IS_ERR(phy_clk)) {
+			dev_err(&pdev->dev, "Failed to get phy clock\n");
+			return PTR_ERR(phy_clk);
+		}
+	}
+
+	err = clk_enable(phy_clk);
+
+	return err;
+}
 
-	xusbxti_clk = clk_get(&pdev->dev, "xusbxti");
-	if (xusbxti_clk && !IS_ERR(xusbxti_clk)) {
-		if (soc_is_exynos4210()) {
-			/* set clock frequency for PLL */
-			phyclk = readl(EXYNOS4_PHYCLK) & ~EXYNOS4210_CLKSEL_MASK;
-
-			switch (clk_get_rate(xusbxti_clk)) {
-			case 12 * MHZ:
-				phyclk |= EXYNOS4210_CLKSEL_12M;
-				break;
-			case 48 * MHZ:
-				phyclk |= EXYNOS4210_CLKSEL_48M;
-				break;
-			default:
-			case 24 * MHZ:
-				phyclk |= EXYNOS4210_CLKSEL_24M;
-				break;
-			}
-			writel(phyclk, EXYNOS4_PHYCLK);
-		} else if (soc_is_exynos4212() || soc_is_exynos4412()) {
-			/* set clock frequency for PLL */
-			phyclk = readl(EXYNOS4_PHYCLK) & ~EXYNOS4X12_CLKSEL_MASK;
-
-			switch (clk_get_rate(xusbxti_clk)) {
-			case 9600 * KHZ:
-				phyclk |= EXYNOS4X12_CLKSEL_9600K;
-				break;
-			case 10 * MHZ:
-				phyclk |= EXYNOS4X12_CLKSEL_10M;
-				break;
-			case 12 * MHZ:
-				phyclk |= EXYNOS4X12_CLKSEL_12M;
-				break;
-			case 19200 * KHZ:
-				phyclk |= EXYNOS4X12_CLKSEL_19200K;
-				break;
-			case 20 * MHZ:
-				phyclk |= EXYNOS4X12_CLKSEL_20M;
-				break;
-			default:
-			case 24 * MHZ:
-				/* default reference clock */
-				phyclk |= EXYNOS4X12_CLKSEL_24M;
-				break;
-			}
-			writel(phyclk, EXYNOS4_PHYCLK);
+static int exynos_usb_phy_clock_disable(struct platform_device *pdev)
+{
+	if (!phy_clk) {
+		if (soc_is_exynos4210() ||
+				soc_is_exynos4212() || soc_is_exynos4412())
+			phy_clk = clk_get(&pdev->dev, "otg");
+		else
+			phy_clk = clk_get(&pdev->dev, "usbhost");
+		if (IS_ERR(phy_clk)) {
+			dev_err(&pdev->dev, "Failed to get phy clock\n");
+			return PTR_ERR(phy_clk);
 		}
-		clk_put(xusbxti_clk);
 	}
+
+	clk_disable(phy_clk);
+
+	return 0;
 }
 
-static int exynos4210_usb_phy0_init(struct platform_device *pdev)
+static u32 exynos_usb_phy_set_clock(struct platform_device *pdev)
 {
+	struct clk *ref_clk;
+	u32 refclk_freq = 0;
+
+	if (soc_is_exynos4210() || soc_is_exynos4212() || soc_is_exynos4412())
+		ref_clk = clk_get(&pdev->dev, "xusbxti");
+	else
+		ref_clk = clk_get(&pdev->dev, "ext_xtal");
+
+	if (IS_ERR(ref_clk)) {
+		dev_err(&pdev->dev, "Failed to get reference clock\n");
+		return PTR_ERR(ref_clk);
+	}
+
+	if (soc_is_exynos4210()) {
+		switch (clk_get_rate(ref_clk)) {
+		case 12 * MHZ:
+			refclk_freq = EXYNOS4210_CLKSEL_12M;
+			break;
+		case 48 * MHZ:
+			refclk_freq = EXYNOS4210_CLKSEL_48M;
+			break;
+		case 24 * MHZ:
+		default:
+			/* default reference clock */
+			refclk_freq = EXYNOS4210_CLKSEL_24M;
+			break;
+		}
+	} else if (soc_is_exynos4212() | soc_is_exynos4412()) {
+		switch (clk_get_rate(ref_clk)) {
+		case 96 * 100000:
+			refclk_freq = EXYNOS4X12_CLKSEL_9600K;
+			break;
+		case 10 * MHZ:
+			refclk_freq = EXYNOS4X12_CLKSEL_10M;
+			break;
+		case 12 * MHZ:
+			refclk_freq = EXYNOS4X12_CLKSEL_12M;
+			break;
+		case 192 * 100000:
+			refclk_freq = EXYNOS4X12_CLKSEL_19200K;
+			break;
+		case 20 * MHZ:
+			refclk_freq = EXYNOS4X12_CLKSEL_20M;
+			break;
+		case 24 * MHZ:
+		default:
+			/* default reference clock */
+			refclk_freq = EXYNOS4X12_CLKSEL_24M;
+			break;
+		}
+	} else {
+		switch (clk_get_rate(ref_clk)) {
+		case 96 * 100000:
+			refclk_freq = EXYNOS5_CLKSEL_9600K;
+			break;
+		case 10 * MHZ:
+			refclk_freq = EXYNOS5_CLKSEL_10M;
+			break;
+		case 12 * MHZ:
+			refclk_freq = EXYNOS5_CLKSEL_12M;
+			break;
+		case 192 * 100000:
+			refclk_freq = EXYNOS5_CLKSEL_19200K;
+			break;
+		case 20 * MHZ:
+			refclk_freq = EXYNOS5_CLKSEL_20M;
+			break;
+		case 50 * MHZ:
+			refclk_freq = EXYNOS5_CLKSEL_50M;
+			break;
+		case 24 * MHZ:
+		default:
+			/* default reference clock */
+			refclk_freq = EXYNOS5_CLKSEL_24M;
+			break;
+		}
+	}
+	clk_put(ref_clk);
+
+	return refclk_freq;
+}
+
+static void exynos_usb_phy_control(enum usb_phy_type phy_type , int on)
+{
+	if (soc_is_exynos4210()) {
+		if (phy_type & USB_PHY0)
+			writel(on, S5P_USBDEVICE_PHY_CONTROL);
+		if (phy_type & USB_PHY1)
+			writel(on, S5P_USBHOST_PHY_CONTROL);
+	} else if (soc_is_exynos4212() | soc_is_exynos4412()) {
+		if (phy_type & USB_PHY)
+			writel(on, S5P_USB_PHY_CONTROL);
+#ifdef CONFIG_USB_S5P_HSIC0
+		if (phy_type & USB_PHY_HSIC0)
+			writel(on, S5P_HSIC_1_PHY_CONTROL);
+#endif
+#ifdef CONFIG_USB_S5P_HSIC1
+		if (phy_type & USB_PHY_HSIC1)
+			writel(on, S5P_HSIC_2_PHY_CONTROL);
+#endif
+	} else {
+		if (phy_type & USB_PHY0)
+			writel(on, EXYNOS5_USBDEV_PHY_CONTROL);
+		if (phy_type & USB_PHY1)
+			writel(on, EXYNOS5_USBHOST_PHY_CONTROL);
+	}
+}
+
+static int exynos4_usb_phy0_init(struct platform_device *pdev)
+{
+	u32 phypwr;
+	u32 phyclk;
 	u32 rstcon;
 
-	writel(readl(S5P_USBDEVICE_PHY_CONTROL) | S5P_USBDEVICE_PHY_ENABLE,
-			S5P_USBDEVICE_PHY_CONTROL);
+	exynos_usb_phy_control(USB_PHY0, PHY_ENABLE);
 
-	exynos4210_usb_phy_clkset(pdev);
+	/* set clock frequency for PLL */
+	phyclk = exynos_usb_phy_set_clock(pdev);
+	phyclk &= ~(PHY0_COMMON_ON_N);
+	writel(phyclk, EXYNOS4_PHYCLK);
 
-	/* set to normal PHY0 */
-	writel((readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK), EXYNOS4_PHYPWR);
+	/* set to normal of PHY0 */
+	phypwr = readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK;
+	writel(phypwr, EXYNOS4_PHYPWR);
 
-	/* reset PHY0 and Link */
+	/* reset all ports of both PHY and Link */
 	rstcon = readl(EXYNOS4_RSTCON) | PHY0_SWRST_MASK;
 	writel(rstcon, EXYNOS4_RSTCON);
 	udelay(10);
-
 	rstcon &= ~PHY0_SWRST_MASK;
 	writel(rstcon, EXYNOS4_RSTCON);
 
 	return 0;
 }
 
-static int exynos4210_usb_phy0_exit(struct platform_device *pdev)
+static int exynos4_usb_phy0_exit(struct platform_device *pdev)
 {
-	writel((readl(EXYNOS4_PHYPWR) | PHY0_ANALOG_POWERDOWN |
-				PHY0_OTG_DISABLE), EXYNOS4_PHYPWR);
+	/* unset to normal of PHY0 */
+	writel((readl(EXYNOS4_PHYPWR) | PHY0_NORMAL_MASK),
+			EXYNOS4_PHYPWR);
 
-	writel(readl(S5P_USBDEVICE_PHY_CONTROL) & ~S5P_USBDEVICE_PHY_ENABLE,
-			S5P_USBDEVICE_PHY_CONTROL);
+	exynos_usb_phy_control(USB_PHY0, PHY_DISABLE);
 
 	return 0;
 }
 
-static int exynos4210_usb_phy1_init(struct platform_device *pdev)
+static int exynos4_usb_phy1_init(struct platform_device *pdev)
 {
-	struct clk *otg_clk;
+	u32 phypwr;
+	u32 phyclk;
 	u32 rstcon;
-	int err;
 
 	atomic_inc(&host_usage);
 
-	otg_clk = clk_get(&pdev->dev, "otg");
-	if (IS_ERR(otg_clk)) {
-		dev_err(&pdev->dev, "Failed to get otg clock\n");
-		return PTR_ERR(otg_clk);
+	if (exynos4_usb_host_phy_is_on()) {
+		dev_err(&pdev->dev, "Already power on PHY\n");
+		return 0;
 	}
 
-	err = clk_enable(otg_clk);
-	if (err) {
-		clk_put(otg_clk);
-		return err;
-	}
+	/*
+	 *  set XuhostOVERCUR to in-active by controlling ET6PUD[15:14]
+	 *  0x0 : pull-up/down disabled
+	 *  0x1 : pull-down enabled
+	 *  0x2 : reserved
+	 *  0x3 : pull-up enabled
+	 */
+	writel((__raw_readl(ETC6PUD) & ~(0x3 << 14)) | (0x3 << 14),
+		ETC6PUD);
 
-	if (exynos4_usb_host_phy_is_on())
-		return 0;
+	exynos_usb_phy_control(USB_PHY1, PHY_ENABLE);
 
-	writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE,
-			S5P_USBHOST_PHY_CONTROL);
+	/* set clock frequency for PLL */
+	phyclk = exynos_usb_phy_set_clock(pdev);
+	phyclk &= ~(PHY1_COMMON_ON_N);
+	writel(phyclk, EXYNOS4_PHYCLK);
 
-	exynos4210_usb_phy_clkset(pdev);
+	/* set to normal HSIC 0 and 1 of PHY1 */
+	phypwr = readl(EXYNOS4_PHYPWR);
+	phypwr &= ~(PHY1_STD_NORMAL_MASK
+		| EXYNOS4210_HSIC0_NORMAL_MASK);
+	writel(phypwr, EXYNOS4_PHYPWR);
 
 	/* floating prevention logic: disable */
 	writel((readl(EXYNOS4_PHY1CON) | FPENABLEN), EXYNOS4_PHY1CON);
 
-	/* set to normal HSIC 0 and 1 of PHY1 */
-	writel((readl(EXYNOS4_PHYPWR) & ~PHY1_HSIC_NORMAL_MASK),
-			EXYNOS4_PHYPWR);
-
-	/* set to normal standard USB of PHY1 */
-	writel((readl(EXYNOS4_PHYPWR) & ~PHY1_STD_NORMAL_MASK), EXYNOS4_PHYPWR);
-
 	/* reset all ports of both PHY and Link */
-	rstcon = readl(EXYNOS4_RSTCON) | HOST_LINK_PORT_SWRST_MASK |
-		PHY1_SWRST_MASK;
+	rstcon = readl(EXYNOS4_RSTCON)
+		| EXYNOS4210_HOST_LINK_PORT_SWRST_MASK
+		| EXYNOS4210_PHY1_SWRST_MASK;
 	writel(rstcon, EXYNOS4_RSTCON);
 	udelay(10);
 
-	rstcon &= ~(HOST_LINK_PORT_SWRST_MASK | PHY1_SWRST_MASK);
+	rstcon &= ~(EXYNOS4210_HOST_LINK_PORT_SWRST_MASK
+		| EXYNOS4210_PHY1_SWRST_MASK);
 	writel(rstcon, EXYNOS4_RSTCON);
 	udelay(80);
 
-	clk_disable(otg_clk);
-	clk_put(otg_clk);
+	return 0;
+}
+
+static int exynos4_usb_phy1_exit(struct platform_device *pdev)
+{
+	u32 phypwr;
+
+	if (atomic_dec_return(&host_usage) > 0) {
+		dev_info(&pdev->dev, "still being used\n");
+		return -EBUSY;
+	}
+
+	phypwr = readl(EXYNOS4_PHYPWR)
+		| PHY1_STD_NORMAL_MASK
+		| EXYNOS4210_HSIC0_NORMAL_MASK;
+	writel(phypwr, EXYNOS4_PHYPWR);
+
+	exynos_usb_phy_control(USB_PHY1, PHY_DISABLE);
 
 	return 0;
 }
 
-static int exynos4210_usb_phy1_exit(struct platform_device *pdev)
+static int exynos4_usb_phy20_init(struct platform_device *pdev)
 {
-	struct clk *otg_clk;
-	int err;
+	u32 phypwr, phyclk, rstcon;
 
-	if (atomic_dec_return(&host_usage) > 0)
-		return 0;
+	atomic_inc(&host_usage);
 
-	otg_clk = clk_get(&pdev->dev, "otg");
-	if (IS_ERR(otg_clk)) {
-		dev_err(&pdev->dev, "Failed to get otg clock\n");
-		return PTR_ERR(otg_clk);
+	if (exynos4_usb_phy20_is_on()) {
+		dev_err(&pdev->dev, "Already power on PHY\n");
+		return 0;
 	}
 
-	err = clk_enable(otg_clk);
-	if (err) {
-		clk_put(otg_clk);
-		return err;
+	/*
+	 *  set XuhostOVERCUR to in-active by controlling ET6PUD[15:14]
+	 *  0x0 : pull-up/down disabled
+	 *  0x1 : pull-down enabled
+	 *  0x2 : reserved
+	 *  0x3 : pull-up enabled
+	 */
+	writel((__raw_readl(ETC6PUD) & ~(0x3 << 14)) | (0x3 << 14),
+		ETC6PUD);
+
+	exynos_usb_phy_control(USB_PHY
+		| USB_PHY_HSIC0,
+		PHY_ENABLE);
+
+	/* set clock frequency for PLL */
+	phyclk = exynos_usb_phy_set_clock(pdev);
+	/* COMMON Block configuration during suspend */
+	phyclk &= ~(PHY0_COMMON_ON_N | PHY1_COMMON_ON_N);
+	writel(phyclk, EXYNOS4_PHYCLK);
+
+	/* set to normal of Device */
+	phypwr = readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK;
+	writel(phypwr, EXYNOS4_PHYPWR);
+
+	/* set to normal of Host */
+	phypwr = readl(EXYNOS4_PHYPWR);
+	phypwr &= ~(PHY1_STD_NORMAL_MASK
+		| EXYNOS4X12_HSIC0_NORMAL_MASK);
+	writel(phypwr, EXYNOS4_PHYPWR);
+
+	/* reset both PHY and Link of Device */
+	rstcon = readl(EXYNOS4_RSTCON) | PHY0_SWRST_MASK;
+	writel(rstcon, EXYNOS4_RSTCON);
+	udelay(10);
+	rstcon &= ~PHY0_SWRST_MASK;
+	writel(rstcon, EXYNOS4_RSTCON);
+
+	/* reset both PHY and Link of Host */
+	rstcon = readl(EXYNOS4_RSTCON)
+		| EXYNOS4X12_HOST_LINK_PORT_SWRST_MASK
+		| EXYNOS4X12_PHY1_SWRST_MASK;
+	writel(rstcon, EXYNOS4_RSTCON);
+	udelay(10);
+
+	rstcon &= ~(EXYNOS4X12_HOST_LINK_PORT_SWRST_MASK
+		| EXYNOS4X12_PHY1_SWRST_MASK);
+	writel(rstcon, EXYNOS4_RSTCON);
+	udelay(80);
+
+	return 0;
+}
+
+static int exynos4_usb_phy20_exit(struct platform_device *pdev)
+{
+	u32 phypwr;
+
+	if (atomic_dec_return(&host_usage) > 0) {
+		dev_info(&pdev->dev, "still being used\n");
+		return -EBUSY;
 	}
 
-	writel((readl(EXYNOS4_PHYPWR) | PHY1_STD_ANALOG_POWERDOWN),
+	/* unset to normal of Device */
+	writel((readl(EXYNOS4_PHYPWR) | PHY0_NORMAL_MASK),
 			EXYNOS4_PHYPWR);
 
-	writel(readl(S5P_USBHOST_PHY_CONTROL) & ~S5P_USBHOST_PHY_ENABLE,
-			S5P_USBHOST_PHY_CONTROL);
+	/* unset to normal of Host */
+	phypwr = readl(EXYNOS4_PHYPWR)
+		| PHY1_STD_NORMAL_MASK
+		| EXYNOS4X12_HSIC0_NORMAL_MASK;
+	writel(phypwr, EXYNOS4_PHYPWR);
 
-	clk_disable(otg_clk);
-	clk_put(otg_clk);
+	exynos_usb_phy_control(USB_PHY
+		| USB_PHY_HSIC0,
+		PHY_DISABLE);
+
+	return 0;
+}
+
+static int exynos_usb_dev_phy20_init(struct platform_device *pdev)
+{
+	if (soc_is_exynos4212() || soc_is_exynos4412())
+		exynos4_usb_phy20_init(pdev);
+
+	writel(0, EXYNOS4_USB_CFG);
+
+	return 0;
+}
+
+static int exynos_usb_dev_phy20_exit(struct platform_device *pdev)
+{
+	if (soc_is_exynos4212() || soc_is_exynos4412())
+		exynos4_usb_phy20_exit(pdev);
 
 	return 0;
 }
 
 int s5p_usb_phy_init(struct platform_device *pdev, int type)
 {
-	if (type == S5P_USB_PHY_DEVICE)
-		return exynos4210_usb_phy0_init(pdev);
-	else if (type == S5P_USB_PHY_HOST)
-		return exynos4210_usb_phy1_init(pdev);
+	int ret = -EINVAL;
+
+	if (exynos_usb_phy_clock_enable(pdev))
+		return ret;
+
+	mutex_lock(&phy_lock);
+	if (type == S5P_USB_PHY_HOST) {
+		if (soc_is_exynos4210())
+			ret = exynos4_usb_phy1_init(pdev);
+		else if (soc_is_exynos4212() || soc_is_exynos4412())
+			ret = exynos4_usb_phy20_init(pdev);
+	} else if (type == S5P_USB_PHY_DEVICE) {
+		if (soc_is_exynos4210())
+			ret = exynos4_usb_phy0_init(pdev);
+		else
+			ret = exynos_usb_dev_phy20_init(pdev);
+	}
+
+	mutex_unlock(&phy_lock);
+	exynos_usb_phy_clock_disable(pdev);
 
-	return -EINVAL;
+	return ret;
 }
 
 int s5p_usb_phy_exit(struct platform_device *pdev, int type)
 {
-	if (type == S5P_USB_PHY_DEVICE)
-		return exynos4210_usb_phy0_exit(pdev);
-	else if (type == S5P_USB_PHY_HOST)
-		return exynos4210_usb_phy1_exit(pdev);
+	int ret = -EINVAL;
+
+	if (exynos_usb_phy_clock_enable(pdev))
+		return ret;
+
+	mutex_lock(&phy_lock);
+
+	if (type == S5P_USB_PHY_HOST) {
+		if (soc_is_exynos4210())
+			ret = exynos4_usb_phy1_exit(pdev);
+		else if (soc_is_exynos4212() || soc_is_exynos4412())
+			ret = exynos4_usb_phy20_exit(pdev);
+	} else if (type == S5P_USB_PHY_DEVICE) {
+		if (soc_is_exynos4210())
+			ret = exynos4_usb_phy0_exit(pdev);
+		else
+			ret = exynos_usb_dev_phy20_exit(pdev);
+	}
+
+	mutex_unlock(&phy_lock);
+	exynos_usb_phy_clock_disable(pdev);
 
-	return -EINVAL;
+	return ret;
 }
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 075d2ec..5f69041 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -199,6 +199,20 @@ config USB_EHCI_S5P
        help
 	 Enable support for the S5P SOC's on-chip EHCI controller.
 
+config USB_S5P_HSIC0
+    boolean "S5P HSIC0 support"
+    depends on USB_EHCI_HCD && PLAT_S5P && USB_EHCI_S5P
+    default n
+    help
+      Enable support for the S5P SOC's on-chip HSIC PHY.
+
+config USB_S5P_HSIC1
+    boolean "S5P HSIC1 support"
+    depends on USB_EHCI_HCD && PLAT_S5P && USB_EHCI_S5P
+    default n
+    help
+      Enable support for the S5P SOC's on-chip HSIC PHY.
+
 config USB_EHCI_MV
 	bool "EHCI support for Marvell on-chip controller"
 	depends on USB_EHCI_HCD && (ARCH_PXA || ARCH_MMP)
-- 
1.7.9.5




More information about the linux-arm-kernel mailing list