CHROMIUM: ehci: exynos: Add experimental HSIC port phy reset.

When tricky USB loads for example:
  * USB extension cable(s) [6ft] +
  * 4port bus powered hub +
  * multiple USB -> ethernet dongles

are disconnected from the Exynos EHCI controller's HSIC port garble
(see USB Spec) is produced which on occasion causes the ports to
become inoperable.  Resetting the HSIC phys in addition to port reset
seems to fix the problem more often than not.

Note, this change does NOT work 100% of the time which means there's
likely some timing related issue that still needs to be enforced.  In
the event that this change does not succeed, the device needs to be
rebooted to successfully recover.

BUG=chrome-os-partner:18932
TEST=manual,

Plug/unplug tricky USB load which causes port error on HSIC and see
downstream devices re-enumerate most of the time.

See following log message (twice) / failure.
s5p-ehci s5p-ehci: s5p_hub_control:65 resetting HSIC port phys DONE

Change-Id: I52c9fe2132588abf456183a20f4f238ac957a696
Signed-off-by: Todd Broch <tbroch@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/61800
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index f788eb8..2054440 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -198,6 +198,12 @@
        help
 	 Enable support for the S5P SOC's on-chip EHCI controller.
 
+config USB_EHCI_S5P_HSIC_RESET
+       boolean "S5P EHCI HSIC reset support"
+       depends on USB_EHCI_S5P
+       help
+	 Enable support for the experimental HSIC reset.
+
 config USB_EHCI_MV
 	bool "EHCI support for Marvell on-chip controller"
 	depends on USB_EHCI_HCD && (ARCH_PXA || ARCH_MMP)
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index 98ab47d..360a070 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -13,9 +13,12 @@
  */
 
 #include <linux/clk.h>
+#include <linux/io.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/of_gpio.h>
+#include <mach/regs-pmu.h>
+#include <mach/regs-usb-phy.h>
 #include <plat/ehci.h>
 #include <plat/usb-phy.h>
 
@@ -35,6 +38,41 @@
 	int vbus_gpio;
 };
 
+static int s5p_hub_control(
+	struct usb_hcd	*hcd,
+	u16		typeReq,
+	u16		wValue,
+	u16		wIndex,
+	char		*buf,
+	u16		wLength
+)
+{
+#ifdef CONFIG_USB_EHCI_S5P_HSIC_RESET
+	u32 hsic_ctrl;
+
+	/* Note, below qualified with wIndex == 2 as this port (HSIC0) is
+	 * the one which root hub sees disconnected and requires additional
+	 * reset of phy to resume operation successfully.
+	 */
+	if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET &&
+	    wIndex == 2) {
+		hsic_ctrl = readl(EXYNOS5_PHY_HSIC_CTRL1);
+		hsic_ctrl |= 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(200);
+		dev_info(hcd->self.controller,
+			 "%s:%d resetting HSIC port phys DONE\n",
+			 __func__, __LINE__);
+	}
+#endif
+	return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+}
+
 static const struct hc_driver s5p_ehci_hc_driver = {
 	.description		= hcd_name,
 	.product_desc		= "S5P EHCI Host Controller",
@@ -56,7 +94,7 @@
 	.endpoint_reset		= ehci_endpoint_reset,
 
 	.hub_status_data	= ehci_hub_status_data,
-	.hub_control		= ehci_hub_control,
+	.hub_control		= s5p_hub_control,
 	.bus_suspend		= ehci_bus_suspend,
 	.bus_resume		= ehci_bus_resume,