diff options
author | Yuvaraj CD <yuvaraj.cd@gmail.com> | 2012-12-26 10:48:11 +0530 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2013-06-18 01:22:08 +0400 |
commit | 3995398445569450f2b3d13cd06f85e2d2ada90a (patch) | |
tree | 040f54a707ee1411f590744e7175db4a077d6af8 | |
parent | f04cab1f04c84d4504a0b0444271ebeb0c27ff83 (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.dtsi | 6 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/map.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/regs-pmu.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-exynos5-dt.c | 14 | ||||
-rw-r--r-- | arch/arm/mach-exynos/setup-usb-phy.c | 214 | ||||
-rw-r--r-- | arch/arm/plat-samsung/include/plat/usb-phy.h | 8 | ||||
-rw-r--r-- | drivers/usb/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 35 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 8 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-exynos.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-s5p.c | 74 | ||||
-rw-r--r-- | drivers/usb/host/ohci-exynos.c | 77 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 2 | ||||
-rw-r--r-- | drivers/usb/otg/otg.c | 3 | ||||
-rw-r--r-- | drivers/usb/phy/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/phy/samsung-usbphy.c | 1004 | ||||
-rw-r--r-- | include/linux/platform_data/samsung-usbphy.h | 29 | ||||
-rw-r--r-- | include/linux/usb/samsung_usb_phy.h | 29 |
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 */ |