From patchwork Mon Aug 6 13:57:40 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dongjin Kim X-Patchwork-Id: 1279471 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork2.kernel.org (Postfix) with ESMTP id 37626DF288 for ; Mon, 6 Aug 2012 14:01:56 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1SyNpL-0002A7-W0; Mon, 06 Aug 2012 13:58:36 +0000 Received: from mail-gg0-f177.google.com ([209.85.161.177]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1SyNpH-00029s-6V for linux-arm-kernel@lists.infradead.org; Mon, 06 Aug 2012 13:58:33 +0000 Received: by ggnm2 with SMTP id m2so401878ggn.36 for ; Mon, 06 Aug 2012 06:58:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=1f4krNlAlz9884r7AMt7nfRlmJ1b7UAA6IeeJ9oxvK8=; b=DacvO1reZEjiq21T+YClClKzlMqXpUpQy04W8NolM7Nk4Tln2pdLCTSypHmKpDs5+3 tdaknpWmon1Zg4GLs/KBQ0ek2jNeIS8w4JjLIArhB0lup8WTz1AFdFTSh82x6ge/5W6k 8ZGG31Azpbv8GZcBFK51nijyyOECszR+TcWCRqp00mV73rbpmGOHfGAJF4BYDm3svgvT JNSKF7Ruj0l48hVzvZ2RsDOpcgtLm9ueSKOn2UKnB0aLI/u9wSVee3/Tikl2StCbzIyY RXI1+3F3sPsU0COddWLfpKCrSM3fwjaiKd0wmYKQJnEWtymf3TYWKKXhU+MDp6iIDcKC 07bw== Received: by 10.66.81.3 with SMTP id v3mr18370485pax.62.1344261509598; Mon, 06 Aug 2012 06:58:29 -0700 (PDT) Received: from localhost.localdomain ([210.113.108.23]) by mx.google.com with ESMTPS id qi8sm9016134pbc.36.2012.08.06.06.58.24 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 06 Aug 2012 06:58:28 -0700 (PDT) From: Dongjin Kim To: Subject: [PATCH] ARM: EXYNOS: Add USB HSIC device Date: Mon, 6 Aug 2012 22:57:40 +0900 Message-Id: <1344261462-14183-3-git-send-email-dongjin.kim@agreeyamobility.net> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1344261462-14183-1-git-send-email-dongjin.kim@agreeyamobility.net> References: <1344261462-14183-1-git-send-email-dongjin.kim@agreeyamobility.net> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.7 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.161.177 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (tobetter[at]gmail.com) -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Cc: Tomasz Stanislawski , Kukjin Kim , Russell King , Sachin Kamat , Greg Kroah-Hartman , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Felipe Balbi , Kyungmin Park , linux-samsung-soc@vger.kernel.org, Alan Stern , Hauke Mehrtens , Neil Zhang , Jongpill Lee , linux-arm-kernel@lists.infradead.org, Dongjin Kim X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org 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 --- 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 * Author: Joonyoung Shim * * This program is free software; you can redistribute it and/or modify it @@ -19,205 +20,457 @@ #include #include +#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)