aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/setup-usb-phy.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-exynos/setup-usb-phy.c')
-rw-r--r--arch/arm/mach-exynos/setup-usb-phy.c1407
1 files changed, 1407 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/setup-usb-phy.c b/arch/arm/mach-exynos/setup-usb-phy.c
new file mode 100644
index 0000000..bae906f
--- /dev/null
+++ b/arch/arm/mach-exynos/setup-usb-phy.c
@@ -0,0 +1,1407 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Yulgon Kim <yulgon.kim@samsung.com>
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <mach/regs-pmu.h>
+#include <mach/regs-pmu5.h>
+#include <mach/regs-usb-phy.h>
+#include <plat/cpu.h>
+#include <plat/usb-phy.h>
+#include <plat/regs-usb3-exynos-drd-phy.h>
+#include <linux/interrupt.h>
+#include <plat/usbgadget.h>
+#include <mach/sec_modem.h>
+
+#ifdef CONFIG_USB_OHCI_S5P
+ #include <plat/devs.h>
+ #include <linux/usb.h>
+ #include <linux/usb/otg.h>
+ #include <linux/usb/hcd.h>
+#endif
+
+#ifdef CONFIG_USB_OHCI_S5P
+ #include <plat/devs.h>
+ #include <linux/usb.h>
+ #include <linux/usb/otg.h>
+ #include <linux/usb/hcd.h>
+#endif
+
+#define ETC6PUD (S5P_VA_GPIO2 + 0x228)
+#define EXYNOS4_USB_CFG (S3C_VA_SYS + 0x21C)
+#define EXYNOS5_USB_CFG (S3C_VA_SYS + 0x230)
+
+#define PHY_ENABLE (1 << 0)
+#define PHY_DISABLE (0)
+
+#ifdef CONFIG_USB_OHCI_S5P
+struct s5p_ohci_hcd {
+ struct device *dev;
+ struct usb_hcd *hcd;
+ struct clk *clk;
+ int power_on;
+};
+#endif
+
+enum usb_host_type {
+ HOST_PHY_EHCI = (0x1 << 0),
+ HOST_PHY_OHCI = (0x1 << 1),
+ HOST_PHY_DEVICE = (0x1 << 2),
+};
+
+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),
+};
+
+struct exynos_usb_phy {
+ u8 lpa_entered;
+ unsigned long flags;
+ unsigned long usage;
+};
+
+static struct exynos_usb_phy usb_phy_control;
+
+static atomic_t host_usage;
+static DEFINE_MUTEX(phy_lock);
+static struct clk *phy_clk = NULL;
+
+static void exynos_usb_mux_change(struct platform_device *pdev, int val)
+{
+ u32 is_host;
+ if (soc_is_exynos4212() || soc_is_exynos4412()) {
+ is_host = readl(EXYNOS4_USB_CFG);
+ writel(val, EXYNOS4_USB_CFG);
+ } else {
+ is_host = readl(EXYNOS5_USB_CFG);
+ writel(val, EXYNOS5_USB_CFG);
+ }
+
+ if (is_host != val)
+ dev_dbg(&pdev->dev, "Change USB MUX from %s to %s",
+ is_host ? "Host" : "Device",
+ val ? "Host" : "Device");
+}
+
+static int exynos4_usb_host_phy_is_on(void)
+{
+ return (readl(EXYNOS4_PHYPWR) & PHY1_STD_ANALOG_POWERDOWN) ? 0 : 1;
+}
+
+static int exynos_usb_device_phy_is_on(void)
+{
+ int ret;
+
+ if (soc_is_exynos4210())
+ ret = (readl(EXYNOS4_PHYPWR) & PHY0_ANALOG_POWERDOWN) ? 0 : 1;
+ else if (soc_is_exynos4212() || soc_is_exynos4412())
+ ret = readl(EXYNOS4_USB_CFG) ? 0 : 1;
+ else
+ ret = readl(EXYNOS5_USB_CFG) ? 0 : 1;
+
+ return ret;
+}
+
+static int exynos4_usb_phy20_is_on(void)
+{
+ return exynos4_usb_host_phy_is_on();
+}
+
+static int exynos5_usb_host_phy20_is_on(void)
+{
+ return (readl(EXYNOS5_PHY_HOST_CTRL0) & HOST_CTRL0_SIDDQ) ? 0 : 1;
+}
+
+static int exynos5_usb_phy30_is_on(void)
+{
+ return readl(EXYNOS5_USBDEV_PHY_CONTROL) ? 1 : 0;
+}
+
+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, "usbotg");
+ 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;
+}
+
+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, "usbotg");
+ 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_disable(phy_clk);
+
+ return 0;
+}
+
+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 = EXYNOS4212_CLKSEL_9600K;
+ break;
+ case 10 * MHZ:
+ refclk_freq = EXYNOS4212_CLKSEL_10M;
+ break;
+ case 12 * MHZ:
+ refclk_freq = EXYNOS4212_CLKSEL_12M;
+ break;
+ case 192 * 100000:
+ refclk_freq = EXYNOS4212_CLKSEL_19200K;
+ break;
+ case 20 * MHZ:
+ refclk_freq = EXYNOS4212_CLKSEL_20M;
+ break;
+ case 24 * MHZ:
+ default:
+ /* default reference clock */
+ refclk_freq = EXYNOS4212_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_USBOTG_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;
+
+ exynos_usb_phy_control(USB_PHY0, PHY_ENABLE);
+
+ /* set clock frequency for PLL */
+ phyclk = readl(EXYNOS4_PHYCLK) & ~(EXYNOS4210_CLKSEL_MASK);
+ phyclk |= exynos_usb_phy_set_clock(pdev);
+ phyclk &= ~(PHY0_COMMON_ON_N);
+ writel(phyclk, EXYNOS4_PHYCLK);
+
+ /* set to normal of PHY0 */
+ phypwr = readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK;
+ writel(phypwr, EXYNOS4_PHYPWR);
+
+ /* 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 exynos4_usb_phy0_exit(struct platform_device *pdev)
+{
+ /* unset to normal of PHY0 */
+ writel((readl(EXYNOS4_PHYPWR) | PHY0_NORMAL_MASK),
+ EXYNOS4_PHYPWR);
+
+ exynos_usb_phy_control(USB_PHY0, PHY_DISABLE);
+
+ return 0;
+}
+
+static int exynos4_usb_phy1_suspend(struct platform_device *pdev)
+{
+ u32 phypwr;
+
+ /* set to suspend HSIC 0 and 1 and standard of PHY1 */
+ phypwr = readl(EXYNOS4_PHYPWR);
+ if (soc_is_exynos4210()) {
+ phypwr |= (PHY1_STD_FORCE_SUSPEND
+ | EXYNOS4210_HSIC0_FORCE_SUSPEND
+ | EXYNOS4210_HSIC1_FORCE_SUSPEND);
+ } else {
+ phypwr = readl(EXYNOS4_PHYPWR);
+ phypwr |= (PHY1_STD_FORCE_SUSPEND
+ | EXYNOS4212_HSIC0_FORCE_SUSPEND
+ | EXYNOS4212_HSIC1_FORCE_SUSPEND);
+ }
+ writel(phypwr, EXYNOS4_PHYPWR);
+
+ return 0;
+}
+
+static int exynos4_usb_phy1_resume(struct platform_device *pdev)
+{
+ u32 rstcon;
+ u32 phypwr;
+ int err;
+
+#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
+ /* HSIC LPA: reset-resume, let cp know pda active from LPA */
+ /* slave wake at lpa wake ??? */
+ /* 12.04.27 Move start of phy1_resume, If usb cable power on the
+ * host phy, EHCI resume miss the PDA_ACTVIE, then CP can't send Host
+ * wakeup Irq */
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ set_hsic_lpa_states(STATE_HSIC_LPA_WAKE);
+#endif
+
+ if (exynos4_usb_host_phy_is_on()) {
+ /* set to resume HSIC 0 and 1 and standard of PHY1 */
+ phypwr = readl(EXYNOS4_PHYPWR);
+ if (soc_is_exynos4210()) {
+ phypwr &= ~(PHY1_STD_FORCE_SUSPEND
+ | EXYNOS4210_HSIC0_FORCE_SUSPEND
+ | EXYNOS4210_HSIC1_FORCE_SUSPEND);
+ } else {
+ phypwr = readl(EXYNOS4_PHYPWR);
+ phypwr &= ~(PHY1_STD_FORCE_SUSPEND
+ | EXYNOS4212_HSIC0_FORCE_SUSPEND
+ | EXYNOS4212_HSIC1_FORCE_SUSPEND);
+ }
+ writel(phypwr, EXYNOS4_PHYPWR);
+ if (usb_phy_control.lpa_entered) {
+ usb_phy_control.lpa_entered = 0;
+ err = 1;
+ } else
+ err = 0;
+ } else {
+ phypwr = readl(EXYNOS4_PHYPWR);
+ /* set to normal HSIC 0 and 1 of PHY1 */
+ if (soc_is_exynos4210()) {
+ writel(PHY_ENABLE, S5P_USBHOST_PHY_CONTROL);
+
+ phypwr &= ~(PHY1_STD_NORMAL_MASK
+ | EXYNOS4210_HSIC0_NORMAL_MASK
+ | EXYNOS4210_HSIC1_NORMAL_MASK);
+ writel(phypwr, EXYNOS4_PHYPWR);
+
+ /* reset all ports of both PHY and Link */
+ rstcon = readl(EXYNOS4_RSTCON)
+ | EXYNOS4210_HOST_LINK_PORT_SWRST_MASK
+ | EXYNOS4210_PHY1_SWRST_MASK;
+ writel(rstcon, EXYNOS4_RSTCON);
+ udelay(10);
+
+ rstcon &= ~(EXYNOS4210_HOST_LINK_PORT_SWRST_MASK
+ | EXYNOS4210_PHY1_SWRST_MASK);
+ writel(rstcon, EXYNOS4_RSTCON);
+ } else {
+ exynos_usb_phy_control(USB_PHY
+ | USB_PHY_HSIC0
+ | USB_PHY_HSIC1,
+ PHY_ENABLE);
+
+ /* set to normal of Device */
+ phypwr = readl(EXYNOS4_PHYPWR) & ~PHY0_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);
+
+ /* set to normal of Host */
+ phypwr &= ~(PHY1_STD_NORMAL_MASK
+ | EXYNOS4212_HSIC0_NORMAL_MASK
+ | EXYNOS4212_HSIC1_NORMAL_MASK);
+ writel(phypwr, EXYNOS4_PHYPWR);
+
+ /* reset all ports of both PHY and Link */
+ rstcon = readl(EXYNOS4_RSTCON)
+ | EXYNOS4212_HOST_LINK_PORT_SWRST_MASK
+ | EXYNOS4212_PHY1_SWRST_MASK;
+ writel(rstcon, EXYNOS4_RSTCON);
+ udelay(10);
+
+ rstcon &= ~(EXYNOS4212_HOST_LINK_PORT_SWRST_MASK
+ | EXYNOS4212_PHY1_SWRST_MASK);
+ writel(rstcon, EXYNOS4_RSTCON);
+ }
+ usb_phy_control.lpa_entered = 0;
+ err = 1;
+ }
+ udelay(80);
+
+ return err;
+}
+
+static int exynos4_usb_phy1_init(struct platform_device *pdev)
+{
+ u32 phypwr;
+ u32 phyclk;
+ u32 rstcon;
+
+
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ set_bit(HOST_PHY_EHCI, &usb_phy_control.usage);
+ else if (!strcmp(pdev->name, "s5p-ohci"))
+ set_bit(HOST_PHY_OHCI, &usb_phy_control.usage);
+ else if (!strcmp(pdev->name, "s3c-usbgadget"))
+ set_bit(HOST_PHY_DEVICE, &usb_phy_control.usage);
+
+ dev_info(&pdev->dev, "usb phy usage(%ld)\n",usb_phy_control.usage);
+
+ if (exynos4_usb_host_phy_is_on()) {
+ dev_err(&pdev->dev, "Already power on PHY\n");
+ return 0;
+ }
+
+ /*
+ * 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_PHY1, PHY_ENABLE);
+
+ /* set clock frequency for PLL */
+ phyclk = readl(EXYNOS4_PHYCLK) & ~(EXYNOS4210_CLKSEL_MASK);
+ phyclk |= exynos_usb_phy_set_clock(pdev);
+#ifdef CONFIG_USB_OHCI_S5P
+ phyclk |= PHY1_COMMON_ON_N;
+#else
+ phyclk &= ~(PHY1_COMMON_ON_N);
+#endif
+ writel(phyclk, EXYNOS4_PHYCLK);
+
+ /* set to normal HSIC 0 and 1 of PHY1 */
+ phypwr = readl(EXYNOS4_PHYPWR);
+ phypwr &= ~(PHY1_STD_NORMAL_MASK
+ | EXYNOS4210_HSIC0_NORMAL_MASK
+ | EXYNOS4210_HSIC1_NORMAL_MASK);
+ writel(phypwr, EXYNOS4_PHYPWR);
+
+ /* floating prevention logic: disable */
+ writel((readl(EXYNOS4_PHY1CON) | FPENABLEN), EXYNOS4_PHY1CON);
+
+ /* reset all ports of both PHY and Link */
+ rstcon = readl(EXYNOS4_RSTCON)
+ | EXYNOS4210_HOST_LINK_PORT_SWRST_MASK
+ | EXYNOS4210_PHY1_SWRST_MASK;
+ writel(rstcon, EXYNOS4_RSTCON);
+ udelay(10);
+
+ rstcon &= ~(EXYNOS4210_HOST_LINK_PORT_SWRST_MASK
+ | EXYNOS4210_PHY1_SWRST_MASK);
+ writel(rstcon, EXYNOS4_RSTCON);
+ udelay(80);
+
+ return 0;
+}
+
+static int exynos4_usb_phy1_exit(struct platform_device *pdev)
+{
+ u32 phypwr;
+
+
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ clear_bit(HOST_PHY_EHCI, &usb_phy_control.usage);
+ else if (!strcmp(pdev->name, "s5p-ohci"))
+ clear_bit(HOST_PHY_OHCI, &usb_phy_control.usage);
+ else if (!strcmp(pdev->name, "s3c-usbgadget"))
+ clear_bit(HOST_PHY_DEVICE, &usb_phy_control.usage);
+
+ if (usb_phy_control.usage) {
+ dev_info(&pdev->dev, "still being used(%ld)\n",usb_phy_control.usage);
+ return -EBUSY;
+ }
+
+ phypwr = readl(EXYNOS4_PHYPWR)
+ | PHY1_STD_NORMAL_MASK
+ | EXYNOS4210_HSIC0_NORMAL_MASK
+ | EXYNOS4210_HSIC1_NORMAL_MASK;
+ writel(phypwr, EXYNOS4_PHYPWR);
+
+ exynos_usb_phy_control(USB_PHY1, PHY_DISABLE);
+
+ return 0;
+}
+
+static int exynos4_usb_phy20_init(struct platform_device *pdev)
+{
+ u32 phypwr, phyclk, rstcon;
+
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ set_bit(HOST_PHY_EHCI, &usb_phy_control.usage);
+ else if (!strcmp(pdev->name, "s5p-ohci"))
+ set_bit(HOST_PHY_OHCI, &usb_phy_control.usage);
+ else if (!strcmp(pdev->name, "s3c-usbgadget"))
+ set_bit(HOST_PHY_DEVICE, &usb_phy_control.usage);
+
+ dev_info(&pdev->dev, "usb phy usage(%ld)\n", usb_phy_control.usage);
+
+ if (exynos4_usb_phy20_is_on()) {
+ dev_err(&pdev->dev, "Already power on PHY\n");
+ return 0;
+ }
+
+ /*
+ * 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
+ | USB_PHY_HSIC1,
+ PHY_ENABLE);
+
+ /* USB MUX change from Device to Host */
+ exynos_usb_mux_change(pdev, 1);
+
+ /* set clock frequency for PLL */
+ phyclk = exynos_usb_phy_set_clock(pdev);
+ /* COMMON Block configuration during suspend */
+ phyclk &= ~(PHY0_COMMON_ON_N);
+#ifdef CONFIG_USB_OHCI_S5P
+ phyclk |= PHY1_COMMON_ON_N;
+#else
+ phyclk &= ~(PHY1_COMMON_ON_N);
+#endif
+ 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
+ | EXYNOS4212_HSIC0_NORMAL_MASK
+ | EXYNOS4212_HSIC1_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)
+ | EXYNOS4212_HOST_LINK_PORT_SWRST_MASK
+ | EXYNOS4212_PHY1_SWRST_MASK;
+ writel(rstcon, EXYNOS4_RSTCON);
+ udelay(10);
+
+ rstcon &= ~(EXYNOS4212_HOST_LINK_PORT_SWRST_MASK
+ | EXYNOS4212_PHY1_SWRST_MASK);
+ writel(rstcon, EXYNOS4_RSTCON);
+ udelay(80);
+
+ return 0;
+}
+
+static int exynos4_usb_phy20_exit(struct platform_device *pdev)
+{
+ u32 phypwr;
+
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ clear_bit(HOST_PHY_EHCI, &usb_phy_control.usage);
+ else if (!strcmp(pdev->name, "s5p-ohci"))
+ clear_bit(HOST_PHY_OHCI, &usb_phy_control.usage);
+ else if (!strcmp(pdev->name, "s3c-usbgadget"))
+ clear_bit(HOST_PHY_DEVICE, &usb_phy_control.usage);
+
+ if (usb_phy_control.usage) {
+ dev_info(&pdev->dev, "still being used(%ld)\n",
+ usb_phy_control.usage);
+ return -EBUSY;
+ } else
+ dev_info(&pdev->dev, "usb host phy off\n");
+
+ /* unset to normal of Device */
+ writel((readl(EXYNOS4_PHYPWR) | PHY0_NORMAL_MASK),
+ EXYNOS4_PHYPWR);
+
+ /* unset to normal of Host */
+ phypwr = readl(EXYNOS4_PHYPWR)
+ | PHY1_STD_NORMAL_MASK
+ | EXYNOS4212_HSIC0_NORMAL_MASK
+ | EXYNOS4212_HSIC1_NORMAL_MASK;
+ writel(phypwr, EXYNOS4_PHYPWR);
+
+ exynos_usb_phy_control(USB_PHY
+ | USB_PHY_HSIC0
+ | USB_PHY_HSIC1,
+ PHY_DISABLE);
+
+ usb_phy_control.lpa_entered = 0;
+
+ return 0;
+}
+
+static int exynos5_usb_phy_host_suspend(struct platform_device *pdev)
+{
+ u32 hostphy_ctrl0;
+
+ /* set to suspend HSIC 1 and 2 */
+ writel(readl(EXYNOS5_PHY_HSIC_CTRL1) | HSIC_CTRL_FORCESUSPEND,
+ EXYNOS5_PHY_HSIC_CTRL1);
+ writel(readl(EXYNOS5_PHY_HSIC_CTRL2) | HSIC_CTRL_FORCESUSPEND,
+ EXYNOS5_PHY_HSIC_CTRL2);
+
+ hostphy_ctrl0 = readl(EXYNOS5_PHY_HOST_CTRL0);
+ /* set to suspend standard of PHY20 */
+ hostphy_ctrl0 |= HOST_CTRL0_FORCESUSPEND;
+ writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
+
+ return 0;
+}
+
+static int exynos5_usb_phy_host_resume(struct platform_device *pdev)
+{
+ u32 hostphy_ctrl0, otgphy_sys, hsic_ctrl;
+ int err;
+
+ if (exynos5_usb_host_phy20_is_on()) {
+ /* set to suspend HSIC 0 and 1 and standard of PHY1 */
+ hostphy_ctrl0 = readl(EXYNOS5_PHY_HOST_CTRL0);
+ hostphy_ctrl0 &= ~(HOST_CTRL0_FORCESUSPEND);
+ writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
+
+ /* set common_on_n of PHY1 for power consumption */
+ hsic_ctrl = readl(EXYNOS5_PHY_HSIC_CTRL1);
+ hsic_ctrl &= ~(HSIC_CTRL_FORCESUSPEND);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+ if (usb_phy_control.lpa_entered) {
+ usb_phy_control.lpa_entered = 0;
+ err = 1;
+ } else
+ err = 0;
+ } else {
+ exynos_usb_phy_control(USB_PHY1, PHY_ENABLE);
+
+ /* otg phy reset */
+ otgphy_sys = readl(EXYNOS5_PHY_OTG_SYS);
+ otgphy_sys &= ~(OTG_SYS_SIDDQ_UOTG);
+ otgphy_sys |= (OTG_SYS_PHY0_SW_RST |
+ OTG_SYS_LINK_SW_RST_UOTG |
+ OTG_SYS_PHYLINK_SW_RESET);
+ writel(otgphy_sys, EXYNOS5_PHY_OTG_SYS);
+ udelay(10);
+ otgphy_sys &= ~(OTG_SYS_PHY0_SW_RST |
+ OTG_SYS_LINK_SW_RST_UOTG |
+ OTG_SYS_PHYLINK_SW_RESET);
+ writel(otgphy_sys, EXYNOS5_PHY_OTG_SYS);
+
+ /* reset all ports of both PHY and Link */
+ hostphy_ctrl0 = readl(EXYNOS5_PHY_HOST_CTRL0);
+ hostphy_ctrl0 &= ~(HOST_CTRL0_SIDDQ | HOST_CTRL0_FORCESUSPEND);
+ hostphy_ctrl0 |= (HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST);
+ writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
+ udelay(10);
+ hostphy_ctrl0 &= ~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST);
+ writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
+
+ /* HSIC phy reset */
+ hsic_ctrl = readl(EXYNOS5_PHY_HSIC_CTRL1);
+ hsic_ctrl &= ~(HSIC_CTRL_SIDDQ | HSIC_CTRL_FORCESUSPEND);
+ hsic_ctrl |= (HSIC_CTRL_PHYSWRST | HSIC_CTRL_UTMISWRST);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+ udelay(10);
+ hsic_ctrl &= ~(HSIC_CTRL_PHYSWRST | HSIC_CTRL_UTMISWRST);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+
+ usb_phy_control.lpa_entered = 0;
+ err = 1;
+ }
+ udelay(80);
+
+ return err;
+}
+
+static int exynos5_usb_phy20_init(struct platform_device *pdev)
+{
+ u32 refclk_freq;
+ u32 hostphy_ctrl0, otgphy_sys, hsic_ctrl, ehcictrl, ohcictrl;
+
+ atomic_inc(&host_usage);
+
+ if (exynos5_usb_host_phy20_is_on()) {
+ dev_err(&pdev->dev, "Already power on PHY\n");
+ return 0;
+ }
+
+ exynos_usb_mux_change(pdev, 1);
+
+ exynos_usb_phy_control(USB_PHY1, PHY_ENABLE);
+
+ /* Host and Device should be set at the same time */
+ hostphy_ctrl0 = readl(EXYNOS5_PHY_HOST_CTRL0);
+ hostphy_ctrl0 &= ~(HOST_CTRL0_FSEL_MASK);
+ otgphy_sys = readl(EXYNOS5_PHY_OTG_SYS);
+ otgphy_sys &= ~(OTG_SYS_CTRL0_FSEL_MASK);
+
+ /* 2.0 phy reference clock configuration */
+ refclk_freq = exynos_usb_phy_set_clock(pdev);
+ hostphy_ctrl0 |= (refclk_freq << HOST_CTRL0_CLKSEL_SHIFT);
+ otgphy_sys |= (refclk_freq << OTG_SYS_CLKSEL_SHIFT);
+
+ /* COMMON Block configuration during suspend */
+ hostphy_ctrl0 |= HOST_CTRL0_COMMONON_N;
+ otgphy_sys &= ~(OTG_SYS_COMMON_ON);
+
+ /* otg phy reset */
+ otgphy_sys &= ~(OTG_SYS_FORCE_SUSPEND | OTG_SYS_SIDDQ_UOTG | OTG_SYS_FORCE_SLEEP);
+ otgphy_sys &= ~(OTG_SYS_REF_CLK_SEL_MASK);
+ otgphy_sys |= (OTG_SYS_REF_CLK_SEL(0x2) | OTG_SYS_OTGDISABLE);
+ otgphy_sys |= (OTG_SYS_PHY0_SW_RST | OTG_SYS_LINK_SW_RST_UOTG | OTG_SYS_PHYLINK_SW_RESET);
+ writel(otgphy_sys, EXYNOS5_PHY_OTG_SYS);
+ udelay(10);
+ otgphy_sys &= ~(OTG_SYS_PHY0_SW_RST | OTG_SYS_LINK_SW_RST_UOTG | OTG_SYS_PHYLINK_SW_RESET);
+ writel(otgphy_sys, EXYNOS5_PHY_OTG_SYS);
+
+ /* host phy reset */
+ hostphy_ctrl0 &= ~(HOST_CTRL0_PHYSWRST | HOST_CTRL0_PHYSWRSTALL | HOST_CTRL0_SIDDQ);
+ hostphy_ctrl0 &= ~(HOST_CTRL0_FORCESUSPEND | HOST_CTRL0_FORCESLEEP);
+ hostphy_ctrl0 |= (HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST);
+ writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
+ udelay(10);
+ hostphy_ctrl0 &= ~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST);
+ writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
+
+ /* HSIC phy reset */
+ hsic_ctrl = (HSIC_CTRL_REFCLKDIV(0x24) | HSIC_CTRL_REFCLKSEL(0x2) |
+ HSIC_CTRL_PHYSWRST);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+ udelay(10);
+ hsic_ctrl &= ~(HSIC_CTRL_PHYSWRST);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+
+ udelay(80);
+ /* enable EHCI DMA burst */
+ ehcictrl = readl(EXYNOS5_PHY_HOST_EHCICTRL);
+ ehcictrl |= (EHCICTRL_ENAINCRXALIGN | EHCICTRL_ENAINCR4 |
+ EHCICTRL_ENAINCR8 | EHCICTRL_ENAINCR16);
+ writel(ehcictrl, EXYNOS5_PHY_HOST_EHCICTRL);
+ /* set ohci_suspend_on_n */
+ ohcictrl = readl(EXYNOS5_PHY_HOST_OHCICTRL);
+ ohcictrl |= OHCICTRL_SUSPLGCY;
+ writel(ohcictrl, EXYNOS5_PHY_HOST_OHCICTRL);
+ return 0;
+}
+
+static int exynos5_usb_phy20_exit(struct platform_device *pdev)
+{
+ u32 hostphy_ctrl0, otgphy_sys, hsic_ctrl;
+
+ if (atomic_dec_return(&host_usage) > 0) {
+ dev_info(&pdev->dev, "still being used\n");
+ return -EBUSY;
+ }
+
+ hsic_ctrl = (HSIC_CTRL_REFCLKDIV(0x24) | HSIC_CTRL_REFCLKSEL(0x2) |
+ HSIC_CTRL_SIDDQ | HSIC_CTRL_FORCESLEEP | HSIC_CTRL_FORCESUSPEND);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+
+ hostphy_ctrl0 = readl(EXYNOS5_PHY_HOST_CTRL0);
+ hostphy_ctrl0 |= (HOST_CTRL0_SIDDQ);
+ hostphy_ctrl0 |= (HOST_CTRL0_FORCESUSPEND | HOST_CTRL0_FORCESLEEP);
+ hostphy_ctrl0 |= (HOST_CTRL0_PHYSWRST | HOST_CTRL0_PHYSWRSTALL);
+ writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
+
+ otgphy_sys = readl(EXYNOS5_PHY_OTG_SYS);
+ otgphy_sys |= (OTG_SYS_FORCE_SUSPEND | OTG_SYS_SIDDQ_UOTG | OTG_SYS_FORCE_SLEEP);
+ writel(otgphy_sys, EXYNOS5_PHY_OTG_SYS);
+
+ exynos_usb_phy_control(USB_PHY1, 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);
+ if (usb_phy_control.lpa_entered)
+ exynos4_usb_phy1_suspend(pdev);
+ } else {
+ exynos5_usb_phy20_init(pdev);
+ if (usb_phy_control.lpa_entered)
+ exynos5_usb_phy_host_suspend(pdev);
+ }
+
+ exynos_usb_mux_change(pdev, 0);
+
+ 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);
+ else
+ exynos5_usb_phy20_exit(pdev);
+
+ exynos_usb_mux_change(pdev, 1);
+
+ return 0;
+}
+
+static int __maybe_unused exynos_usb_hsic_init(struct platform_device *pdev)
+{
+ u32 rstcon, hsic_ctrl;
+
+ if (soc_is_exynos4212() || soc_is_exynos4412()) {
+ exynos_usb_phy_control(USB_PHY_HSIC0
+ | USB_PHY_HSIC1,
+ PHY_ENABLE);
+
+ /* reset both PHY and Link of Host */
+ rstcon = readl(EXYNOS4_RSTCON)
+ | EXYNOS4212_PHY1_HSIC0_SWRST
+ | EXYNOS4212_PHY1_HSIC1_SWRST;
+ writel(rstcon, EXYNOS4_RSTCON);
+ udelay(10);
+
+ rstcon &= ~(EXYNOS4212_PHY1_HSIC0_SWRST
+ | EXYNOS4212_PHY1_HSIC1_SWRST);
+ writel(rstcon, EXYNOS4_RSTCON);
+ } else {
+ /* HSIC phy reset */
+ hsic_ctrl = (HSIC_CTRL_REFCLKDIV(0x24) | HSIC_CTRL_REFCLKSEL(0x2) |
+ HSIC_CTRL_PHYSWRST);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+ udelay(10);
+ hsic_ctrl &= ~(HSIC_CTRL_PHYSWRST);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused exynos_usb_hsic_exit(struct platform_device *pdev)
+{
+ u32 hsic_ctrl;
+
+ if (soc_is_exynos4212() || soc_is_exynos4412()) {
+ exynos_usb_phy_control(USB_PHY_HSIC0
+ | USB_PHY_HSIC1,
+ PHY_DISABLE);
+ } else {
+ hsic_ctrl = (HSIC_CTRL_REFCLKDIV(0x24) | HSIC_CTRL_REFCLKSEL(0x2) |
+ HSIC_CTRL_SIDDQ | HSIC_CTRL_FORCESLEEP | HSIC_CTRL_FORCESUSPEND);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL1);
+ writel(hsic_ctrl, EXYNOS5_PHY_HSIC_CTRL2);
+ }
+
+ return 0;
+}
+
+static int exynos5_usb_phy30_init(struct platform_device *pdev)
+{
+ u32 reg;
+ bool use_ext_clk = true;
+
+ exynos_usb_phy_control(USB_PHY0, PHY_ENABLE);
+
+ /* Reset USB 3.0 PHY */
+ writel(0x00000000, EXYNOS_USB3_PHYREG0);
+ writel(0x24d4e6e4, EXYNOS_USB3_PHYPARAM0);
+ writel(0x03fff820, EXYNOS_USB3_PHYPARAM1);
+ writel(0x00000000, EXYNOS_USB3_PHYRESUME);
+
+ if (soc_is_exynos5250() && samsung_rev() < EXYNOS5250_REV_1_0) {
+ writel(0x087fffc0, EXYNOS_USB3_LINKSYSTEM);
+ writel(0x00000000, EXYNOS_USB3_PHYBATCHG);
+ /* Over-current pin is inactive on SMDK5250 rev 0.0 */
+ writel((readl(EXYNOS_USB3_LINKPORT) & ~(0x3<<4)) |
+ (0x3<<2), EXYNOS_USB3_LINKPORT);
+ } else {
+ writel(0x08000000, EXYNOS_USB3_LINKSYSTEM);
+ writel(0x00000004, EXYNOS_USB3_PHYBATCHG);
+#ifdef CONFIG_USB_EXYNOS_SWITCH
+ writel(readl(EXYNOS_USB3_LINKPORT) |
+ (0xf<<2), EXYNOS_USB3_LINKPORT);
+#endif
+ /* REVISIT :use externel clock 100MHz */
+ if (use_ext_clk)
+ writel(readl(EXYNOS_USB3_PHYPARAM0) | (0x1<<31),
+ EXYNOS_USB3_PHYPARAM0);
+ else
+ writel(readl(EXYNOS_USB3_PHYPARAM0) & ~(0x1<<31),
+ EXYNOS_USB3_PHYPARAM0);
+ }
+
+ /* UTMI Power Control */
+ writel(EXYNOS_USB3_PHYUTMI_OTGDISABLE, EXYNOS_USB3_PHYUTMI);
+
+ /* Set 100MHz external clock */
+ reg = EXYNOS_USB3_PHYCLKRST_PORTRESET |
+ /* HS PLL uses ref_pad_clk{p,m} or ref_alt_clk_{p,m}
+ * as reference */
+ EXYNOS_USB3_PHYCLKRST_REFCLKSEL(2) |
+ /* Digital power supply in normal operating mode */
+ EXYNOS_USB3_PHYCLKRST_RETENABLEN |
+ /* 0x27-100MHz, 0x2a-24MHz, 0x31-20MHz, 0x38-19.2MHz */
+ EXYNOS_USB3_PHYCLKRST_FSEL(0x27) |
+ /* 0x19-100MHz, 0x68-24MHz, 0x7d-20Mhz */
+ EXYNOS_USB3_PHYCLKRST_MPLL_MULTIPLIER(0x19) |
+ /* Enable ref clock for SS function */
+ EXYNOS_USB3_PHYCLKRST_REF_SSP_EN |
+ /* Enable spread spectrum */
+ EXYNOS_USB3_PHYCLKRST_SSC_EN;
+
+ if (!(soc_is_exynos5250() && samsung_rev() < EXYNOS5250_REV_1_0))
+ reg |= EXYNOS_USB3_PHYCLKRST_COMMONONN;
+
+ writel(reg, EXYNOS_USB3_PHYCLKRST);
+
+ udelay(10);
+
+ reg &= ~(EXYNOS_USB3_PHYCLKRST_PORTRESET);
+ writel(reg, EXYNOS_USB3_PHYCLKRST);
+
+ return 0;
+}
+
+static int exynos5_usb_phy30_exit(struct platform_device *pdev)
+{
+ u32 reg;
+
+ reg = EXYNOS_USB3_PHYUTMI_OTGDISABLE |
+ EXYNOS_USB3_PHYUTMI_FORCESUSPEND |
+ EXYNOS_USB3_PHYUTMI_FORCESLEEP;
+ writel(reg, EXYNOS_USB3_PHYUTMI);
+
+ exynos_usb_phy_control(USB_PHY0, PHY_DISABLE);
+
+ return 0;
+}
+
+int exynos4_check_usb_op(void)
+{
+ u32 phypwr;
+ u32 op = 1;
+ unsigned long flags;
+ int ret;
+
+ ret = clk_enable(phy_clk);
+ if (ret)
+ return 0;
+
+ local_irq_save(flags);
+ phypwr = readl(EXYNOS4_PHYPWR);
+
+ /*If USB Device is power on, */
+ if (exynos_usb_device_phy_is_on()) {
+ op = 1;
+ goto done;
+ } else if (!exynos4_usb_host_phy_is_on()) {
+ op = 0;
+ goto done;
+ }
+
+ /*If USB Device & Host is suspended, */
+ if (soc_is_exynos4210()) {
+ if (phypwr & (PHY1_STD_FORCE_SUSPEND
+ | EXYNOS4210_HSIC0_FORCE_SUSPEND
+ | EXYNOS4210_HSIC1_FORCE_SUSPEND)) {
+#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
+ /* HSIC LPA: LPA USB phy retention reume call the usb
+ * reset resume, so we should let CP to HSIC L3 mode. */
+ set_hsic_lpa_states(STATE_HSIC_LPA_ENTER);
+#endif
+ writel(readl(EXYNOS4_PHYPWR)
+ | PHY1_STD_ANALOG_POWERDOWN,
+ EXYNOS4_PHYPWR);
+ writel(PHY_DISABLE, S5P_USBHOST_PHY_CONTROL);
+
+ op = 0;
+ }
+ } else {
+ if (phypwr & (PHY1_STD_FORCE_SUSPEND
+ | EXYNOS4212_HSIC0_FORCE_SUSPEND
+ | EXYNOS4212_HSIC1_FORCE_SUSPEND)) {
+#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
+ /* HSIC LPA: LPA USB phy retention reume call the usb
+ * reset resume, so we should let CP to HSIC L3 mode. */
+ set_hsic_lpa_states(STATE_HSIC_LPA_ENTER);
+#endif
+ /* unset to normal of Host */
+ writel(readl(EXYNOS4_PHYPWR)
+ | PHY1_STD_ANALOG_POWERDOWN
+ | EXYNOS4212_HSIC0_ANALOG_POWERDOWN
+ | EXYNOS4212_HSIC1_ANALOG_POWERDOWN,
+ EXYNOS4_PHYPWR);
+ /* unset to normal of Device */
+ writel((readl(EXYNOS4_PHYPWR) | PHY0_NORMAL_MASK),
+ EXYNOS4_PHYPWR);
+
+ exynos_usb_phy_control(USB_PHY
+ | USB_PHY_HSIC0
+ | USB_PHY_HSIC1,
+ PHY_DISABLE);
+
+ op = 0;
+ usb_phy_control.lpa_entered = 1;
+ }
+ }
+done:
+ local_irq_restore(flags);
+ clk_disable(phy_clk);
+
+ return op;
+}
+
+static int exynos5_check_usb_op(void)
+{
+ u32 hostphy_ctrl0, otgphy_sys;
+ u32 op = 1;
+ unsigned long flags;
+ int ret;
+
+ ret = clk_enable(phy_clk);
+ if (ret)
+ return 0;
+
+ local_irq_save(flags);
+ /* Check USB 3.0 DRD power */
+ if (exynos5_usb_phy30_is_on()) {
+ op = 1;
+ goto done;
+ }
+ /*If USB Device is power on, */
+ if (exynos_usb_device_phy_is_on()) {
+ op = 1;
+ goto done;
+ } else if (!exynos5_usb_host_phy20_is_on()) {
+ op = 0;
+ goto done;
+ }
+
+ hostphy_ctrl0 = readl(EXYNOS5_PHY_HOST_CTRL0);
+
+ if (hostphy_ctrl0 & HOST_CTRL0_FORCESUSPEND) {
+ /* unset to normal of Host */
+ hostphy_ctrl0 |= (HOST_CTRL0_SIDDQ);
+ writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
+
+ /* unset to normal of Device */
+ otgphy_sys = readl(EXYNOS5_PHY_OTG_SYS);
+ otgphy_sys |= OTG_SYS_SIDDQ_UOTG;
+ writel(otgphy_sys, EXYNOS5_PHY_OTG_SYS);
+
+ exynos_usb_phy_control(USB_PHY1,
+ PHY_DISABLE);
+
+ op = 0;
+ usb_phy_control.lpa_entered = 1;
+ }
+done:
+ local_irq_restore(flags);
+ clk_disable(phy_clk);
+
+ return op;
+}
+
+/**
+ * exynos_check_usb_op - Check usb operation
+ *
+ * USB operation is checked for AP Power mode.
+ * NOTE: Should be checked before Entering AP power mode.
+ * exynos4 - USB Host & Device
+ * exynos5 - USB Host & Device & DRD
+ *
+ * return 1 : operation, 0 : stop.
+ */
+int exynos_check_usb_op(void)
+{
+ if (soc_is_exynos4210() ||
+ soc_is_exynos4212() ||
+ soc_is_exynos4412())
+ return exynos4_check_usb_op();
+ else
+ return exynos5_check_usb_op();
+}
+
+int s5p_usb_phy_suspend(struct platform_device *pdev, int type)
+{
+ int ret = 0;
+#ifdef CONFIG_USB_OHCI_S5P
+ struct s5p_ohci_hcd *s5p_ohci = platform_get_drvdata(&s5p_device_ohci);
+ struct usb_hcd *ohci_hcd = s5p_ohci->hcd;
+ u32 phyclk;
+#endif
+
+ if (exynos_usb_phy_clock_enable(pdev))
+ return 0;
+
+ mutex_lock(&phy_lock);
+
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ clear_bit(HOST_PHY_EHCI, &usb_phy_control.flags);
+ else if (!strcmp(pdev->name, "s5p-ohci"))
+ clear_bit(HOST_PHY_OHCI, &usb_phy_control.flags);
+
+ if (usb_phy_control.flags)
+ goto done;
+
+ if (type == S5P_USB_PHY_HOST) {
+ if (soc_is_exynos4210() ||
+ soc_is_exynos4212() ||
+ soc_is_exynos4412()) {
+#ifdef CONFIG_USB_OHCI_S5P
+ /* Set OHCI clock off when ohci_hcd is suspended */
+ if (ohci_hcd->state == HC_STATE_SUSPENDED) {
+ phyclk = readl(EXYNOS4_PHYCLK);
+ phyclk &= ~(PHY1_COMMON_ON_N);
+ writel(phyclk, EXYNOS4_PHYCLK);
+ }
+ dev_info(&pdev->dev, "host_phy_susp:%d\n",
+ ohci_hcd->state);
+#endif
+ ret = exynos4_usb_phy1_suspend(pdev);
+ } else
+ ret = exynos5_usb_phy_host_suspend(pdev);
+ }
+done:
+ mutex_unlock(&phy_lock);
+ exynos_usb_phy_clock_disable(pdev);
+
+ return ret;
+}
+
+int s5p_usb_phy_resume(struct platform_device *pdev, int type)
+{
+ int ret = 0;
+ u32 phyclk;
+
+ if (exynos_usb_phy_clock_enable(pdev))
+ return 0;
+
+ mutex_lock(&phy_lock);
+
+ if (usb_phy_control.flags)
+ goto done;
+
+ if (type == S5P_USB_PHY_HOST) {
+ if (soc_is_exynos4210() ||
+ soc_is_exynos4212() ||
+ soc_is_exynos4412()) {
+ dev_info(&pdev->dev, "host_phy_resume\n");
+#ifdef CONFIG_USB_OHCI_S5P
+ phyclk = readl(EXYNOS4_PHYCLK);
+ phyclk |= PHY1_COMMON_ON_N;
+ writel(phyclk, EXYNOS4_PHYCLK);
+#endif
+ ret = exynos4_usb_phy1_resume(pdev);
+ } else
+ ret = exynos5_usb_phy_host_resume(pdev);
+ }
+done:
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ set_bit(HOST_PHY_EHCI, &usb_phy_control.flags);
+ else if (!strcmp(pdev->name, "s5p-ohci"))
+ set_bit(HOST_PHY_OHCI, &usb_phy_control.flags);
+
+ mutex_unlock(&phy_lock);
+ exynos_usb_phy_clock_disable(pdev);
+
+ return ret;
+}
+
+int s5p_usb_phy0_tune(struct s5p_usbgadget_platdata *pdata, int def_mode)
+{
+ u32 phytune;
+ static u32 def_phytune;
+
+ if (!pdata)
+ return -1;
+
+ printk(KERN_DEBUG "usb: %s read original tune\n", __func__);
+ phytune = readl(PHY0_PHYTUNE);
+ if (!def_phytune) {
+ def_phytune = phytune;
+ printk(KERN_DEBUG "usb: %s save default phytune (0x%x)\n",
+ __func__, def_phytune);
+ }
+
+ printk(KERN_DEBUG "usb: %s original tune=0x%x\n",
+ __func__, phytune);
+ printk(KERN_DEBUG "usb: %s tune_mask=0x%x, tune=0x%x\n",
+ __func__, pdata->phy_tune_mask, pdata->phy_tune);
+
+ if (pdata->phy_tune_mask) {
+ phytune &= ~(pdata->phy_tune_mask);
+ phytune |= pdata->phy_tune;
+ udelay(10);
+ if (def_mode) {
+ printk(KERN_DEBUG "usb: %s set defult tune=0x%x\n",
+ __func__, def_phytune);
+ writel(def_phytune, PHY0_PHYTUNE);
+
+ } else {
+ printk(KERN_DEBUG "usb: %s custom tune=0x%x\n",
+ __func__, phytune);
+ writel(phytune, PHY0_PHYTUNE);
+ }
+ phytune = readl(PHY0_PHYTUNE);
+ printk(KERN_DEBUG "usb: %s modified tune=0x%x\n",
+ __func__, phytune);
+ } else
+ printk(KERN_DEBUG "usb: %s default tune\n", __func__);
+
+ return 0;
+}
+
+void set_exynos_usb_phy_tune(int type)
+{
+ u32 phytune;
+ if (soc_is_exynos4412()) {
+ if (type == S5P_USB_PHY_DEVICE) {
+ phytune = readl(PHY0_PHYTUNE);
+ printk(KERN_DEBUG "usb: %s old phy0 tune=0x%x t=%d\n",
+ __func__, phytune, type);
+ /* sqrxtune [13:11] 3b110 : -15% */
+ phytune &= ~(0x7 << 11);
+ phytune |= (0x6 << 11);
+ udelay(10);
+ writel(phytune, PHY0_PHYTUNE);
+ phytune = readl(PHY0_PHYTUNE);
+ printk(KERN_DEBUG "usb: %s new phy0 tune=0x%x\n",
+ __func__, phytune);
+ } else if (type == S5P_USB_PHY_HOST) {
+ phytune = readl(PHY1_PHYTUNE);
+ printk(KERN_DEBUG "usb: %s old phy1 tune=0x%x t=%d\n",
+ __func__, phytune, type);
+ /* sqrxtune [13:11] 3b110 : -15% */
+ phytune &= ~(0x7 << 11);
+ phytune |= (0x6 << 11);
+ udelay(10);
+ writel(phytune, PHY1_PHYTUNE);
+ phytune = readl(PHY1_PHYTUNE);
+ printk(KERN_DEBUG "usb: %s new phy1 tune=0x%x\n",
+ __func__, phytune);
+ }
+ } else
+ printk(KERN_DEBUG "usb: %s it is not exynos4412.(t=%d)\n",
+ __func__, type);
+}
+
+int s5p_usb_phy_init(struct platform_device *pdev, int type)
+{
+ int ret = -EINVAL;
+
+ if (exynos_usb_phy_clock_enable(pdev))
+ return ret;
+
+ mutex_lock(&phy_lock);
+
+ if (type == S5P_USB_PHY_HOST) {
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ set_bit(HOST_PHY_EHCI, &usb_phy_control.flags);
+ else if (!strcmp(pdev->name, "s5p-ohci"))
+ set_bit(HOST_PHY_OHCI, &usb_phy_control.flags);
+
+#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
+ /* HSIC LPA: Let CP know the slave wakeup from LPA wakeup */
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ set_hsic_lpa_states(STATE_HSIC_LPA_PHY_INIT);
+#endif
+ if (soc_is_exynos4210())
+ ret = exynos4_usb_phy1_init(pdev);
+ else if (soc_is_exynos4212() || soc_is_exynos4412()) {
+ ret = exynos4_usb_phy20_init(pdev);
+ set_exynos_usb_phy_tune(type);
+ } else
+ ret = exynos5_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);
+ set_exynos_usb_phy_tune(type);
+ }
+ /* set custom usb phy tune */
+ if (pdev->dev.platform_data)
+ ret = s5p_usb_phy0_tune(pdev->dev.platform_data, 0);
+ } else if (type == S5P_USB_PHY_OTGHOST) {
+ if (soc_is_exynos4210())
+ ret = exynos4_usb_phy0_init(pdev);
+ else
+ ret = exynos_usb_dev_phy20_init(pdev);
+ } else if (type == S5P_USB_PHY_DRD)
+ ret = exynos5_usb_phy30_init(pdev);
+
+ mutex_unlock(&phy_lock);
+ exynos_usb_phy_clock_disable(pdev);
+
+ return ret;
+}
+
+int s5p_usb_phy_exit(struct platform_device *pdev, int type)
+{
+ 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
+ ret = exynos5_usb_phy20_exit(pdev);
+
+ if (!strcmp(pdev->name, "s5p-ehci"))
+ clear_bit(HOST_PHY_EHCI, &usb_phy_control.flags);
+ else if (!strcmp(pdev->name, "s5p-ohci"))
+ clear_bit(HOST_PHY_OHCI, &usb_phy_control.flags);
+ } else if (type == S5P_USB_PHY_DEVICE) {
+ /* set default usb phy tune */
+ if (pdev->dev.platform_data && soc_is_exynos4210())
+ ret = s5p_usb_phy0_tune(pdev->dev.platform_data, 1);
+ if (soc_is_exynos4210())
+ ret = exynos4_usb_phy0_exit(pdev);
+ else
+ ret = exynos_usb_dev_phy20_exit(pdev);
+ } else if (type == S5P_USB_PHY_DRD)
+ ret = exynos5_usb_phy30_exit(pdev);
+ else if (type == S5P_USB_PHY_OTGHOST) {
+ 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 ret;
+}