aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuvaraj CD <yuvaraj.cd@gmail.com>2012-12-26 10:48:11 +0530
committerAndrey Konovalov <andrey.konovalov@linaro.org>2013-06-18 01:22:08 +0400
commit3995398445569450f2b3d13cd06f85e2d2ada90a (patch)
tree040f54a707ee1411f590744e7175db4a077d6af8
parentf04cab1f04c84d4504a0b0444271ebeb0c27ff83 (diff)
USB3.0: Migration of Samsung USBPHY changes
Signed-off-by: Yuvaraj CD <yuvaraj.cd@gmail.com>
-rw-r--r--arch/arm/boot/dts/exynos5250.dtsi6
-rw-r--r--arch/arm/mach-exynos/include/mach/map.h2
-rw-r--r--arch/arm/mach-exynos/include/mach/regs-pmu.h4
-rw-r--r--arch/arm/mach-exynos/mach-exynos5-dt.c14
-rw-r--r--arch/arm/mach-exynos/setup-usb-phy.c214
-rw-r--r--arch/arm/plat-samsung/include/plat/usb-phy.h8
-rw-r--r--drivers/usb/Makefile2
-rw-r--r--drivers/usb/core/hub.c35
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/dwc3/core.c8
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c2
-rw-r--r--drivers/usb/host/ehci-s5p.c74
-rw-r--r--drivers/usb/host/ohci-exynos.c77
-rw-r--r--drivers/usb/host/xhci.c2
-rw-r--r--drivers/usb/otg/otg.c3
-rw-r--r--drivers/usb/phy/Kconfig7
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/samsung-usbphy.c1004
-rw-r--r--include/linux/platform_data/samsung-usbphy.h29
-rw-r--r--include/linux/usb/samsung_usb_phy.h29
20 files changed, 1279 insertions, 243 deletions
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index a723bc363c67..6b63be7fd084 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -81,6 +81,11 @@
interrupts = <0 96 0>;
};
+ usbphy {
+ compatible = "samsung,exynos5250-usbphy";
+ reg = <0x12130000 0x100>, <0x12100000 0x100>;
+ };
+
usb@12000000 {
compatible = "samsung,exynos-dwc3";
reg = <0x12000000 0x10000>;
@@ -275,6 +280,7 @@
#size-cells = <0>;
};
+
amba {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index 903424dbaf87..858253be187a 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -188,7 +188,7 @@
#define EXYNOS4_PA_HSOTG 0x12480000
#define EXYNOS4_PA_USB_HSPHY 0x125B0000
-
+#define EXYNOS5_PA_HSPHY 0x12130000
#define EXYNOS4_PA_SATA 0x12560000
#define EXYNOS4_PA_SATAPHY 0x125D0000
#define EXYNOS4_PA_SATAPHY_CTRL 0x126B0000
diff --git a/arch/arm/mach-exynos/include/mach/regs-pmu.h b/arch/arm/mach-exynos/include/mach/regs-pmu.h
index 3f30aa1ae354..dfc39f204d18 100644
--- a/arch/arm/mach-exynos/include/mach/regs-pmu.h
+++ b/arch/arm/mach-exynos/include/mach/regs-pmu.h
@@ -41,6 +41,10 @@
#define S5P_HDMI_PHY_CONTROL S5P_PMUREG(0x0700)
#define S5P_HDMI_PHY_ENABLE (1 << 0)
+/* only for EXYNOS5250*/
+#define S5P_USBDRD_PHY_CONTROL S5P_PMUREG(0x0704)
+#define S5P_USBDRD_PHY_ENABLE (1 << 0)
+
#define S5P_DAC_PHY_CONTROL S5P_PMUREG(0x070C)
#define S5P_DAC_PHY_ENABLE (1 << 0)
diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c
index eaeede26c1b7..752b16860c96 100644
--- a/arch/arm/mach-exynos/mach-exynos5-dt.c
+++ b/arch/arm/mach-exynos/mach-exynos5-dt.c
@@ -17,6 +17,7 @@
#include <asm/mach/arch.h>
#include <asm/hardware/gic.h>
+
#include <mach/map.h>
#include <mach/regs-pmu.h>
@@ -26,6 +27,17 @@
#include "common.h"
+#include <plat/regs-srom.h>
+#include <plat/devs.h>
+#include <plat/usb-phy.h>
+
+#include <linux/platform_data/samsung-usbphy.h>
+
+static struct samsung_usbphy_data exynos5_usbphy_pdata = {
+ .pmu_isolation = s5p_usb_phy_pmu_isolation,
+ .phy_cfg_sel = s5p_usb_phy_cfg_sel,
+};
+
/*
* The following lookup table is used to override device names when devices
* are registered from device tree. This is temporarily added to enable
@@ -104,6 +116,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = {
OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL),
OF_DEV_AUXDATA("samsung,exynos5250-tmu", 0x10060000,
"exynos-tmu", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5250-usbphy", EXYNOS5_PA_HSPHY,
+ "s3c-usbphy", &exynos5_usbphy_pdata),
OF_DEV_AUXDATA("samsung,exynos-dwc3", EXYNOS5_PA_DRD,
"exynos-dwc3", NULL),
{},
diff --git a/arch/arm/mach-exynos/setup-usb-phy.c b/arch/arm/mach-exynos/setup-usb-phy.c
index 3101c1b3a293..78100981b0be 100644
--- a/arch/arm/mach-exynos/setup-usb-phy.c
+++ b/arch/arm/mach-exynos/setup-usb-phy.c
@@ -14,21 +14,18 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/usb/samsung_usb_phy.h>
+#include <linux/platform_data/samsung-usbphy.h>
#include <mach/regs-pmu.h>
#include <mach/regs-usb-phy.h>
#include <plat/cpu.h>
+#include <plat/map-base.h>
#include <plat/usb-phy.h>
#define PHY_ENABLE 1
#define PHY_DISABLE 0
#define EXYNOS5_USB_CFG (S3C_VA_SYS + 0x230)
-#define EXYNOS5_USB_CFG (S3C_VA_SYS + 0x230)
-
-enum usb_phy_type {
- USB_PHY = (0x1 << 0),
-};
-
static atomic_t host_usage;
static int exynos4_usb_host_phy_is_on(void)
@@ -163,7 +160,7 @@ static int exynos4210_usb_phy_clkset(struct platform_device *pdev)
}
return phyclk;
}
-
+#if 0
static void exynos_usb_phy_control(enum usb_phy_type phy_type , int on)
{
if (soc_is_exynos5250()) {
@@ -171,7 +168,7 @@ static void exynos_usb_phy_control(enum usb_phy_type phy_type , int on)
writel(on, S5P_USBHOST_PHY_CONTROL);
}
}
-
+#endif
static int exynos4210_usb_phy0_init(struct platform_device *pdev)
{
u32 rstcon;
@@ -274,156 +271,12 @@ static int exynos4210_usb_phy1_exit(struct platform_device *pdev)
return 0;
}
-static int exynos5_usb_phy20_init(struct platform_device *pdev)
-{
- struct clk *host_clk;
- u32 refclk_freq;
- u32 hostphy_ctrl0;
- u32 otgphy_sys;
- u32 hsic_ctrl;
- u32 ehcictrl;
- u32 ohcictrl;
-
- atomic_inc(&host_usage);
- host_clk = exynos_usb_clock_enable(pdev);
- if (host_clk == NULL) {
- dev_err(&pdev->dev, "Failed to enable USB2.0 host clock\n");
- return -1;
- }
-
- if (exynos4_usb_host_phy_is_on()) {
- dev_err(&pdev->dev, "Already power on PHY\n");
- return 0;
- }
-
- exynos_usb_mux_change(pdev, 1);
-
- exynos_usb_phy_control(USB_PHY, 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 = exynos4210_usb_phy_clkset(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 << OTG_SYS_REF_CLK_SEL_SHIFT);
- otgphy_sys |= (((OTG_SYS_REF_CLK_SEL_CLKCORE & OTG_SYS_REF_CLK_SEL_MASK)
- << OTG_SYS_REF_CLK_SEL_SHIFT)
- | 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_12 & HSIC_CTRL_REFCLKDIV_MASK)
- << HSIC_CTRL_REFCLKDIV_SHIFT)
- | ((HSIC_CTRL_REFCLKSEL & HSIC_CTRL_REFCLKSEL_MASK)
- << HSIC_CTRL_REFCLKSEL_SHIFT)
- | 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);
-
- clk_disable(host_clk);
- clk_put(host_clk);
- return 0;
-}
-
-static int exynos5_usb_phy20_exit(struct platform_device *pdev)
-{
- struct clk *host_clk;
- u32 hostphy_ctrl0;
- u32 otgphy_sys;
- u32 hsic_ctrl;
-
- if (atomic_dec_return(&host_usage) > 0) {
- dev_info(&pdev->dev, "still being used\n");
- return -EBUSY;
- }
-
- host_clk = exynos_usb_clock_enable(pdev);
- if (host_clk == NULL) {
- dev_err(&pdev->dev, "Failed to enable otg clock this time\n");
- return -1;
- }
-
- hsic_ctrl = (((HSIC_CTRL_REFCLKDIV_12 & HSIC_CTRL_REFCLKDIV_MASK)
- << HSIC_CTRL_REFCLKDIV_SHIFT)
- | ((HSIC_CTRL_REFCLKSEL & HSIC_CTRL_REFCLKSEL_MASK)
- << HSIC_CTRL_REFCLKSEL_SHIFT)
- | 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_PHY, PHY_DISABLE);
-
- clk_disable(host_clk);
- clk_put(host_clk);
- return 0;
-}
int s5p_usb_phy_init(struct platform_device *pdev, int type)
{
- if (type == S5P_USB_PHY_DEVICE)
+ if (type == USB_PHY_TYPE_DEVICE)
return exynos4210_usb_phy0_init(pdev);
- else if (type == S5P_USB_PHY_HOST) {
- if (soc_is_exynos5250())
- return exynos5_usb_phy20_init(pdev);
- else
+ else if (type == USB_PHY_TYPE_HOST) {
return exynos4210_usb_phy1_init(pdev);
}
@@ -432,13 +285,56 @@ int s5p_usb_phy_init(struct platform_device *pdev, int type)
int s5p_usb_phy_exit(struct platform_device *pdev, int type)
{
- if (type == S5P_USB_PHY_DEVICE)
+
+ if (type == USB_PHY_TYPE_DEVICE)
return exynos4210_usb_phy0_exit(pdev);
- else if (type == S5P_USB_PHY_HOST) {
- if (soc_is_exynos5250())
- return exynos5_usb_phy20_exit(pdev);
- else
+ else if (type == USB_PHY_TYPE_HOST) {
return exynos4210_usb_phy1_exit(pdev);
}
return -EINVAL;
}
+
+ void s5p_usb_phy_pmu_isolation(int on, int type)
+ {
+ if (type == USB_PHY_TYPE_HOST) {
+ if (on)
+ writel(readl(S5P_USBHOST_PHY_CONTROL)
+ & ~S5P_USBHOST_PHY_ENABLE,
+ S5P_USBHOST_PHY_CONTROL);
+ else
+ writel(readl(S5P_USBHOST_PHY_CONTROL)
+ | S5P_USBHOST_PHY_ENABLE,
+ S5P_USBHOST_PHY_CONTROL);
+ }else if(type == USB_PHY_TYPE_DRD) {
+ if (on)
+ writel(readl(S5P_USBDRD_PHY_CONTROL)
+ & ~S5P_USBDRD_PHY_ENABLE,
+ S5P_USBDRD_PHY_CONTROL);
+ else
+ writel(readl(S5P_USBDRD_PHY_CONTROL)
+ | S5P_USBDRD_PHY_ENABLE,
+ S5P_USBDRD_PHY_CONTROL);
+ } else {
+ if (on)
+ writel(readl(S5P_USBDEVICE_PHY_CONTROL)
+ & ~S5P_USBDEVICE_PHY_ENABLE,
+ S5P_USBDEVICE_PHY_CONTROL);
+ else
+ writel(readl(S5P_USBDEVICE_PHY_CONTROL)
+ | S5P_USBDEVICE_PHY_ENABLE,
+ S5P_USBDEVICE_PHY_CONTROL);
+ }
+ }
+
+/* Switch between HOST and OTG link from PHY_CFG */
+void s5p_usb_phy_cfg_sel(struct device *dev, int type)
+{
+ u32 is_host;
+
+ is_host = readl(EXYNOS5_USB_CFG);
+ writel(type, EXYNOS5_USB_CFG);
+
+ if (is_host != type)
+ dev_dbg(dev, "Changed USB MUX from %s to %s",
+ is_host ? "Host" : "Device", type ? "Host" : "Device");
+}
diff --git a/arch/arm/plat-samsung/include/plat/usb-phy.h b/arch/arm/plat-samsung/include/plat/usb-phy.h
index 959bcdb03a25..72fb31229127 100644
--- a/arch/arm/plat-samsung/include/plat/usb-phy.h
+++ b/arch/arm/plat-samsung/include/plat/usb-phy.h
@@ -11,12 +11,8 @@
#ifndef __PLAT_SAMSUNG_USB_PHY_H
#define __PLAT_SAMSUNG_USB_PHY_H __FILE__
-enum s5p_usb_phy_type {
- S5P_USB_PHY_DEVICE,
- S5P_USB_PHY_HOST,
-};
-
extern int s5p_usb_phy_init(struct platform_device *pdev, int type);
extern int s5p_usb_phy_exit(struct platform_device *pdev, int type);
-
+extern void s5p_usb_phy_pmu_isolation(int on, int type);
+extern void s5p_usb_phy_cfg_sel(struct device *dev, int type);
#endif /* __PLAT_SAMSUNG_USB_PHY_H */
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index f5ed3d75fa5a..6c21c9187c37 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_OTG_UTILS) += otg/
+obj-$(CONFIG_USB_COMMON) += phy/
obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_MON) += mon/
@@ -46,7 +47,6 @@ obj-$(CONFIG_USB_MICROTEK) += image/
obj-$(CONFIG_USB_SERIAL) += serial/
obj-$(CONFIG_USB) += misc/
-obj-$(CONFIG_USB_COMMON) += phy/
obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
obj-$(CONFIG_USB_ATM) += atm/
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 2a89588def06..b2d007565180 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -836,7 +836,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
unsigned delay;
u16 wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
-
/* Enable power on each port. Some hubs have reserved values
* of LPSM (> 2) in their descriptors, even though they are
* USB 2.0 hubs. Some hubs do not implement port-power switching
@@ -1019,7 +1018,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
int status;
bool need_debounce_delay = false;
unsigned delay;
-
/* Continue a partial initialization */
if (type == HUB_INIT2)
goto init2;
@@ -1100,7 +1098,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
struct usb_device *udev = hub->ports[port1 - 1]->child;
u16 portstatus, portchange;
-
portstatus = portchange = 0;
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
@@ -1386,7 +1383,7 @@ static int hub_configure(struct usb_hub *hub,
}
hdev->maxchild = hub->descriptor->bNbrPorts;
- dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
+ dev_dbg (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *),
@@ -1397,7 +1394,6 @@ static int hub_configure(struct usb_hub *hub,
}
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
-
/* FIXME for USB 3.0, skip for now */
if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
!(hub_is_superspeed(hdev))) {
@@ -1410,9 +1406,9 @@ static int hub_configure(struct usb_hub *hub,
? 'F' : 'R';
portstr[hdev->maxchild] = 0;
dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
- } else
+ } else {
dev_dbg(hub_dev, "standalone hub\n");
-
+ }
switch (wHubCharacteristics & HUB_CHAR_LPSM) {
case HUB_CHAR_COMMON_LPSM:
dev_dbg(hub_dev, "ganged power switching\n");
@@ -1743,7 +1739,7 @@ descriptor_error:
goto descriptor_error;
/* We found a hub */
- dev_info (&intf->dev, "USB hub found\n");
+ dev_dbg (&intf->dev, "USB hub found\n");
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
if (!hub) {
@@ -1770,7 +1766,6 @@ descriptor_error:
if (hub_configure(hub, endpoint) >= 0)
return 0;
-
hub_disconnect (intf);
return -ENODEV;
}
@@ -2062,7 +2057,7 @@ void usb_disconnect(struct usb_device **pdev)
* this quiesces everything except pending urbs.
*/
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
- dev_info(&udev->dev, "USB disconnect, device number %d\n",
+ dev_dbg(&udev->dev, "USB disconnect, device number %d\n",
udev->devnum);
usb_lock_device(udev);
@@ -2115,10 +2110,10 @@ static void show_string(struct usb_device *udev, char *id, char *string)
static void announce_device(struct usb_device *udev)
{
- dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
+ dev_dbg(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
udev->descriptor.iManufacturer,
udev->descriptor.iProduct,
@@ -2164,7 +2159,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
if (desc->bmAttributes & USB_OTG_HNP) {
unsigned port1 = udev->portnum;
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
? "" : "non-");
@@ -2183,7 +2178,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
/* OTG MESSAGE: report errors here,
* customize to match your product.
*/
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"can't set HNP mode: %d\n",
err);
bus->b_hnp_enable = 0;
@@ -2466,7 +2461,7 @@ int usb_authorize_device(struct usb_device *usb_dev)
* set other configurations. */
}
}
- dev_info(&usb_dev->dev, "authorized to connect\n");
+ dev_dbg(&usb_dev->dev, "authorized to connect\n");
error_enumerate:
error_device_descriptor:
@@ -3996,7 +3991,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
speed = usb_speed_string(udev->speed);
if (udev->speed != USB_SPEED_SUPER)
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"%s %s USB device number %d using %s\n",
(udev->config) ? "reset" : "new", speed,
devnum, udev->bus->controller->driver->name);
@@ -4110,7 +4105,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
}
if (udev->speed == USB_SPEED_SUPER) {
devnum = udev->devnum;
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"%s SuperSpeed USB device number %d using %s\n",
(udev->config) ? "reset" : "new",
devnum, udev->bus->controller->driver->name);
@@ -4222,7 +4217,7 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0,
qual, sizeof *qual);
if (status == sizeof *qual) {
- dev_info(&udev->dev, "not running at top speed; "
+ dev_dbg(&udev->dev, "not running at top speed; "
"connect to a high speed hub\n");
/* hub LEDs are probably harder to miss than syslog */
if (hub->has_indicators) {
@@ -4291,7 +4286,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
le16_to_cpu(hub->descriptor->wHubCharacteristics);
struct usb_device *udev;
int status, i;
-
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed(hub, portstatus));
@@ -4640,7 +4634,6 @@ static void hub_events(void)
hub->nerrors = 0;
hub->error = 0;
}
-
/* deal with port status changes */
for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
if (test_bit(i, hub->busy_bits))
@@ -5052,7 +5045,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor)) {
- dev_info(&udev->dev, "device firmware changed\n");
+ dev_dbg(&udev->dev, "device firmware changed\n");
udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index f81b92572735..cc7ea9fe9ce3 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -1002,7 +1002,6 @@ static int __init usb_init(void)
pr_info("%s: USB support disabled\n", usbcore_name);
return 0;
}
-
retval = usb_debugfs_init();
if (retval)
goto out;
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f00c74978b7a..10ee1ca3c616 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -365,12 +365,9 @@ static int dwc3_probe(struct platform_device *pdev)
struct resource *res;
struct dwc3 *dwc;
struct device *dev = &pdev->dev;
-
int ret = -ENOMEM;
-
void __iomem *regs;
void *mem;
-
u8 mode;
mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
@@ -472,7 +469,10 @@ static int dwc3_probe(struct platform_device *pdev)
goto err0;
}
- mode = DWC3_MODE(dwc->hwparams.hwparams0);
+// mode = DWC3_MODE(dwc->hwparams.hwparams0);
+ /* Putting controller in Host mode here */
+
+ mode = DWC3_MODE_HOST; /* Just a hack for time being */
switch (mode) {
case DWC3_MODE_DEVICE:
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index a2d7c62d4e09..87f2c28600b1 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -43,6 +43,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
memset(&pdata, 0x00, sizeof(pdata));
pdev = platform_device_alloc("nop_usb_xceiv", 0);
+
if (!pdev)
return -ENOMEM;
@@ -50,6 +51,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
pdata.type = USB_PHY_TYPE_USB2;
ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
+
if (ret)
goto err1;
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index 36e3214bef35..5f18a32714d1 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -17,6 +17,8 @@
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/platform_data/usb-ehci-s5p.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/samsung_usb_phy.h>
#include <plat/usb-phy.h>
#define EHCI_INSNREG00(base) (base + 0x90)
@@ -32,6 +34,8 @@ struct s5p_ehci_hcd {
struct device *dev;
struct usb_hcd *hcd;
struct clk *clk;
+ struct usb_phy *phy;
+ struct s5p_ehci_platdata *pdata;
};
static const struct hc_driver s5p_ehci_hc_driver = {
@@ -65,6 +69,30 @@ static const struct hc_driver s5p_ehci_hc_driver = {
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
+static void s5p_ehci_phy_enable(struct s5p_ehci_hcd *s5p_ehci)
+{
+ struct platform_device *pdev = to_platform_device(s5p_ehci->dev);
+
+ if (s5p_ehci->phy) {
+ samsung_usbphy_set_type(s5p_ehci->phy, USB_PHY_TYPE_HOST);
+ usb_phy_init(s5p_ehci->phy);
+ } else if (s5p_ehci->pdata->phy_init) {
+ s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
+ }
+}
+
+static void s5p_ehci_phy_disable(struct s5p_ehci_hcd *s5p_ehci)
+{
+ struct platform_device *pdev = to_platform_device(s5p_ehci->dev);
+
+ if (s5p_ehci->phy) {
+ samsung_usbphy_set_type(s5p_ehci->phy, USB_PHY_TYPE_HOST);
+ usb_phy_shutdown(s5p_ehci->phy);
+ } else if (s5p_ehci->pdata->phy_exit) {
+ s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
+ }
+}
+
static void s5p_setup_hub_gpio(struct platform_device *pdev, const char *propname, int level)
{
int err;
@@ -107,20 +135,15 @@ static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32);
static int s5p_ehci_probe(struct platform_device *pdev)
{
- struct s5p_ehci_platdata *pdata;
+ struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
struct s5p_ehci_hcd *s5p_ehci;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct resource *res;
+ struct usb_phy *phy;
int irq;
int err;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "No platform data defined\n");
- return -EINVAL;
- }
-
/*
* Right now device-tree probed devices don't get dma_mask set.
* Since shared usb code relies on it, set it here for now.
@@ -138,6 +161,19 @@ static int s5p_ehci_probe(struct platform_device *pdev)
if (!s5p_ehci)
return -ENOMEM;
+ phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+
+ if (IS_ERR_OR_NULL(phy)) {
+ /* Fallback to pdata */
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data or transceiver defined\n");
+ return -EPROBE_DEFER;
+ } else {
+ s5p_ehci->pdata = pdata;
+ }
+ } else {
+ s5p_ehci->phy = phy;
+ }
s5p_ehci->dev = &pdev->dev;
hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev,
@@ -186,8 +222,7 @@ static int s5p_ehci_probe(struct platform_device *pdev)
s5p_setup_hub_gpio(pdev, "samsung,hub-reset", GPIOF_OUT_INIT_LOW);
s5p_setup_hub_gpio(pdev, "samsung,hub-connect", GPIOF_OUT_INIT_LOW);
- if (pdata->phy_init)
- pdata->phy_init(pdev, S5P_USB_PHY_HOST);
+ s5p_ehci_phy_enable(s5p_ehci);
mdelay(1);
s5p_setup_hub_gpio(pdev, "samsung,hub-reset", GPIOF_OUT_INIT_HIGH);
@@ -202,13 +237,15 @@ static int s5p_ehci_probe(struct platform_device *pdev)
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err) {
dev_err(&pdev->dev, "Failed to add USB HCD\n");
- goto fail_io;
+ goto fail_add_hcd;
}
platform_set_drvdata(pdev, s5p_ehci);
return 0;
+fail_add_hcd:
+ s5p_ehci_phy_disable(s5p_ehci);
fail_io:
clk_disable_unprepare(s5p_ehci->clk);
fail_clk:
@@ -224,8 +261,7 @@ static int s5p_ehci_remove(struct platform_device *pdev)
usb_remove_hcd(hcd);
- if (pdata && pdata->phy_exit)
- pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
+ s5p_ehci_phy_disable(s5p_ehci);
clk_disable_unprepare(s5p_ehci->clk);
@@ -249,16 +285,12 @@ static int s5p_ehci_suspend(struct device *dev)
struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
struct usb_hcd *hcd = s5p_ehci->hcd;
bool do_wakeup = device_may_wakeup(dev);
- struct platform_device *pdev = to_platform_device(dev);
- struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
int rc;
rc = ehci_suspend(hcd, do_wakeup);
- if (pdata && pdata->phy_exit)
- pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
-
- clk_disable_unprepare(s5p_ehci->clk);
+ s5p_ehci_phy_disable(s5p_ehci);
+ clk_disable(s5p_ehci->clk);
return rc;
}
@@ -267,14 +299,10 @@ static int s5p_ehci_resume(struct device *dev)
{
struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
struct usb_hcd *hcd = s5p_ehci->hcd;
- struct platform_device *pdev = to_platform_device(dev);
- struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
clk_prepare_enable(s5p_ehci->clk);
- if (pdata && pdata->phy_init)
- pdata->phy_init(pdev, S5P_USB_PHY_HOST);
-
+ s5p_ehci_phy_enable(s5p_ehci);
/* DMA burst Enable */
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index aa3b8844bb9f..f3dcae4f3d85 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -15,12 +15,16 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/platform_data/usb-exynos.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/samsung_usb_phy.h>
#include <plat/usb-phy.h>
struct exynos_ohci_hcd {
struct device *dev;
struct usb_hcd *hcd;
struct clk *clk;
+ struct usb_phy *phy;
+ struct exynos4_ohci_platdata *pdata;
};
static int ohci_exynos_reset(struct usb_hcd *hcd)
@@ -28,6 +32,30 @@ static int ohci_exynos_reset(struct usb_hcd *hcd)
return ohci_init(hcd_to_ohci(hcd));
}
+static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci)
+{
+ struct platform_device *pdev = to_platform_device(exynos_ohci->dev);
+
+ if (exynos_ohci->phy) {
+ samsung_usbphy_set_type(exynos_ohci->phy, USB_PHY_TYPE_HOST);
+ usb_phy_init(exynos_ohci->phy);
+ } else if (exynos_ohci->pdata->phy_init) {
+ exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
+ }
+}
+
+static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci)
+{
+ struct platform_device *pdev = to_platform_device(exynos_ohci->dev);
+
+ if (exynos_ohci->phy) {
+ samsung_usbphy_set_type(exynos_ohci->phy, USB_PHY_TYPE_HOST);
+ usb_phy_shutdown(exynos_ohci->phy);
+ } else if (exynos_ohci->pdata->phy_exit) {
+ exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
+ }
+}
+
static int ohci_exynos_start(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
@@ -78,11 +106,12 @@ static u64 ohci_exynos_dma_mask = DMA_BIT_MASK(32);
static int exynos_ohci_probe(struct platform_device *pdev)
{
- struct exynos4_ohci_platdata *pdata;
+ struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
struct exynos_ohci_hcd *exynos_ohci;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct resource *res;
+ struct usb_phy *phy;
int irq;
int err;
@@ -106,7 +135,19 @@ static int exynos_ohci_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!exynos_ohci)
return -ENOMEM;
-
+ phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+
+ if (IS_ERR_OR_NULL(phy)) {
+ /* Fallback to pdata */
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data or transceiver defined\n");
+ return -EPROBE_DEFER;
+ } else {
+ exynos_ohci->pdata = pdata;
+ }
+ } else {
+ exynos_ohci->phy = phy;
+ }
exynos_ohci->dev = &pdev->dev;
hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev,
@@ -152,22 +193,21 @@ static int exynos_ohci_probe(struct platform_device *pdev)
goto fail_io;
}
- if (pdata->phy_init)
- pdata->phy_init(pdev, S5P_USB_PHY_HOST);
-
+ exynos_ohci_phy_enable(exynos_ohci);
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err) {
dev_err(&pdev->dev, "Failed to add USB HCD\n");
- goto fail_io;
+ goto fail_add_hcd;
}
platform_set_drvdata(pdev, exynos_ohci);
return 0;
-
+fail_add_hcd:
+ exynos_ohci_phy_disable(exynos_ohci);
fail_io:
clk_disable_unprepare(exynos_ohci->clk);
fail_clk:
@@ -177,16 +217,14 @@ fail_clk:
static int exynos_ohci_remove(struct platform_device *pdev)
{
- struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
struct usb_hcd *hcd = exynos_ohci->hcd;
usb_remove_hcd(hcd);
- if (pdata && pdata->phy_exit)
- pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
-
- clk_disable_unprepare(exynos_ohci->clk);
+ exynos_ohci_phy_disable(exynos_ohci);
+ clk_disable(exynos_ohci->clk);
+ clk_put(exynos_ohci->clk);
usb_put_hcd(hcd);
@@ -208,8 +246,6 @@ static int exynos_ohci_suspend(struct device *dev)
struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
struct usb_hcd *hcd = exynos_ohci->hcd;
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
- struct platform_device *pdev = to_platform_device(dev);
- struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
unsigned long flags;
int rc = 0;
@@ -228,10 +264,8 @@ static int exynos_ohci_suspend(struct device *dev)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- if (pdata && pdata->phy_exit)
- pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
-
- clk_disable_unprepare(exynos_ohci->clk);
+ exynos_ohci_phy_disable(exynos_ohci);
+ clk_disable(exynos_ohci->clk);
fail:
spin_unlock_irqrestore(&ohci->lock, flags);
@@ -243,13 +277,12 @@ static int exynos_ohci_resume(struct device *dev)
{
struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
struct usb_hcd *hcd = exynos_ohci->hcd;
- struct platform_device *pdev = to_platform_device(dev);
- struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
clk_prepare_enable(exynos_ohci->clk);
- if (pdata && pdata->phy_init)
- pdata->phy_init(pdev, S5P_USB_PHY_HOST);
+ exynos_ohci_phy_enable(exynos_ohci);
+ /* Mark hardware accessible again as we are out of D3 state by now */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
ohci_resume(hcd, false);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 849470b18831..8d08e03fb645 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4607,7 +4607,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
struct device *dev = hcd->self.controller;
int retval;
u32 temp;
-
/* Accept arbitrarily long scatter-gather lists */
hcd->self.sg_tablesize = ~0;
/* XHCI controllers don't stop the ep queue on short packets :| */
@@ -4700,7 +4699,6 @@ MODULE_LICENSE("GPL");
static int __init xhci_hcd_init(void)
{
int retval;
-
retval = xhci_register_pci();
if (retval < 0) {
printk(KERN_DEBUG "Problem registering PCI driver.");
diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c
index a30c04115115..29a45d7fb4b7 100644
--- a/drivers/usb/otg/otg.c
+++ b/drivers/usb/otg/otg.c
@@ -28,10 +28,8 @@ static struct usb_phy *__usb_find_phy(struct list_head *list,
list_for_each_entry(phy, list, head) {
if (phy->type != type)
continue;
-
return phy;
}
-
return ERR_PTR(-ENODEV);
}
@@ -65,7 +63,6 @@ struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type)
ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return NULL;
-
phy = usb_get_phy(type);
if (!IS_ERR(phy)) {
*ptr = phy;
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 5de6e7f39f9c..989cbb8a7ac0 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -45,3 +45,10 @@ config USB_RCAR_PHY
To compile this driver as a module, choose M here: the
module will be called rcar-phy.
+
+config SAMSUNG_USBPHY
+ bool "Samsung USB PHY controller Driver"
+ select USB_OTG_UTILS
+ help
+ Enable this to support Samsung USB phy controller for samsung
+ SoCs.
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 1a579a860a03..ec304f642402 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_USB_ISP1301) += isp1301.o
obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o
obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o
obj-$(CONFIG_USB_RCAR_PHY) += rcar-phy.o
+obj-$(CONFIG_SAMSUNG_USBPHY) += samsung-usbphy.o
diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c
new file mode 100644
index 000000000000..c790c8916f09
--- /dev/null
+++ b/drivers/usb/phy/samsung-usbphy.c
@@ -0,0 +1,1004 @@
+/*
+copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Praveen Paneri <p.paneri@samsung.com>
+ *
+ * Samsung USB-PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and
+ * OHCI-EXYNOS controllers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/usb/samsung_usb_phy.h>
+#include <linux/platform_data/samsung-usbphy.h>
+
+/* Register definitions */
+
+#define SAMSUNG_PHYPWR (0x00)
+
+#define PHYPWR_NORMAL_MASK (0x19 << 0)
+#define PHYPWR_OTG_DISABLE (0x1 << 4)
+#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3)
+#define PHYPWR_FORCE_SUSPEND (0x1 << 1)
+/* For Exynos4 */
+#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0)
+#define PHYPWR_SLEEP_PHY0 (0x1 << 5)
+
+#define SAMSUNG_PHYCLK (0x04)
+
+#define PHYCLK_MODE_USB11 (0x1 << 6)
+#define PHYCLK_EXT_OSC (0x1 << 5)
+#define PHYCLK_COMMON_ON_N (0x1 << 4)
+#define PHYCLK_ID_PULL (0x1 << 2)
+#define PHYCLK_CLKSEL_MASK (0x3 << 0)
+#define PHYCLK_CLKSEL_48M (0x0 << 0)
+#define PHYCLK_CLKSEL_12M (0x2 << 0)
+#define PHYCLK_CLKSEL_24M (0x3 << 0)
+
+#define SAMSUNG_RSTCON (0x08)
+
+#define RSTCON_PHYLINK_SWRST (0x1 << 2)
+#define RSTCON_HLINK_SWRST (0x1 << 1)
+#define RSTCON_SWRST (0x1 << 0)
+/* EXYNOS5 */
+#define EXYNOS5_PHY_HOST_CTRL0 (0x00)
+
+#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31)
+
+#define HOST_CTRL0_REFCLKSEL_MASK (0x3)
+#define HOST_CTRL0_REFCLKSEL(_x) ((_x) << 19)
+#define HOST_CTRL0_REFCLKSEL_XTAL \
+ HOST_CTRL0_REFCLKSEL(0x0)
+#define HOST_CTRL0_REFCLKSEL_EXTL \
+ HOST_CTRL0_REFCLKSEL(0x1)
+#define HOST_CTRL0_REFCLKSEL_CLKCORE \
+ HOST_CTRL0_REFCLKSEL(0x2)
+
+#define HOST_CTRL0_FSEL_MASK (0x7 << 16)
+#define HOST_CTRL0_FSEL(_x) ((_x) << 16)
+#define HOST_CTRL0_FSEL_CLKSEL_50M (0x7)
+#define HOST_CTRL0_FSEL_CLKSEL_24M (0x5)
+#define HOST_CTRL0_FSEL_CLKSEL_20M (0x4)
+#define HOST_CTRL0_FSEL_CLKSEL_19200K (0x3)
+#define HOST_CTRL0_FSEL_CLKSEL_12M (0x2)
+#define HOST_CTRL0_FSEL_CLKSEL_10M (0x1)
+#define HOST_CTRL0_FSEL_CLKSEL_9600K (0x0)
+
+#define HOST_CTRL0_TESTBURNIN (0x1 << 11)
+#define HOST_CTRL0_RETENABLE (0x1 << 10)
+#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 (0x04)
+
+#define EXYNOS5_PHY_HSIC_CTRL1 (0x10)
+
+#define EXYNOS5_PHY_HSIC_TUNE1 (0x14)
+
+#define EXYNOS5_PHY_HSIC_CTRL2 (0x20)
+
+#define EXYNOS5_PHY_HSIC_TUNE2 (0x24)
+
+#define HSIC_CTRL_REFCLKSEL_MASK (0x3)
+#define HSIC_CTRL_REFCLKSEL (0x2 << 23)
+
+#define HSIC_CTRL_REFCLKDIV_MASK (0x7f)
+#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16)
+#define HSIC_CTRL_REFCLKDIV_12 \
+ HSIC_CTRL_REFCLKDIV(0x24)
+#define HSIC_CTRL_REFCLKDIV_15 \
+ HSIC_CTRL_REFCLKDIV(0x1C)
+#define HSIC_CTRL_REFCLKDIV_16 \
+ HSIC_CTRL_REFCLKDIV(0x1A)
+#define HSIC_CTRL_REFCLKDIV_19_2 \
+ HSIC_CTRL_REFCLKDIV(0x15)
+#define HSIC_CTRL_REFCLKDIV_20 \
+ HSIC_CTRL_REFCLKDIV(0x14)
+
+#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 (0x30)
+
+#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29)
+#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28)
+#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27)
+#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26)
+
+#define EXYNOS5_PHY_HOST_OHCICTRL (0x34)
+
+#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3)
+#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2)
+#define HOST_OHCICTRL_CNTSEL (0x1 << 1)
+#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0)
+
+#define EXYNOS5_PHY_OTG_SYS (0x38)
+
+#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14)
+#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13)
+#define OTG_SYS_PHY0_SWRST (0x1 << 12)
+
+#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9)
+#define OTG_SYS_REFCLKSEL(_x) ((_x) << 9)
+#define OTG_SYS_REFCLKSEL_XTAL \
+ OTG_SYS_REFCLKSEL(0x0)
+#define OTG_SYS_REFCLKSEL_EXTL \
+ OTG_SYS_REFCLKSEL(0x1)
+#define OTG_SYS_REFCLKSEL_CLKCORE \
+ OTG_SYS_REFCLKSEL(0x2)
+
+#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8)
+#define OTG_SYS_COMMON_ON (0x1 << 7)
+
+#define OTG_SYS_FSEL_MASK (0x7 << 4)
+#define OTG_SYS_FSEL(_x) ((_x) << 4)
+
+#define OTG_SYS_FORCESLEEP (0x1 << 3)
+#define OTG_SYS_OTGDISABLE (0x1 << 2)
+#define OTG_SYS_SIDDQ_UOTG (0x1 << 1)
+#define OTG_SYS_FORCESUSPEND (0x1 << 0)
+
+#define EXYNOS5_PHY_OTG_TUNE (0x40)
+
+/* USB 3.0: DRD */
+#define EXYNOS5_DRD_LINKSYSTEM (0x04)
+
+#define LINKSYSTEM_FLADJ_MASK (0x3f << 1)
+#define LINKSYSTEM_FLADJ(_x) ((_x) << 1)
+#define LINKSYSTEM_XHCI_VERSION_CONTROL (1 << 27)
+
+#define EXYNOS5_DRD_PHYUTMI (0x08)
+
+#define PHYUTMI_OTGDISABLE (1 << 6)
+#define PHYUTMI_FORCESUSPEND (1 << 1)
+#define PHYUTMI_FORCESLEEP (1 << 0)
+
+#define EXYNOS5_DRD_PHYPIPE (0x0C)
+
+#define EXYNOS5_DRD_PHYCLKRST (0x10)
+
+#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23)
+#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23)
+
+#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21)
+#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21)
+
+#define PHYCLKRST_SSC_EN (1 << 20)
+#define PHYCLKRST_REF_SSP_EN (1 << 19)
+#define PHYCLKRST_REF_CLKDIV2 (1 << 18)
+
+#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11)
+#define PHYCLKRST_MPLL_MULTIPLIER(_x) ((_x) << 11)
+#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF \
+ PHYCLKRST_MPLL_MULTIPLIER(0x19)
+#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF \
+ PHYCLKRST_MPLL_MULTIPLIER(0x02)
+#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF \
+ PHYCLKRST_MPLL_MULTIPLIER(0x68)
+#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF \
+ PHYCLKRST_MPLL_MULTIPLIER(0x7d)
+#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF \
+ PHYCLKRST_MPLL_MULTIPLIER(0x02)
+
+#define PHYCLKRST_FSEL_MASK (0x3f << 5)
+#define PHYCLKRST_FSEL(_x) ((_x) << 5)
+#define PHYCLKRST_FSEL_PAD_100MHZ \
+ PHYCLKRST_FSEL(0x27)
+#define PHYCLKRST_FSEL_PAD_24MHZ \
+ PHYCLKRST_FSEL(0x2a)
+#define PHYCLKRST_FSEL_PAD_20MHZ \
++ PHYCLKRST_FSEL(0x31)
+#define PHYCLKRST_FSEL_PAD_19_2MHZ \
+ PHYCLKRST_FSEL(0x38)
+
+#define PHYCLKRST_RETENABLEN (1 << 4)
+
+#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2)
+#define PHYCLKRST_REFCLKSEL(_x) ((_x) << 2)
+#define PHYCLKRST_REFCLKSEL_PAD_REFCLK \
+ PHYCLKRST_REFCLKSEL(2)
+#define PHYCLKRST_REFCLKSEL_EXT_REFCLK \
+ PHYCLKRST_REFCLKSEL(3)
+
+#define PHYCLKRST_PORTRESET (1 << 1)
+#define PHYCLKRST_COMMONONN (1 << 0)
+
+#define EXYNOS5_DRD_PHYREG0 (0x14)
+#define EXYNOS5_DRD_PHYREG1 (0x18)
+
+#define EXYNOS5_DRD_PHYPARAM0 (0x1C)
+
+#define PHYPARAM0_REF_USE_PAD (0x1 << 31)
+#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26)
+#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26)
+
+#define EXYNOS5_DRD_PHYPARAM1 (0x20)
+
+#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0)
+#define PHYPARAM1_PCS_TXDEEMPH (0x1C)
+
+#define EXYNOS5_DRD_PHYTERM (0x24)
+
+#define EXYNOS5_DRD_PHYTEST (0x28)
+
+#define PHYTEST_POWERDOWN_SSP (1 << 3)
+#define PHYTEST_POWERDOWN_HSP (1 << 2)
+
+#define EXYNOS5_DRD_PHYADP (0x2C)
+
+#define EXYNOS5_DRD_PHYBATCHG (0x30)
+
+#define PHYBATCHG_UTMI_CLKSEL (0x1 << 2)
+
+#define EXYNOS5_DRD_PHYRESUME (0x34)
+#define EXYNOS5_DRD_LINKPORT (0x44)
+
+#ifndef MHZ
+#define MHZ (1000*1000)
+#endif
+
+enum samsung_cpu_type {
+ TYPE_S3C64XX,
+ TYPE_EXYNOS4210,
+ TYPE_EXYNOS5250,
+};
+
+/*
+ * struct samsung_usbphy - transceiver driver state
+ * @phy: transceiver structure
+ * @phy3: transceiver structure for USB 3.0
+ * @plat: platform data
+ * @dev: The parent device supplied to the probe function
+ * @clk: usb phy clock
+ * @regs: usb phy register memory base
+ * @regs_phy3: usb 3.0 phy register memory base
+ * @ref_clk_freq: reference clock frequency selection
+ * @cpu_type: machine identifier
+ * @phy_type: It keeps track of the PHY type.
+ * @host_usage: host_phy usage count.
+*/
+struct samsung_usbphy {
+ struct usb_phy phy;
+ struct usb_phy phy3;
+ struct samsung_usbphy_data *plat;
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *regs;
+ void __iomem *regs_phy3;
+ int ref_clk_freq;
+ int cpu_type;
+ enum samsung_usb_phy_type phy_type;
+ atomic_t host_usage;
+};
+
+#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy)
+#define phy3_to_sphy(x) container_of((x), struct samsung_usbphy, phy3)
+/*
++ * PHYs are different for USB Device and USB Host. Controllers can make
++ * sure that the correct PHY type is selected by calling this function
++ * before any PHY operation.
++ */
+int samsung_usbphy_set_type(struct usb_phy *phy,
+ enum samsung_usb_phy_type phy_type)
+{
+ struct samsung_usbphy *sphy = phy_to_sphy(phy);
+
+ if (sphy->phy_type != phy_type)
+ sphy->phy_type = phy_type;
+
+ return 0;
+}
+
+/*
+ * Returns reference clock frequency selection value
+ */
+static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy)
+{
+ struct clk *ref_clk;
+ int refclk_freq = 0;
+
+ if (sphy->cpu_type == TYPE_EXYNOS5250)
+ ref_clk = clk_get(sphy->dev, "ext_xtal");
+ else
+ ref_clk = clk_get(sphy->dev, "xusbxti");
+ if (IS_ERR(ref_clk)) {
+ dev_err(sphy->dev, "Failed to get reference clock\n");
+ return PTR_ERR(ref_clk);
+ }
+ if (sphy->cpu_type == TYPE_EXYNOS5250) {
+ /* set clock frequency for PLL */
+ switch (clk_get_rate(ref_clk)) {
+ case 96 * 100000:
+ refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_9600K;
+ break;
+ case 10 * MHZ:
+ refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_10M;
+ break;
+ case 12 * MHZ:
+ refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_12M;
+ break;
+ case 192 * 100000:
+ refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_19200K;
+ break;
+ case 20 * MHZ:
+ refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_20M;
+ break;
+ case 50 * MHZ:
+ refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_50M;
+ break;
+ case 24 * MHZ:
+ default:
+ /* default reference clock */
+ refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_24M;
+ }
+ } else {
+ switch (clk_get_rate(ref_clk)) {
+ case 12 * MHZ:
+ refclk_freq = PHYCLK_CLKSEL_12M;
+ break;
+ case 24 * MHZ:
+ refclk_freq = PHYCLK_CLKSEL_24M;
+ break;
+ case 48 * MHZ:
+ refclk_freq = PHYCLK_CLKSEL_48M;
+ break;
+ default:
+ if (sphy->cpu_type == TYPE_S3C64XX)
+ refclk_freq = PHYCLK_CLKSEL_48M;
+ else
+ refclk_freq = PHYCLK_CLKSEL_24M;
+ break;
+ }
+ }
+ clk_put(ref_clk);
+
+ return refclk_freq;
+}
+
+ /*
+ * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock form clock core.
+ */
+static u32 exynos5_usbphy3_set_clock(struct samsung_usbphy *sphy)
+{
+ u32 reg;
+ u32 refclk;
+
+ refclk = sphy->ref_clk_freq;
+
+ reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
+ PHYCLKRST_FSEL(refclk);
+
+ switch (refclk) {
+ case HOST_CTRL0_FSEL_CLKSEL_50M:
+ reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
+ PHYCLKRST_SSC_REFCLKSEL(0x00));
+ break;
+ case HOST_CTRL0_FSEL_CLKSEL_20M:
+ reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
+ PHYCLKRST_SSC_REFCLKSEL(0x00));
+ break;
+ case HOST_CTRL0_FSEL_CLKSEL_19200K:
+ reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF |
+ PHYCLKRST_SSC_REFCLKSEL(0x88));
+ break;
+ case HOST_CTRL0_FSEL_CLKSEL_24M:
+ default:
+ reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
+ PHYCLKRST_SSC_REFCLKSEL(0x88));
+ break;
+ }
+
+ return reg;
+}
+
+static int exynos5_phyhost_is_on(void *regs)
+{
+ return (readl(regs + EXYNOS5_PHY_HOST_CTRL0) &
+ HOST_CTRL0_SIDDQ) ? 0 : 1;
+}
+
+static int samsung_exynos5_usbphy3_enable(struct samsung_usbphy *sphy)
+{
+ void __iomem *regs = sphy->regs_phy3;
+ u32 phyparam0;
+ u32 phyparam1;
+ u32 linksystem;
+ u32 phybatchg;
+ u32 phytest;
+ u32 phyclkrst;
+ /* Reset USB 3.0 PHY */
+ writel(0x0, regs + EXYNOS5_DRD_PHYREG0);
+
+ phyparam0 = readl(regs + EXYNOS5_DRD_PHYPARAM0);
+ /* Select PHY CLK source */
+ phyparam0 &= ~PHYPARAM0_REF_USE_PAD;
+ /* Set Loss-of-Signal Detector sensitivity */
+ phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK;
+ phyparam0 |= PHYPARAM0_REF_LOSLEVEL;
+ writel(phyparam0, regs + EXYNOS5_DRD_PHYPARAM0);
+
+ writel(0x0, regs + EXYNOS5_DRD_PHYRESUME);
+
+ /*
+ * Setting the Frame length Adj value[6:1] to default 0x20
+ * See xHCI 1.0 spec, 5.2.4
+ */
+ linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL |
+ LINKSYSTEM_FLADJ(0x20);
+ writel(linksystem, regs + EXYNOS5_DRD_LINKSYSTEM);
+
+ phyparam1 = readl(regs + EXYNOS5_DRD_PHYPARAM1);
+ /* Set Tx De-Emphasis level */
+ phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
+ phyparam1 |= PHYPARAM1_PCS_TXDEEMPH;
+ writel(phyparam1, regs + EXYNOS5_DRD_PHYPARAM1);
+
+ phybatchg = readl(regs + EXYNOS5_DRD_PHYBATCHG);
+ phybatchg |= PHYBATCHG_UTMI_CLKSEL;
+ writel(phybatchg, regs + EXYNOS5_DRD_PHYBATCHG);
+
+ /* PHYTEST POWERDOWN Control */
+ phytest = readl(regs + EXYNOS5_DRD_PHYTEST);
+ phytest &= ~(PHYTEST_POWERDOWN_SSP |
+ PHYTEST_POWERDOWN_HSP);
+ writel(phytest, regs + EXYNOS5_DRD_PHYTEST);
+
+ /* UTMI Power Control */
+ writel(PHYUTMI_OTGDISABLE, regs + EXYNOS5_DRD_PHYUTMI);
+
+ phyclkrst = exynos5_usbphy3_set_clock(sphy);
+
+ phyclkrst |= PHYCLKRST_PORTRESET |
+ /* Digital power supply in normal operating mode */
+ PHYCLKRST_RETENABLEN |
+ /* Enable ref clock for SS function */
+ PHYCLKRST_REF_SSP_EN |
+ /* Enable spread spectrum */
+ PHYCLKRST_SSC_EN |
+ /* Power down HS Bias and PLL blocks in suspend mode */
+ PHYCLKRST_COMMONONN;
+
+ writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
+
+ udelay(10);
+
+ phyclkrst &= ~(PHYCLKRST_PORTRESET);
+ writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
+
+ return 0;
+}
+
+static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy)
+{
+ void __iomem *regs = sphy->regs;
+ u32 phyclk = sphy->ref_clk_freq;
+ u32 phyhost;
+ u32 phyotg;
+ u32 phyhsic;
+ u32 ehcictrl;
+ u32 ohcictrl;
+
+ atomic_inc(&sphy->host_usage);
+
+ if (exynos5_phyhost_is_on(regs)) {
+ dev_info(sphy->dev, "Already power on PHY\n");
+ return;
+ }
+ /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */
+ if (sphy->plat && sphy->plat->phy_cfg_sel)
+ sphy->plat->phy_cfg_sel(sphy->dev, USB_PHY_TYPE_HOST);
+
+ /* Host configuration */
+ phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
+
+ /* phy reference clock configuration */
+ phyhost &= ~HOST_CTRL0_FSEL_MASK;
+ phyhost |= HOST_CTRL0_FSEL(phyclk);
+
+ /* host phy reset */
+ phyhost &= ~(HOST_CTRL0_PHYSWRST |
+ HOST_CTRL0_PHYSWRSTALL |
+ HOST_CTRL0_SIDDQ |
+ /* Enable normal mode of operation */
+ HOST_CTRL0_FORCESUSPEND |
+ HOST_CTRL0_FORCESLEEP);
+
+ /* Link reset */
+ phyhost |= (HOST_CTRL0_LINKSWRST |
+ HOST_CTRL0_UTMISWRST |
+ /* COMMON Block configuration during suspend */
+ HOST_CTRL0_COMMONON_N);
+ writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0);
+ udelay(10);
+ phyhost &= ~(HOST_CTRL0_LINKSWRST |
+ HOST_CTRL0_UTMISWRST);
+ writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0);
+
+ /* OTG configuration */
+ phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS);
+
+ /* phy reference clock configuration */
+ phyotg &= ~OTG_SYS_FSEL_MASK;
+ phyotg |= OTG_SYS_FSEL(phyclk);
+
+ /* Enable normal mode of operation */
+ phyotg &= ~(OTG_SYS_FORCESUSPEND |
+ OTG_SYS_SIDDQ_UOTG |
+ OTG_SYS_FORCESLEEP |
+ OTG_SYS_REFCLKSEL_MASK |
+ /* COMMON Block configuration during suspend */
+ OTG_SYS_COMMON_ON);
+
+ /* OTG phy & link reset */
+ phyotg |= (OTG_SYS_PHY0_SWRST |
+ OTG_SYS_LINKSWRST_UOTG |
+ OTG_SYS_PHYLINK_SWRESET |
+ OTG_SYS_OTGDISABLE |
+ /* Set phy refclk */
+ OTG_SYS_REFCLKSEL_CLKCORE);
+
+ writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS);
+ udelay(10);
+ phyotg &= ~(OTG_SYS_PHY0_SWRST |
+ OTG_SYS_LINKSWRST_UOTG |
+ OTG_SYS_PHYLINK_SWRESET);
+ writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS);
+
+ /* HSIC phy configuration */
+ phyhsic = (HSIC_CTRL_REFCLKDIV_12 |
+ HSIC_CTRL_REFCLKSEL |
+ HSIC_CTRL_PHYSWRST);
+ writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1);
+ writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2);
+ udelay(10);
+ phyhsic &= ~HSIC_CTRL_PHYSWRST;
+ writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1);
+ writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2);
+
+ udelay(80);
+
+ /* enable EHCI DMA burst */
+ ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL);
+ ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN |
+ HOST_EHCICTRL_ENAINCR4 |
+ HOST_EHCICTRL_ENAINCR8 |
+ HOST_EHCICTRL_ENAINCR16);
+ writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL);
+
+ /* set ohci_suspend_on_n */
+ ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL);
+ ohcictrl |= HOST_OHCICTRL_SUSPLGCY;
+ writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL);
+}
+
+static void samsung_exynos5_usbphy3_disable(struct samsung_usbphy *sphy)
+{
+ u32 phyutmi;
+ u32 phyclkrst;
+ u32 phytest;
+ void __iomem *regs = sphy->regs_phy3;
+
+ phyutmi = PHYUTMI_OTGDISABLE |
+ PHYUTMI_FORCESUSPEND |
+ PHYUTMI_FORCESLEEP;
+ writel(phyutmi, regs + EXYNOS5_DRD_PHYUTMI);
+
+ /* Resetting the PHYCLKRST enable bits to reduce leakage current */
+ phyclkrst = readl(regs + EXYNOS5_DRD_PHYCLKRST);
+ phyclkrst &= ~(PHYCLKRST_REF_SSP_EN |
+ PHYCLKRST_SSC_EN |
+ PHYCLKRST_COMMONONN);
+ writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
+
+ /* Control PHYTEST to remove leakage current */
+ phytest = readl(regs + EXYNOS5_DRD_PHYTEST);
+ phytest |= (PHYTEST_POWERDOWN_SSP |
+ PHYTEST_POWERDOWN_HSP);
+ writel(phytest, regs + EXYNOS5_DRD_PHYTEST);
+}
+
+static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy)
+{
+ void __iomem *regs = sphy->regs;
+ u32 phyhost;
+ u32 phyotg;
+ u32 phyhsic;
+
+ if (atomic_dec_return(&sphy->host_usage) > 0) {
+ dev_info(sphy->dev, "still being used\n");
+ return;
+ }
+
+ phyhsic = (HSIC_CTRL_REFCLKDIV_12 |
+ HSIC_CTRL_REFCLKSEL |
+ HSIC_CTRL_SIDDQ |
+ HSIC_CTRL_FORCESLEEP |
+ HSIC_CTRL_FORCESUSPEND);
+ writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1);
+ writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2);
+
+ phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
+ phyhost |= (HOST_CTRL0_SIDDQ |
+ HOST_CTRL0_FORCESUSPEND |
+ HOST_CTRL0_FORCESLEEP |
+ HOST_CTRL0_PHYSWRST |
+ HOST_CTRL0_PHYSWRSTALL);
+ writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0);
+
+ phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS);
+ phyotg |= (OTG_SYS_FORCESUSPEND |
+ OTG_SYS_SIDDQ_UOTG |
+ OTG_SYS_FORCESLEEP);
+ writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS);
+}
+
+static int samsung_usbphy3_init(struct usb_phy *phy3)
+{
+ struct samsung_usbphy *sphy;
+ int ret = 0;
+
+ sphy = phy3_to_sphy(phy3);
+
+ if (sphy->cpu_type != TYPE_EXYNOS5250) {
+ dev_err(sphy->dev, "Not a valid cpu_type for USB 3.0\n");
+ return -ENODEV;
+ }
+ /* setting default phy-type for USB 3.0 */
+ samsung_usbphy_set_type(&sphy->phy3, USB_PHY_TYPE_DRD);
+
+ /* Enable the phy clock */
+ ret = clk_prepare_enable(sphy->clk);
+ if (ret) {
+ dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
+ return ret;
+ }
+
+ /* Disable phy isolation */
+ if (sphy->plat && sphy->plat->pmu_isolation)
+ sphy->plat->pmu_isolation(false, sphy->phy_type);
+
+ /* Initialize usb phy registers */
+ samsung_exynos5_usbphy3_enable(sphy);
+
+ /* Disable the phy clock */
+ clk_disable_unprepare(sphy->clk);
+
+ return ret;
+}
+
+/*
+ * The function passed to the usb driver for phy shutdown
+ */
+static void samsung_usbphy3_shutdown(struct usb_phy *phy3)
+{
+ struct samsung_usbphy *sphy;
+
+ sphy = phy3_to_sphy(phy3);
+
+ if (sphy->cpu_type != TYPE_EXYNOS5250) {
+ dev_err(sphy->dev, "Not a valid cpu_type for USB 3.0\n");
+ return;
+ }
+
+ /* setting default phy-type for USB 3.0 */
+ samsung_usbphy_set_type(&sphy->phy3, USB_PHY_TYPE_DRD);
+
+ if (clk_prepare_enable(sphy->clk)) {
+ dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
+ return;
+ }
+
+ /* De-initialize usb phy registers */
+ samsung_exynos5_usbphy3_disable(sphy);
+
+ /* Enable phy isolation */
+ if (sphy->plat && sphy->plat->pmu_isolation)
+ sphy->plat->pmu_isolation(true, sphy->phy_type);
+
+ clk_disable_unprepare(sphy->clk);
+}
+
+static void samsung_usbphy_enable(struct samsung_usbphy *sphy)
+{
+ void __iomem *regs = sphy->regs;
+ u32 phypwr;
+ u32 phyclk;
+ u32 rstcon;
+
+ /* set clock frequency for PLL */
+ phyclk = sphy->ref_clk_freq;
+ phypwr = readl(regs + SAMSUNG_PHYPWR);
+ rstcon = readl(regs + SAMSUNG_RSTCON);
+
+ switch (sphy->cpu_type) {
+ case TYPE_S3C64XX:
+ phyclk &= ~PHYCLK_COMMON_ON_N;
+ phypwr &= ~PHYPWR_NORMAL_MASK;
+ rstcon |= RSTCON_SWRST;
+ break;
+ case TYPE_EXYNOS4210:
+ phypwr &= ~PHYPWR_NORMAL_MASK_PHY0;
+ rstcon |= RSTCON_SWRST;
+ default:
+ break;
+ }
+
+ writel(phyclk, regs + SAMSUNG_PHYCLK);
+ /* set to normal of PHY0 */
+ writel(phypwr, regs + SAMSUNG_PHYPWR);
+ /* reset all ports of PHY and Link */
+ writel(rstcon, regs + SAMSUNG_RSTCON);
+ udelay(10);
+ rstcon &= ~RSTCON_SWRST;
+ writel(rstcon, regs + SAMSUNG_RSTCON);
+}
+
+static void samsung_usbphy_disable(struct samsung_usbphy *sphy)
+{
+ void __iomem *regs = sphy->regs;
+ u32 phypwr;
+
+ phypwr = readl(regs + SAMSUNG_PHYPWR);
+
+ switch (sphy->cpu_type) {
+ case TYPE_S3C64XX:
+ phypwr |= PHYPWR_NORMAL_MASK;
+ break;
+ case TYPE_EXYNOS4210:
+ phypwr |= PHYPWR_NORMAL_MASK_PHY0;
+ default:
+ break;
+ }
+
+ /* unset to normal of PHY0 */
+ writel(phypwr, regs + SAMSUNG_PHYPWR);
+}
+
+/*
+ * The function passed to the usb driver for phy initialization
+ */
+static int samsung_usbphy_init(struct usb_phy *phy)
+{
+ struct samsung_usbphy *sphy;
+ int ret = 0;
+
+ sphy = phy_to_sphy(phy);
+
+ /* Enable the phy clock */
+ ret = clk_prepare_enable(sphy->clk);
+ if (ret) {
+ dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
+ return ret;
+ }
+
+ /* Disable phy isolation */
+ if (sphy->plat && sphy->plat->pmu_isolation)
+ sphy->plat->pmu_isolation(false, sphy->phy_type);
+
+ /* Initialize usb phy registers */
+ if (sphy->cpu_type == TYPE_EXYNOS5250)
+ samsung_exynos5_usbphy_enable(sphy);
+ else
+ samsung_usbphy_enable(sphy);
+ /* Disable the phy clock */
+ clk_disable_unprepare(sphy->clk);
+ return ret;
+}
+
+/*
++ * The function passed to the usb driver for phy shutdown
++ */
+static void samsung_usbphy_shutdown(struct usb_phy *phy)
+{
+ struct samsung_usbphy *sphy;
+
+ sphy = phy_to_sphy(phy);
+
+ if (clk_prepare_enable(sphy->clk)) {
+ dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
+ return;
+ }
+
+ /* De-initialize usb phy registers */
+ if (sphy->cpu_type == TYPE_EXYNOS5250)
+ samsung_exynos5_usbphy_disable(sphy);
+ else
+ samsung_usbphy_disable(sphy);
+
+ /* Enable phy isolation */
+ if (sphy->plat && sphy->plat->pmu_isolation)
+ sphy->plat->pmu_isolation(true, sphy->phy_type);
+
+ clk_disable_unprepare(sphy->clk);
+}
+
+static const struct of_device_id samsung_usbphy_dt_match[];
+
+static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev)
+{
+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ int data;
+ const struct of_device_id *match;
+ match = of_match_node(samsung_usbphy_dt_match,
+ pdev->dev.of_node);
+ data = (int) match->data;
+ return data;
+ }
+
+ return platform_get_device_id(pdev)->driver_data;
+}
+
+static int samsung_usbphy_probe(struct platform_device *pdev)
+{
+ struct samsung_usbphy *sphy;
+ struct samsung_usbphy_data *pdata;
+ struct device *dev = &pdev->dev;
+ struct resource *phy_mem;
+ void __iomem *phy_base;
+ struct resource *phy3_mem;
+ void __iomem *phy3_base = NULL;
+ struct clk *clk;
+ int ret = 0;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "%s: no platform data defined\n", __func__);
+ return -EINVAL;
+ }
+ phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!phy_mem) {
+ dev_err(dev, "%s: missing mem resource\n", __func__);
+ return -ENODEV;
+ }
+
+ phy_base = devm_request_and_ioremap(dev, phy_mem);
+ if (!phy_base) {
+ dev_err(dev, "%s: register mapping failed\n", __func__);
+ return -ENXIO;
+ }
+
+ sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
+ if (!sphy)
+ return -ENOMEM;
+
+ sphy->dev = &pdev->dev;
+ sphy->plat = pdata;
+ sphy->regs = phy_base;
+ sphy->phy.dev = sphy->dev;
+ sphy->phy.label = "samsung-usbphy";
+ sphy->phy.init = samsung_usbphy_init;
+ sphy->phy.shutdown = samsung_usbphy_shutdown;
+ sphy->cpu_type = samsung_usbphy_get_driver_data(pdev);
+ sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
+ platform_set_drvdata(pdev, sphy);
+
+ if (sphy->cpu_type == TYPE_EXYNOS5250)
+ clk = devm_clk_get(dev, "usbhost");
+ else
+ clk = devm_clk_get(dev, "otg");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "Failed to get otg clock\n");
+ return PTR_ERR(clk);
+ }
+
+ sphy->clk = clk;
+
+ if (sphy->cpu_type == TYPE_EXYNOS5250) {
+ phy3_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!phy3_mem) {
+ dev_err(dev, "%s: missing mem resource\n", __func__);
+ return -ENODEV;
+ }
+ phy3_base = devm_request_and_ioremap(dev, phy3_mem);
+ if (!phy3_base) {
+ dev_err(dev, "%s: register mapping failed\n", __func__);
+ return -ENXIO;
+ }
+ }
+
+ sphy->regs_phy3 = phy3_base;
+ sphy->phy3.dev = sphy->dev;
+ sphy->phy3.label = "samsung-usbphy3";
+ sphy->phy3.init = samsung_usbphy3_init;
+ sphy->phy3.shutdown = samsung_usbphy3_shutdown;
+
+ ret = usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2);
+
+ if (ret)
+ return ret;
+
+ if (sphy->cpu_type != TYPE_EXYNOS5250) {
+ dev_warn(dev, "Not a valid cpu_type for USB 3.0\n");
+ } else {
+ ret = usb_add_phy(&sphy->phy3, USB_PHY_TYPE_USB3);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int __exit samsung_usbphy_remove(struct platform_device *pdev)
+{
+ struct samsung_usbphy *sphy = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&sphy->phy);
+ if (sphy->cpu_type == TYPE_EXYNOS5250)
+ usb_remove_phy(&sphy->phy3);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id samsung_usbphy_dt_match[] = {
+ {
+ .compatible = "samsung,s3c64xx-usbphy",
+ .data = (void *)TYPE_S3C64XX,
+ }, {
+ .compatible = "samsung,exynos4210-usbphy",
+ .data = (void *)TYPE_EXYNOS4210,
+ },{
+ .compatible = "samsung,exynos5250-usbphy",
+ .data = (void *)TYPE_EXYNOS5250,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match);
+#else
+#define samsung_usbphy_dt_match NULL
+#endif
+
+static struct platform_device_id samsung_usbphy_driver_ids[] = {
+ {
+ .name = "s3c64xx-usbphy",
+ .driver_data = TYPE_S3C64XX,
+ }, {
+ .name = "exynos4210-usbphy",
+ .driver_data = TYPE_EXYNOS4210,
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids);
+
+static struct platform_driver samsung_usbphy_driver = {
+ .probe = samsung_usbphy_probe,
+ .remove = samsung_usbphy_remove,
+ .id_table = samsung_usbphy_driver_ids,
+ .driver = {
+ //.name = "samsung-usbphy",
+ .name = "exynos5250-usbphy",
+ .owner = THIS_MODULE,
+ .of_match_table = samsung_usbphy_dt_match,
+ },
+};
+
+module_platform_driver(samsung_usbphy_driver);
+
+MODULE_DESCRIPTION("Samsung USB phy controller");
+MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:samsung-usbphy");
diff --git a/include/linux/platform_data/samsung-usbphy.h b/include/linux/platform_data/samsung-usbphy.h
new file mode 100644
index 000000000000..52decfe36590
--- /dev/null
+++ b/include/linux/platform_data/samsung-usbphy.h
@@ -0,0 +1,29 @@
+/*
+copyright (C) 2012 Samsung Electronics Co.Ltd
+ * http://www.samsung.com/
+ * Author: Praveen Paneri <p.paneri@samsung.com>
+ *
+ * Defines platform data for samsung usb phy driver.
+ *
+ * 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.
+ */
+
+#ifndef __SAMSUNG_USBPHY_PLATFORM_H
+#define __SAMSUNG_USBPHY_PLATFORM_H
+
+/**
+ * samsung_usbphy_data - Platform data for USB PHY driver.
+ * @pmu_isolation: Function to control usb phy isolation in PMU.
+ * @phy_cfg_sel: Function to control phy_cfg - Host/OTG.
+ */
+struct samsung_usbphy_data {
+ void (*pmu_isolation)(int on, int phy_type);
+ void (*phy_cfg_sel)(struct device *dev, int phy_type);
+};
+
+extern void samsung_usbphy_set_pdata(struct samsung_usbphy_data *pd);
+
+#endif /* __SAMSUNG_USBPHY_PLATFORM_H */
diff --git a/include/linux/usb/samsung_usb_phy.h b/include/linux/usb/samsung_usb_phy.h
new file mode 100644
index 000000000000..bd6d900ef47e
--- /dev/null
+++ b/include/linux/usb/samsung_usb_phy.h
@@ -0,0 +1,29 @@
+/*
+ *copyright (C) 2012 Samsung Electronics Co.Ltd
+ * http://www.samsung.com/
+ *
+ * Defines phy types for samsung usb phy controllers - HOST or DEIVCE.
+ *
+ * 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/usb/phy.h>
+enum samsung_usb_phy_type
+{
+ USB_PHY_TYPE_DEVICE,
+ USB_PHY_TYPE_HOST,
+ USB_PHY_TYPE_DRD,
+};
+
+#ifdef CONFIG_SAMSUNG_USBPHY
+extern int samsung_usbphy_set_type(struct usb_phy *phy,
+ enum samsung_usb_phy_type phy_type);
+#else
+static inline int samsung_usbphy_set_type(struct usb_phy *phy,
+ enum samsung_usb_phy_type phy_type)
+{
+ return 0;
+}
+#endif /* CONFIG_SAMSUNG_USBPHY */