Subject: ALCHEMY: AU1200 USB Host Controller (OHCI/EHCI)

From: "Jordan Crouse" <[email-masked]>
Date: Thu, 8 Dec 2005 14:00:42 -0700

Ok, here we go.  I give you the OHCI/EHCI host controller support for
the Alchemy AU1200 processor.  I'm sending this up, partly because I have
it ready to go, but also because it seems that enough folks are getting their
hands on AU1200 parts to make this a hot topic.  

Special thanks to Pete Popov and his merry band of kernel hackers for 
paving the way by pushing to seperate EHCI and PCI in the USB subsystem.    
Note that the AU1200 does support UDC/OTG as well, but thats another patch 
for another day. :)

Jordan
ALCHEMY:  Add Au1200 support for OHCI and EHCI

Add the Au1200 platform to the alchemy OHCI and EHCI drivers.

Signed-off-by: Jordan Crouse <[email-masked]>
---

 arch/mips/au1000/common/cputable.c                 |    2 
 arch/mips/au1000/common/platform.c                 |    4 
 drivers/usb/Kconfig                                |    8 -
 drivers/usb/Makefile                               |    7 
 drivers/usb/core/Kconfig                           |    5 
 drivers/usb/core/hcd.c                             |   13 +
 drivers/usb/core/hcd.h                             |    4 
 drivers/usb/core/hub.c                             |   76 ++++-
 drivers/usb/core/usb.c                             |   63 ++++
 drivers/usb/host/Kconfig                           |    2 
 drivers/usb/host/ehci-au1xxx.c                     |  304 ++++++++++++++++++++
 drivers/usb/host/ehci-hcd.c                        |   81 +++++
 drivers/usb/host/ehci-hub.c                        |   74 +++++
 drivers/usb/host/ehci.h                            |    8 +
 drivers/usb/host/ohci-au1xxx.c                     |  109 ++++++-
 drivers/usb/host/ohci-hcd.c                        |   73 +++++
 drivers/usb/host/ohci-hub.c                        |   56 ++++
 drivers/usb/host/ohci-pci.c                        |    4 
 drivers/usb/host/ohci.h                            |    1 
 include/asm-mips/mach-mips/cpu-feature-overrides.h |    4 
 include/linux/usb.h                                |    7 
 21 files changed, 861 insertions(+), 44 deletions(-)

diff --git a/arch/mips/au1000/common/cputable.c b/arch/mips/au1000/common/cputable.c
index 4dbde82..d8df5fd 100644
--- a/arch/mips/au1000/common/cputable.c
+++ b/arch/mips/au1000/common/cputable.c
@@ -38,7 +38,7 @@ struct cpu_spec	cpu_specs[] = {
     { 0xffffffff, 0x02030204, "Au1100 BE", 0, 1 },
     { 0xffffffff, 0x03030200, "Au1550 AA", 0, 1 },
     { 0xffffffff, 0x04030200, "Au1200 AB", 0, 0 },
-    { 0xffffffff, 0x04030201, "Au1200 AC", 0, 1 },
+    { 0xffffffff, 0x04030201, "Au1200 AC", 1, 0 },
     { 0x00000000, 0x00000000, "Unknown Au1xxx", 1, 0 },
 };
 
diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c
index 48d3f54..dbb4ee7 100644
--- a/arch/mips/au1000/common/platform.c
+++ b/arch/mips/au1000/common/platform.c
@@ -20,7 +20,7 @@
 static struct resource au1xxx_usb_ohci_resources[] = {
 	[0] = {
 		.start		= USB_OHCI_BASE,
-		.end		= USB_OHCI_BASE + USB_OHCI_LEN,
+		.end		= USB_OHCI_BASE + USB_OHCI_LEN - 1,
 		.flags		= IORESOURCE_MEM,
 	},
 	[1] = {
@@ -278,9 +278,7 @@ static struct platform_device *au1xxx_pl
 	&au1100_lcd_device,
 #endif
 #ifdef CONFIG_SOC_AU1200
-#if 0	/* fixme */
 	&au1xxx_usb_ehci_device,
-#endif
 	&au1xxx_usb_gdt_device,
 	&au1xxx_usb_otg_device,
 	&au1200_lcd_device,
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 85dacc9..bff1258 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -9,10 +9,16 @@ menu "USB support"
 # NOTE:  SL-811 option should be board-specific ...
 config USB_ARCH_HAS_HCD
 	boolean
-	default y if USB_ARCH_HAS_OHCI
+	default y if USB_ARCH_HAS_OHCI || USB_ARCH_HAS_EHCI
 	default y if ARM				# SL-811
 	default PCI
 
+# some non-PCI hcds implement EHCI
+config USB_ARCH_HAS_EHCI
+	boolean
+	default y if SOC_AU1200
+	default PCI
+
 # many non-PCI SOC chips embed OHCI
 config USB_ARCH_HAS_OHCI
 	boolean
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index a50c2bc..f9642e8 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -76,3 +76,10 @@ obj-$(CONFIG_USB_SISUSBVGA)	+= misc/
 
 obj-$(CONFIG_USB_ATM)		+= atm/
 obj-$(CONFIG_USB_SPEEDTOUCH)	+= atm/
+
+
+ifneq ($(CONFIG_USB_GADGET_AMD5536UDC),y)
+ifeq ($(CONFIG_USB_PORT_AMD5536OTG),y)
+obj-$(CONFIG_USB)		+= gadget/
+endif
+endif
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index ff03184..7293161 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -82,6 +82,11 @@ config USB_OTG
 	select USB_SUSPEND
 	default n
 
+config USB_OTG_HIGHSPEED
+	bool
+	depends on USB_OTG
+	default n
+
 
 config USB_OTG_WHITELIST
 	bool "Rely on OTG Targeted Peripherals List"
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index da24c31..031147c 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -714,6 +714,7 @@ static void usb_bus_init (struct usb_bus
 	bus->root_hub = NULL;
 	bus->hcpriv = NULL;
 	bus->busnum = -1;
+	bus->otg_port = 0;
 	bus->bandwidth_allocated = 0;
 	bus->bandwidth_int_reqs  = 0;
 	bus->bandwidth_isoc_reqs = 0;
@@ -1830,6 +1831,12 @@ int usb_add_hcd(struct usb_hcd *hcd,
 	rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
 			USB_SPEED_FULL;
 
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG)
+	if ((retval = hcd->driver->start_otg (hcd)) < 0) {
+		dev_warn (hcd->self.controller, "OTG init error %d\n", retval);
+		goto err_hcd_driver_start;
+	}
+#endif	
 	/* Although in principle hcd->driver->start() might need to use rhdev,
 	 * none of the current drivers do.
 	 */
@@ -1855,6 +1862,9 @@ int usb_add_hcd(struct usb_hcd *hcd,
 
  err_register_root_hub:
 	hcd->driver->stop(hcd);
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG)
+	hcd->driver->stop_otg (hcd);
+#endif	
 
  err_hcd_driver_start:
 	usb_put_dev(rhdev);
@@ -1897,6 +1907,9 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 	del_timer_sync(&hcd->rh_timer);
 
 	hcd->driver->stop(hcd);
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG)
+	hcd->driver->stop_otg(hcd);
+#endif
 	hcd->state = HC_STATE_HALT;
 
 	if (hcd->irq >= 0)
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index c8a1b35..48c6dd6 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -216,6 +216,10 @@ struct hc_driver {
 	int		(*bus_suspend)(struct usb_hcd *);
 	int		(*bus_resume)(struct usb_hcd *);
 	int		(*start_port_reset)(struct usb_hcd *, unsigned port_num);
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG)
+	int		(*start_otg)(struct usb_hcd *);
+	void		(*stop_otg)(struct usb_hcd *);
+#endif	
 	void		(*hub_irq_enable)(struct usb_hcd *);
 		/* Needed only if port-change IRQs are level-triggered */
 };
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index f78bd12..629124e 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1228,6 +1228,10 @@ int usb_new_device(struct usb_device *ud
 {
 	int err;
 	int c;
+#ifdef CONFIG_USB_OTG_WHITELIST_RELAXED
+	unsigned port1 = 0;
+	struct usb_device *root = udev->bus->root_hub;
+#endif	
 
 	err = usb_get_configuration(udev);
 	if (err < 0) {
@@ -1253,14 +1257,29 @@ int usb_new_device(struct usb_device *ud
 	show_string(udev, "SerialNumber", udev->serial);
 
 #ifdef	CONFIG_USB_OTG
+
+#ifdef CONFIG_USB_OTG_WHITELIST_RELAXED
+		
+	if (udev->parent) {
+
+	struct usb_device *tdev = udev;
+
+	while (tdev->parent != root)
+		tdev = tdev->parent;
+	for (port1 = 1; port1 <= root->maxchild; port1++) {
+		if (root->children[port1-1] == tdev)
+			break;
+	}
+	}
+	root = udev->parent;
+#endif
 	/*
 	 * OTG-aware devices on OTG-capable root hubs may be able to use SRP,
 	 * to wake us after we've powered off VBUS; and HNP, switching roles
 	 * "host" to "peripheral".  The OTG descriptor helps figure this out.
 	 */
 	if (!udev->bus->is_b_host
-			&& udev->config
-			&& udev->parent == udev->bus->root_hub) {
+			&& udev->config) {
 		struct usb_otg_descriptor	*desc = 0;
 		struct usb_bus			*bus = udev->bus;
 
@@ -1269,6 +1288,7 @@ int usb_new_device(struct usb_device *ud
 					le16_to_cpu(udev->config[0].desc.wTotalLength),
 					USB_DT_OTG, (void **) &desc) == 0) {
 			if (desc->bmAttributes & USB_OTG_HNP) {
+#ifndef CONFIG_USB_OTG_WHITELIST_RELAXED
 				unsigned		port1;
 				struct usb_device	*root = udev->parent;
 				
@@ -1277,15 +1297,23 @@ int usb_new_device(struct usb_device *ud
 					if (root->children[port1-1] == udev)
 						break;
 				}
-
-				dev_info(&udev->dev,
-					"Dual-Role OTG device on %sHNP port\n",
-					(port1 == bus->otg_port)
-						? "" : "non-");
-
-				/* enable HNP before suspend, it's simpler */
-				if (port1 == bus->otg_port)
+#endif
+				if (udev->parent != bus->root_hub) {
+					dev_info(&udev->dev,
+		  				"Dual-Role OTG device connected through hub(s)\n");
+					bus->b_hnp_enable = 0;
+				}
+				else if (port1 == bus->otg_port) {
+					dev_info(&udev->dev,
+							"Dual-Role OTG device on HNP port\n");
 					bus->b_hnp_enable = 1;
+				}
+				else {
+					dev_info(&udev->dev,
+							"Dual-Role OTG device on non-HNP port\n");
+					bus->b_hnp_enable = 0;
+				}	
+				/* enable HNP before suspend, it's simpler */
 				err = usb_control_msg(udev,
 					usb_sndctrlpipe(udev, 0),
 					USB_REQ_SET_FEATURE, 0,
@@ -1298,16 +1326,22 @@ int usb_new_device(struct usb_device *ud
 					 * customize to match your product.
 					 */
 					dev_info(&udev->dev,
-						"can't set HNP mode; %d\n",
+							"Device Not Responding (trying to set HNP mode: %d)\n",
 						err);
 					bus->b_hnp_enable = 0;
+					goto fail;
 				}
 			}
 		}
 	}
 
-	if (!is_targeted(udev)) {
-
+#ifdef CONFIG_USB_OTG_WHITELIST_RELAXED
+	if (port1 && (port1 == udev->bus->otg_port) \
+		&& !is_targeted(udev))
+#else
+	if (!is_targeted(udev))
+#endif
+	{
 		/* Maybe it can talk to us, though we can't talk to it.
 		 * (Includes HNP test device.)
 		 */
@@ -1315,10 +1349,12 @@ int usb_new_device(struct usb_device *ud
 			static int __usb_suspend_device(struct usb_device *,
 						int port1);
 			err = __usb_suspend_device(udev, udev->bus->otg_port);
-			if (err < 0)
+			if (err < 0) {
 				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+				goto fail;
+			}
 		}
-		err = -ENODEV;
+		err = -ENOTCONN;
 		goto fail;
 	}
 #endif
@@ -2565,6 +2601,16 @@ static void hub_port_connect_change(stru
 		}
 
 		up (&udev->serialize);
+
+#ifdef CONFIG_USB_OTG
+
+		/* don't want to disturb HNP: no retry */
+		if ((udev->bus->is_b_host || udev->bus->b_hnp_enable)
+		    && (status == -ENOTCONN))
+			/* FIXME: rather keep the port enabled until */
+			/*        disconnect, but it works this way  */
+			goto loop;
+#endif
 		if (status)
 			goto loop_disable;
 
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index e197ce9..13113b3 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -81,6 +81,11 @@ static struct device_driver usb_generic_
 
 static int usb_generic_driver_data;
 
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG) 
+
+struct otg_transceiver * (*usb_otg_get_transceiver)(void) = NULL;
+#endif
+
 /* called from driver core with usb_bus_type.subsys writelock */
 static int usb_probe_interface(struct device *dev)
 {
@@ -1485,6 +1490,58 @@ static int usb_generic_resume(struct dev
 	return 0;
 }
 
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG) 
+/*
+ * To be called from OTG controller driver
+ */
+int usb_host_register_otg(struct otg_transceiver * (*get_transceiver)(void))
+{
+	struct list_head  *tmp;
+	struct usb_bus    *bus;
+	struct usb_hcd    *hcd;
+
+	if (get_transceiver) {
+		usb_otg_get_transceiver = get_transceiver;
+		down (&usb_bus_list_lock);
+		tmp = usb_bus_list.next;
+		while (tmp != &usb_bus_list) {
+			bus = list_entry(tmp, struct usb_bus, bus_list);
+			tmp = tmp->next;
+			hcd = container_of (bus, struct usb_hcd, self);
+			hcd->driver->start_otg (hcd);
+		}
+		up (&usb_bus_list_lock);
+		pr_info("USB OTG driver registered\n");
+		return 0;
+	}
+	else {
+		printk(KERN_ERR "can't register OTG, no device\n");
+		return -ENODEV;
+	}
+}
+
+void usb_host_deregister_otg(void)
+{
+	struct list_head  *tmp;
+	struct usb_bus    *bus;
+	struct usb_hcd    *hcd;
+
+	down (&usb_bus_list_lock);
+	tmp = usb_bus_list.next;
+	while (tmp != &usb_bus_list) {
+		bus = list_entry(tmp, struct usb_bus, bus_list);
+		tmp = tmp->next;
+		if (bus->otg_port) {
+			hcd = container_of (bus, struct usb_hcd, self);
+			hcd->driver->stop_otg (hcd);
+		}
+	}
+	up (&usb_bus_list_lock);
+	usb_otg_get_transceiver = NULL;
+	pr_info("USB OTG driver deregistered\n");
+}
+#endif
+
 struct bus_type usb_bus_type = {
 	.name =		"usb",
 	.match =	usb_device_match,
@@ -1642,4 +1699,10 @@ EXPORT_SYMBOL (usb_buffer_dmasync_sg);
 #endif
 EXPORT_SYMBOL (usb_buffer_unmap_sg);
 
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG) 
+EXPORT_SYMBOL(usb_host_register_otg);
+EXPORT_SYMBOL(usb_host_deregister_otg);
+EXPORT_SYMBOL(usb_otg_get_transceiver);
+#endif
+
 MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index ed1899d..4a4db1a 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -6,7 +6,7 @@ comment "USB Host Controller Drivers"
 
 config USB_EHCI_HCD
 	tristate "EHCI HCD (USB 2.0) support"
-	depends on USB && PCI
+	depends on USB && USB_ARCH_HAS_EHCI
 	---help---
 	  The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
 	  "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
new file mode 100644
index 0000000..32cf6e6
--- /dev/null
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -0,0 +1,304 @@
+/*
+ * EHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 2000-2004 David Brownell <[email-masked]>
+ *
+ * Bus Glue for AMD Alchemy Au1xxx
+ *
+ * Based on "ohci-au1xxx.c" by Matt Porter <[email-masked]>
+ *
+ * Modified for AMD Alchemy Au1200 EHC
+ *  by K.Boge <[email-masked]>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach-au1x00/au1000.h>
+
+#ifndef CONFIG_SOC_AU1200
+#error "Alchemy chip doesn't have EHC"
+#else   /* Au1200 */
+
+#define USB_HOST_CONFIG   (USB_MSR_BASE + USB_MSR_MCFG)
+#define USB_MCFG_PFEN     (1<<31)
+#define USB_MCFG_RDCOMB   (1<<30)
+#define USB_MCFG_SSDEN    (1<<23)
+#define USB_MCFG_PHYPLLEN (1<<19)
+#define USB_MCFG_EHCCLKEN (1<<17)
+#define USB_MCFG_UCAM     (1<<7)
+#define USB_MCFG_EBMEN    (1<<3)
+#define USB_MCFG_EMEMEN   (1<<2)
+
+#define USBH_ENABLE_CE    (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN)
+#ifdef CONFIG_DMA_COHERENT
+#define USBH_ENABLE_INIT  (USBH_ENABLE_CE \
+                         | USB_MCFG_PFEN | USB_MCFG_RDCOMB \
+                         | USB_MCFG_SSDEN | USB_MCFG_UCAM \
+                         | USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
+#else
+#define USBH_ENABLE_INIT  (USBH_ENABLE_CE \
+                         | USB_MCFG_PFEN | USB_MCFG_RDCOMB \
+                         | USB_MCFG_SSDEN \
+                         | USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
+#endif
+#define USBH_DISABLE      (USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
+
+#endif  /* Au1200 */
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void au1xxx_start_ehc(struct platform_device *dev)
+{
+	pr_debug(__FILE__ ": starting Au1xxx EHCI USB Controller\n");
+
+	/* write HW defaults again in case Yamon cleared them */
+	if (au_readl(USB_HOST_CONFIG) == 0) {
+		au_writel(0x00d02000, USB_HOST_CONFIG);
+		au_readl(USB_HOST_CONFIG);
+		udelay(1000);
+	}
+	/* enable host controller */
+	au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+	au_readl(USB_HOST_CONFIG);
+	udelay(1000);
+	au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+	au_readl(USB_HOST_CONFIG);
+	udelay(1000);
+
+	pr_debug(__FILE__ ": Clock to USB host has been enabled\n");
+}
+
+static void au1xxx_stop_ehc(struct platform_device *dev)
+{
+	pr_debug(__FILE__ ": stopping Au1xxx EHCI USB Controller\n");
+
+	/* Disable mem */
+	au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+	udelay(1000);
+	/* Disable clock */
+	au_writel(~USB_MCFG_EHCCLKEN & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+	au_readl(USB_HOST_CONFIG);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_ehci_au1xxx_probe - initialize Au1xxx-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_ehci_au1xxx_probe (const struct hc_driver *driver,
+			   struct usb_hcd **hcd_out,
+			   struct platform_device *dev)
+{
+	int retval;
+	struct usb_hcd *hcd;
+
+#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
+
+	/* Au1200 AB USB does not support coherent memory */
+	if (!(read_c0_prid() & 0xff)) {
+	pr_info ("Au1200 ohci: !!! This is chip revision AB                     !!!\n");
+	pr_info ("             !!! update your board or re-configure the kernel !!!\n");
+	return -ENODEV;
+	}
+#endif
+
+	if (dev->resource[1].flags != IORESOURCE_IRQ) {
+	pr_debug ("resource[1] is not IORESOURCE_IRQ");
+	retval = -ENOMEM;
+	}
+
+	hcd = usb_create_hcd(driver, &dev->dev, "Au1xxx");
+	if (!hcd)
+		return -ENOMEM;
+	hcd->rsrc_start = dev->resource[0].start;
+	hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
+
+	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+		pr_debug("request_mem_region failed");
+		retval = -EBUSY;
+		goto err1;
+	}
+
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		pr_debug("ioremap failed");
+		retval = -ENOMEM;
+		goto err2;
+	}
+
+	au1xxx_start_ehc(dev);
+	
+	if ((retval = driver->reset (hcd)) < 0) {
+		pr_debug ("can't reset hc\n");
+		goto err3;
+	}
+	
+	/* ehci_hcd_init(hcd_to_ehci(hcd)); */
+
+	retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT | SA_SHIRQ);
+	if (retval == 0)
+		return retval;
+
+ err3:
+	au1xxx_stop_ehc(dev);
+	iounmap(hcd->regs);
+ err2:
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err1:
+	usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_ehci_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_ehci_hcd_au1xxx_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_ehci_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+	usb_remove_hcd(hcd);
+	au1xxx_stop_ehc(dev);
+	iounmap(hcd->regs);
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ehci_au1xxx_hc_driver = {
+	.description =		hcd_name,
+	.product_desc =		"Au1xxx EHCI",
+	.hcd_priv_size =	sizeof(struct ehci_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq =			ehci_irq,
+	.flags =		HCD_MEMORY | HCD_USB2,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.reset =		ehci_init,
+	.start =		ehci_run,
+#ifdef	CONFIG_PM
+	/* suspend:		ehci_au1xxx_suspend,  -- tbd */
+	/* resume:		ehci_au1xxx_resume,   -- tbd */
+#endif /*CONFIG_PM*/
+	.stop =			ehci_stop,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue =		ehci_urb_enqueue,
+	.urb_dequeue =		ehci_urb_dequeue,
+	.endpoint_disable =	ehci_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number =	ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data =	ehci_hub_status_data,
+	.hub_control =		ehci_hub_control,
+#ifdef	CONFIG_USB_SUSPEND
+	.hub_suspend =		ehci_hub_suspend,
+	.hub_resume =		ehci_hub_resume,
+#endif	
+	.start_port_reset =	ehci_start_port_reset,
+#ifdef	CONFIG_USB_OTG_HIGHSPEED
+	.start_otg =		ehci_start_otg,
+	.stop_otg =		ehci_stop_otg,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_hcd_au1xxx_drv_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct usb_hcd *hcd = NULL;
+	int ret;
+
+	pr_debug ("In ehci_hcd_au1xxx_drv_probe\n");
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	ret = usb_ehci_au1xxx_probe(&ehci_au1xxx_hc_driver, &hcd, pdev);
+	return ret;
+}
+
+static int ehci_hcd_au1xxx_drv_remove(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+	usb_ehci_au1xxx_remove(hcd, pdev);
+	return 0;
+}
+	/*TBD*/
+/*static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+	return 0;
+}
+static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+	return 0;
+}
+*/
+
+static struct device_driver ehci_hcd_au1xxx_driver = {
+	.name		= "au1xxx-ehci",
+	.bus		= &platform_bus_type,
+	.probe		= ehci_hcd_au1xxx_drv_probe,
+	.remove		= ehci_hcd_au1xxx_drv_remove,
+	/*.suspend	= ehci_hcd_au1xxx_drv_suspend, */
+	/*.resume	= ehci_hcd_au1xxx_drv_resume, */
+};
+
+static int __init ehci_hcd_au1xxx_init (void)
+{
+	pr_debug (DRIVER_INFO " (Au1xxx)\n");
+
+	return driver_register(&ehci_hcd_au1xxx_driver);
+}
+
+static void __exit ehci_hcd_au1xxx_cleanup (void)
+{
+	driver_unregister(&ehci_hcd_au1xxx_driver);
+}
+
+module_init (ehci_hcd_au1xxx_init);
+module_exit (ehci_hcd_au1xxx_cleanup);
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 29f52a4..2b431ef 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -40,6 +40,7 @@
 #include <linux/interrupt.h>
 #include <linux/reboot.h>
 #include <linux/usb.h>
+#include <linux/usb_otg.h>
 #include <linux/moduleparam.h>
 #include <linux/dma-mapping.h>
 
@@ -124,6 +125,11 @@ static const char	hcd_name [] = "ehci_hc
 #define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
 #define EHCI_SHRINK_JIFFIES	(HZ/200)	/* async qh unlink delay */
 
+#if (EHCI_SHRINK_JIFFIES < 1)
+#undef EHCI_SHRINK_JIFFIES
+#define EHCI_SHRINK_JIFFIES	1
+#endif
+
 /* Initial IRQ latency:  faster than hw default */
 static int log2_irq_thresh = 0;		// 0 to 6
 module_param (log2_irq_thresh, int, S_IRUGO);
@@ -418,7 +424,7 @@ static int ehci_init(struct usb_hcd *hcd
 	u32			temp;
 	int			retval;
 	u32			hcc_params;
-
+	
 	spin_lock_init(&ehci->lock);
 
 	init_timer(&ehci->watchdog);
@@ -573,6 +579,66 @@ static int ehci_run (struct usb_hcd *hcd
 
 /*-------------------------------------------------------------------------*/
 
+#if defined(CONFIG_USB_OTG_HIGHSPEED) && defined(CONFIG_USB_PORT_AMD5536OTG)
+
+static int ehci_start_otg (struct usb_hcd *hcd)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+
+	if (!hcd->self.otg_port) {
+		hcd->self.otg_port = USB_OTG_PORT;
+		ehci->power_budget = OTG_PWR_BUDGET;
+	}
+	if (usb_otg_get_transceiver) {
+		ehci->transceiver = usb_otg_get_transceiver();
+		if (ehci->transceiver) {
+			int	status;
+
+			if (ehci->transceiver->host) {
+				ehci_err (ehci, "OTG already registered\n");
+				return -EBUSY;
+			}
+			ehci->transceiver->host = &hcd->self;
+			hcd->self.hand_over = 0;
+			status = ehci->transceiver->set_host(
+					ehci->transceiver, &hcd->self);
+
+			ehci_dbg (ehci, "init %s transceiver, status %d\n",
+				  ehci->transceiver->label, status);
+
+			/* if (status)
+			put_device(ehci->transceiver->dev); */
+			return status;
+		}
+		else  {
+			ehci_err (ehci, "can't find transceiver\n");
+			return -ENODEV;
+		}
+	}
+	else {
+		ehci_info (ehci, "OTG driver not loaded\n");
+		return 0;
+	}
+}
+
+static void ehci_stop_otg (struct usb_hcd *hcd)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+
+	ehci_dbg (ehci, "clean up %s transceiver\n",
+		  ehci->transceiver->label);
+	if (ehci->transceiver) {
+		ehci->transceiver->host = NULL;
+		ehci->transceiver->set_host(ehci->transceiver, NULL);
+	}
+	ehci->transceiver = NULL;
+	ehci->power_budget = 0;
+	hcd->self.otg_port = 0;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
@@ -884,20 +950,23 @@ static int ehci_get_frame (struct usb_hc
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 	return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
+#if defined(CONFIG_USB_OTG_HIGHSPEED) && defined(CONFIG_USB_PORT_AMD5536OTG)
+	.start_otg =		ehci_start_otg,
+	.stop_otg =		ehci_stop_otg,
+#endif
 }
 
 /*-------------------------------------------------------------------------*/
 
 #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
-
 MODULE_DESCRIPTION (DRIVER_INFO);
 MODULE_AUTHOR (DRIVER_AUTHOR);
 MODULE_LICENSE ("GPL");
 
-#ifdef CONFIG_PCI
+#if defined(CONFIG_SOC_AU1X00)
+#include "ehci-au1xxx.c"
+#elif defined(CONFIG_PCI)
 #include "ehci-pci.c"
-#endif
-
-#if !defined(CONFIG_PCI)
+#else
 #error "missing bus glue for ehci-hcd"
 #endif
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 82caf33..26e070d 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -201,6 +201,17 @@ static int check_reset_complete (
 		port_status |= PORT_OWNER;
 		port_status &= ~PORT_RWC_BITS;
 		writel (port_status, &ehci->regs->port_status [index]);
+		
+#if defined(CONFIG_USB_OTG_HIGHSPEED) && defined(CONFIG_USB_PORT_AMD5536OTG)
+
+		if (ehci->transceiver &&
+		    ((index + 1) == ehci_to_hcd(ehci)->self.otg_port) &&
+		    (ehci->transceiver->companion)) {
+			ehci->transceiver->companion->hand_over = 1;
+			usb_bus_start_enum (ehci->transceiver->companion,
+				ehci->transceiver->companion->otg_port);
+		}
+#endif
 
 	} else
 		ehci_dbg (ehci, "port %d high speed\n", index + 1);
@@ -304,6 +315,59 @@ ehci_hub_descriptor (
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_USB_OTG_HIGHSPEED
+
+static int ehci_start_port_reset (struct usb_hcd *hcd, unsigned port)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+	u32			status;
+
+	if (!port)
+		return -EINVAL;
+	port--;
+
+	/* start port reset before HNP protocol times out */
+	status = readl (&ehci->regs->port_status [port]);
+	if (status & PORT_RESUME)
+		return -EINVAL;
+
+	if (!(status & PORT_CONNECT))
+		return -ENODEV;
+
+	status |= PORT_RESET;
+	status &= ~(PORT_PE | PORT_CSC);   /* PORT_CSC for khubd notification */
+	ehci->reset_done [port] = jiffies + msecs_to_jiffies (50);
+	writel (status, &ehci->regs->port_status [port]);
+	return 0;
+}
+
+#ifdef CONFIG_USB_PORT_AMD5536OTG
+
+static void start_hnp (struct ehci_hcd *ehci)
+{
+	const unsigned	port = ehci_to_hcd(ehci)->self.otg_port - 1;
+	unsigned long	flags;
+	u32		status;
+
+	otg_start_hnp (ehci->transceiver);
+	local_irq_save (flags);
+	status = readl (&ehci->regs->port_status [port]);
+	if (!(status & PORT_OWNER) && (status & PORT_PE))
+		writel (status | PORT_SUSPEND, &ehci->regs->port_status [port]);
+	local_irq_restore (flags);
+}
+#endif
+
+static void start_hnp (struct ehci_hcd *ehci);
+
+#else
+
+#define	ehci_start_port_reset		NULL
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 #define	PORT_WAKE_BITS 	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
 
 static int ehci_hub_control (
@@ -517,10 +581,20 @@ static int ehci_hub_control (
 			if ((temp & PORT_PE) == 0
 					|| (temp & PORT_RESET) != 0)
 				goto error;
+#ifdef CONFIG_USB_OTG_HIGHSPEED
+			if (hcd->self.otg_port == (wIndex + 1)
+					&& hcd->self.b_hnp_enable)
+				start_hnp(ehci);
+			else {
+#endif
+
 			if (hcd->remote_wakeup)
 				temp |= PORT_WAKE_BITS;
 			writel (temp | PORT_SUSPEND,
 				&ehci->regs->port_status [wIndex]);
+#ifdef CONFIG_USB_OTG_HIGHSPEED
+			}
+#endif
 			break;
 		case USB_PORT_FEAT_POWER:
 			if (HCS_PPC (ehci->hcs_params))
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 18e257c..d762443 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -75,6 +75,14 @@ struct ehci_hcd {			/* one per controlle
 	/* per root hub port */
 	unsigned long		reset_done [EHCI_MAX_ROOT_PORTS];
 
+#ifdef CONFIG_USB_OTG_HIGHSPEED
+	/*
+	 * OTG controller needs software interaction
+	 */
+	struct otg_transceiver	*transceiver;
+	unsigned		power_budget;
+#endif
+
 	/* per-HC memory pools (could be per-bus, but ...) */
 	struct dma_pool		*qh_pool;	/* qh per active urb */
 	struct dma_pool		*qtd_pool;	/* one or more per qh */
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index 486202d..a5f2dfe 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -19,9 +19,10 @@
  */
 
 #include <linux/platform_device.h>
-
 #include <asm/mach-au1x00/au1000.h>
 
+#ifndef CONFIG_SOC_AU1200
+
 #define USBH_ENABLE_BE (1<<0)
 #define USBH_ENABLE_C  (1<<1)
 #define USBH_ENABLE_E  (1<<2)
@@ -36,16 +37,46 @@
 #error not byte order defined
 #endif
 
+#else   /* Au1200 */
+
+#define USB_HOST_CONFIG    (USB_MSR_BASE + USB_MSR_MCFG)
+#define USB_MCFG_PFEN     (1<<31)
+#define USB_MCFG_RDCOMB   (1<<30)
+#define USB_MCFG_SSDEN    (1<<23)
+#define USB_MCFG_OHCCLKEN (1<<16)
+#define USB_MCFG_UCAM     (1<<7)
+#define USB_MCFG_OBMEN    (1<<1)
+#define USB_MCFG_OMEMEN   (1<<0)
+
+#define USBH_ENABLE_CE    USB_MCFG_OHCCLKEN
+#ifdef CONFIG_DMA_COHERENT
+#define USBH_ENABLE_INIT  (USB_MCFG_OHCCLKEN \
+                         | USB_MCFG_PFEN | USB_MCFG_RDCOMB \
+                         | USB_MCFG_SSDEN | USB_MCFG_UCAM \
+                         | USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
+#else
+#define USBH_ENABLE_INIT  (USB_MCFG_OHCCLKEN \
+                         | USB_MCFG_PFEN | USB_MCFG_RDCOMB \
+                         | USB_MCFG_SSDEN \
+                         | USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
+#endif
+#define USBH_DISABLE      (USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
+
+#endif  /* Au1200 */
+
 extern int usb_disabled(void);
 
 /*-------------------------------------------------------------------------*/
 
-static void au1xxx_start_hc(struct platform_device *dev)
+static void au1xxx_start_ohc(struct platform_device *dev)
 {
 	printk(KERN_DEBUG __FILE__
 		": starting Au1xxx OHCI USB Controller\n");
 
 	/* enable host controller */
+	
+#ifndef CONFIG_SOC_AU1200
+
 	au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
 	udelay(1000);
 	au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG);
@@ -56,17 +87,46 @@ static void au1xxx_start_hc(struct platf
 		!(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
 		udelay(1000);
 
+#else   /* Au1200 */
+
+	/* write HW defaults again in case Yamon cleared them */
+	if (au_readl(USB_HOST_CONFIG) == 0) {
+	au_writel(0x00d02000, USB_HOST_CONFIG);
+	au_readl(USB_HOST_CONFIG);
+	udelay(1000);
+	}
+	au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+	au_readl(USB_HOST_CONFIG);
+	udelay(1000);
+	au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+	au_readl(USB_HOST_CONFIG);
+	udelay(1000);
+
+#endif  /* Au1200 */
+
 	printk(KERN_DEBUG __FILE__
 	": Clock to USB host has been enabled \n");
 }
 
-static void au1xxx_stop_hc(struct platform_device *dev)
+static void au1xxx_stop_ohc(struct platform_device *dev)
 {
 	printk(KERN_DEBUG __FILE__
 	       ": stopping Au1xxx OHCI USB Controller\n");
 
+#ifndef CONFIG_SOC_AU1200
+
 	/* Disable clock */
 	au_writel(readl((void *)USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
+
+#else   /* Au1200 */
+
+	/* Disable mem */
+	au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+	udelay(1000);
+	/* Disable clock */
+	au_writel(~USBH_ENABLE_CE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+	au_readl(USB_HOST_CONFIG);
+#endif  /* Au1200 */
 }

@@ -85,14 +145,24 @@ static void au1xxx_stop_hc(struct platfo
  * through the hotplug entry's driver_data.
  *
  */
-int usb_hcd_au1xxx_probe (const struct hc_driver *driver,
+int usb_ohci_au1xxx_probe (const struct hc_driver *driver,
 			  struct platform_device *dev)
 {
 	int retval;
 	struct usb_hcd *hcd;
 
+#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
+
+	/* Au1200 AB USB does not support coherent memory */
+	if (!(read_c0_prid() & 0xff)) {
+	pr_info ("Au1200 ohci: !!! This is chip revision AB                     !!!\n");
+	pr_info ("             !!! update your board or re-configure the kernel !!!\n");
+	return -ENODEV;
+	}
+#endif
+
 	if (dev->resource[1].flags != IORESOURCE_IRQ) {
-		pr_debug ("resource[1] is not IORESOURCE_IRQ");
+		pr_debug ("resource[1] is not IORESOURCE_IRQ\n");
 		retval = -ENOMEM;
 	}
 
@@ -103,26 +173,26 @@ int usb_hcd_au1xxx_probe (const struct h
 	hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
 
 	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
-		pr_debug("request_mem_region failed");
+		pr_debug("request_mem_region failed\n");
 		retval = -EBUSY;
 		goto err1;
 	}
 
 	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
 	if (!hcd->regs) {
-		pr_debug("ioremap failed");
+		pr_debug("ioremap failed\n");
 		retval = -ENOMEM;
 		goto err2;
 	}
 
-	au1xxx_start_hc(dev);
+	au1xxx_start_ohc(dev);
 	ohci_hcd_init(hcd_to_ohci(hcd));
 
-	retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
+	retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT | SA_SHIRQ);
 	if (retval == 0)
 		return retval;
 
-	au1xxx_stop_hc(dev);
+	au1xxx_stop_ohc(dev);
 	iounmap(hcd->regs);
  err2:
 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
@@ -145,10 +215,10 @@ int usb_hcd_au1xxx_probe (const struct h
  * context, normally "rmmod", "apmd", or something similar.
  *
  */
-void usb_hcd_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev)
+void usb_ohci_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev)
 {
 	usb_remove_hcd(hcd);
-	au1xxx_stop_hc(dev);
+	au1xxx_stop_ohc(dev);
 	iounmap(hcd->regs);
 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	usb_put_hcd(hcd);
@@ -216,11 +286,15 @@ static const struct hc_driver ohci_au1xx
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
-#ifdef	CONFIG_PM
-	.bus_suspend =		ohci_bus_suspend,
-	.bus_resume =		ohci_bus_resume,
+#ifdef	CONFIG_USB_SUSPEND
+	.hub_suspend =		ohci_hub_suspend,
+	.hub_resume =		ohci_hub_resume,
 #endif
 	.start_port_reset =	ohci_start_port_reset,
+#ifdef	CONFIG_USB_OTG
+	.start_otg =		ohci_start_otg,
+	.stop_otg =		ohci_stop_otg,
+#endif
 };
 
 /*-------------------------------------------------------------------------*/
@@ -234,7 +308,7 @@ static int ohci_hcd_au1xxx_drv_probe(str
 	if (usb_disabled())
 		return -ENODEV;
 
-	ret = usb_hcd_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev);
+	ret = usb_ohci_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev);
 	return ret;
 }
 
@@ -242,7 +316,7 @@ static int ohci_hcd_au1xxx_drv_remove(st
 {
 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
-	usb_hcd_au1xxx_remove(hcd, pdev);
+	usb_ohci_au1xxx_remove(hcd, pdev);
 	return 0;
 }
 	/*TBD*/
@@ -287,3 +361,4 @@ static void __exit ohci_hcd_au1xxx_clean
 
 module_init (ohci_hcd_au1xxx_init);
 module_exit (ohci_hcd_au1xxx_cleanup);
+
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index b8efc6e..b610ea5 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -879,6 +879,79 @@ static int ohci_restart (struct ohci_hcd
 
 /*-------------------------------------------------------------------------*/
 
+#if defined(CONFIG_USB_OTG) && (CONFIG_USB_PORT_AMD5536OTG)
+
+static int ohci_start_otg (struct usb_hcd *hcd)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
+
+	if (!hcd->self.otg_port) {
+		hcd->self.otg_port = USB_OTG_PORT;
+		ohci->power_budget = OTG_PWR_BUDGET;
+	}
+	if (usb_otg_get_transceiver) {
+		ohci->transceiver = usb_otg_get_transceiver();
+		if (ohci->transceiver) {
+			int	status;
+
+#ifdef CONFIG_USB_OTG_HIGHSPEED
+			if (ohci->transceiver->companion) {
+	ohci_err (ohci, "OTG already registered\n");
+	return -EBUSY;
+			}
+			ohci->transceiver->companion = &hcd->self;
+			hcd->self.hand_over = 0;
+#else
+			if (ohci->transceiver->host) {
+	ohci_err (ohci, "OTG already registered\n");
+	return -EBUSY;
+			}
+			ohci->transceiver->host = &hcd->self;
+#endif
+			status = ohci->transceiver->set_host(
+					ohci->transceiver, &hcd->self);
+
+			ohci_dbg(ohci, "init %s transceiver, status %d\n",
+				 ohci->transceiver->label, status);
+
+			/* if (status)
+			put_device(ohci->transceiver->dev); */
+			return status;
+		}
+		else  {
+			ohci_err (ohci, "can't find transceiver\n");
+			return -ENODEV;
+		}
+	}
+	else {
+		ohci_info (ohci, "OTG driver not loaded\n");
+		return 0;
+	}
+}
+
+static void ohci_stop_otg (struct usb_hcd *hcd)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
+
+	if (ohci->transceiver) {
+		ohci_dbg (ohci, "clean up %s transceiver\n",
+			  ohci->transceiver->label);
+#ifdef CONFIG_USB_OTG_HIGHSPEED
+		ohci->transceiver->companion = NULL;
+#else
+		ohci->transceiver->host = NULL;
+#endif
+		ohci->transceiver->set_host(ohci->transceiver, NULL);
+		ohci->transceiver = NULL;
+		ohci->power_budget = 0;
+		hcd->self.otg_port = 0;
+	}
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
 
 MODULE_AUTHOR (DRIVER_AUTHOR);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 72e3b12..31a8a0d 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -431,13 +431,31 @@ static int ohci_start_port_reset (struct
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	u32			status;
+	int			usec = 500;  /* 500 usec handshake time */
 
 	if (!port)
 		return -EINVAL;
 	port--;
 
 	/* start port reset before HNP protocol times out */
+#ifdef	CONFIG_USB_OTG_HIGHSPEED
+
+	if (hcd->self.hand_over && (port + 1 == hcd->self.otg_port)) {
+		udelay (usec);
+		usec = 0;
+		status = ohci_readl (ohci, &ohci->regs->roothub.portstatus [port]);
+	}
+	else {
+#endif
 	status = ohci_readl(ohci, &ohci->regs->roothub.portstatus [port]);
+	while (usec && !(status & RH_PS_CCS)) {
+		status = ohci_readl (ohci, &ohci->regs->roothub.portstatus [port]);
+		usec--;
+		udelay(1);
+	}
+#ifdef	CONFIG_USB_OTG_HIGHSPEED
+	}
+#endif	
 	if (!(status & RH_PS_CCS))
 		return -ENODEV;
 
@@ -446,8 +464,29 @@ static int ohci_start_port_reset (struct
 	return 0;
 }
 
+#ifdef	CONFIG_USB_PORT_AMD5536OTG
+
+static void start_hnp (struct ohci_hcd *ohci)
+{
+	const unsigned	port = ohci_to_hcd(ohci)->self.otg_port - 1;
+	unsigned long	flags;
+	u32		status;
+
+	otg_start_hnp (ohci->transceiver);
+
+	local_irq_save (flags);
+	status = ohci_readl (ohci, &ohci->regs->roothub.portstatus [port]);
+	if (status & RH_PS_PES)
+		ohci_writel (ohci, RH_PS_PSS,
+		&ohci->regs->roothub.portstatus [port]);
+	local_irq_restore (flags);
+}
+#else
+
 static void start_hnp(struct ohci_hcd *ohci);
 
+#endif
+
 #else
 
 #define	ohci_start_port_reset		NULL
@@ -482,10 +521,27 @@ static inline void root_port_reset (stru
 	u16	now = ohci_readl(ohci, &ohci->regs->fmnumber);
 	u16	reset_done = now + PORT_RESET_MSEC;
 
+#ifdef CONFIG_USB_OTG
+	struct usb_hcd *hcd = ohci_to_hcd (ohci);
+#endif
+
 	/* build a "continuous enough" reset signal, with up to
 	 * 3msec gap between pulses.  scheduler HZ==100 must work;
 	 * this might need to be deadline-scheduled.
 	 */
+
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_HIGHSPEED
+	if (hcd->self.hand_over && (port + 1 == hcd->self.otg_port)) {
+		hcd->self.hand_over = 0;
+		reset_done = now + PORT_RESET_HW_MSEC - 1;
+	}
+	else
+#else
+	if (hcd->self.is_b_host)
+		reset_done = now + PORT_RESET_HW_MSEC - 1;
+#endif
+#endif
 	do {
 		/* spin until any current reset finishes */
 		for (;;) {
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 1b09dde..54d3afa 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -188,6 +188,10 @@ static const struct hc_driver ohci_pci_h
 	.bus_resume =		ohci_bus_resume,
 #endif
 	.start_port_reset =	ohci_start_port_reset,
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG)
+	.start_otg =		ohci_start_otg,
+	.stop_otg =		ohci_stop_otg,
+#endif	
 };
 
 /*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index caacf14..b94717a 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -371,6 +371,7 @@ struct ohci_hcd {
 	 * other external transceivers should be software-transparent 
 	 */
 	struct otg_transceiver	*transceiver;
+	unsigned		power_budget;
 
 	/*
 	 * memory management for queue data structures
diff --git a/include/asm-mips/mach-mips/cpu-feature-overrides.h b/include/asm-mips/mach-mips/cpu-feature-overrides.h
index 9f92aed..e06af6c 100644
--- a/include/asm-mips/mach-mips/cpu-feature-overrides.h
+++ b/include/asm-mips/mach-mips/cpu-feature-overrides.h
@@ -29,7 +29,11 @@
 /* #define cpu_has_prefetch	? */
 #define cpu_has_mcheck		1
 /* #define cpu_has_ejtag	? */
+#ifdef CONFIG_CPU_HAS_LLSC
 #define cpu_has_llsc		1
+#else
+#define cpu_has_llsc		0
+#endif
 /* #define cpu_has_vtag_icache	? */
 /* #define cpu_has_dc_aliases	? */
 /* #define cpu_has_ic_fills_f_dc ? */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index d81b050..e71b584 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -270,6 +270,7 @@ struct usb_bus {
 	u8 otg_port;			/* 0, or number of OTG/HNP port */
 	unsigned is_b_host:1;		/* true during some HNP roleswitches */
 	unsigned b_hnp_enable:1;	/* OTG: did A-Host enable HNP? */
+	unsigned hand_over:1;		/* HS controller detected FS device */
 
 	int devnum_next;		/* Next open device number in
 					 * round-robin allocation */
@@ -1015,6 +1016,12 @@ extern int usb_clear_halt(struct usb_dev
 extern int usb_reset_configuration(struct usb_device *dev);
 extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
 
+#if defined(CONFIG_USB_OTG) && defined(CONFIG_USB_PORT_AMD5536OTG)
+extern struct otg_transceiver * (*usb_otg_get_transceiver)(void);
+extern int usb_host_register_otg (struct otg_transceiver * (*get_transceiver)(void));
+extern void usb_host_deregister_otg(void);
+#endif
+
 /*
  * timeouts, in milliseconds, used for sending/receiving control messages
  * they typically complete within a few frames (msec) after they're issued

---

From: David Brownell <[email-masked]>
Date: Fri, 9 Dec 2005 21:13:42 -0800

> hands on AU1200 parts to make this a hot topic.  

Interesting.  This is actually a couple different things ... the OTG
related bits would IMO be good to split out from the EHCI ones, and
from the OHCI ones.

Maybe once 2.6.15 gets out you'd split these out a bit?

> paving the way by pushing to seperate EHCI and PCI in the USB subsystem.

Actually that patch started with Matt Porter, although some earlier
non-mergeable versions came from ARC (now TDI).  And splitting it out
turned up a bunch of problems, mostly now fixed.

> Note that the AU1200 does support UDC/OTG as well, but thats another patch 

Actually you included a preview in this patch.  Interesting ... that
makes three OTG implementations I've seen on Linux ... the second
highspeed one, too.

I think you shouldn't need that OTG_HIGHSPEED symbol, given there's
already USB_OTG and USB_GADGET_DUALSPEED, that's implicit.  And in
fact, it'd be implicit inside EHCI given just USB_OTG!

That OTG stuff needs work yet.  No <linux/usb.h> changes should be
needed, and I'm not sure what this "relaxed whitelist" is for.  But
by the time those answers are apparent, I expect you'll have give
us mergeable patches for OHCI, EHCI, and the UDC.  ;)

- Dave

---

From: Pete Popov <[email-masked]>
Date: Fri, 09 Dec 2005 22:42:34 -0800

> turned up a bunch of problems, mostly now fixed.

Matt Porter is included in the 'merry band' :) He's a partner at
Embedded Alley.

Pete

---

From: Bora Sahin <[email-masked]>
Date: Mon, 12 Dec 2005 12:51:25 +0200

Hi,

Thursday, December 8, 2005, 11:00:42 PM, you wrote:

Jordan> Ok, here we go.  I give you the OHCI/EHCI host controller support for
Jordan> the Alchemy AU1200 processor.  I'm sending this up, partly because I
Jordan> have it ready to go, but also because it seems that enough folks are
Jordan> getting their hands on AU1200 parts to make this a hot topic.

Especially, it's high time to me... :-)

Jordan> Special thanks to Pete Popov and his merry band of kernel hackers for
Jordan> paving the way by pushing to seperate EHCI and PCI in the USB subsystem.

Me to...

I have a few comments related to the OHCI part of it...

+#else   /* Au1200 */
+

Maybe, the place where to put those defines is
include/asm-mips/mach-au1x00/au1000.h? Because there are some similar defines in
that file, actually shift values. For consistency, are they used?..

Aha! What I was lacking in my patch was USB_MCFG_UCAM! For test, I added it to
my patch, and it worked! Reserved in the doc. So do USB_MCFG_PFEN and
USB_MCFG_RDCOMB. What's the meaning of that fields? 

+#else   /* Au1200 */
+

Are au_readl() and udelay() necessary between the two au_writel()? Just for
curiosity, I tried it without them and as it seems, it worked! 

-- 
Bora SAHIN