summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci.h
diff options
context:
space:
mode:
authorVitaly Bordug <vitb@kernel.crashing.org>2008-11-09 19:43:30 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-07 18:59:52 +0100
commit796bcae7361c28cf825780f6f1aac9dd3411394e (patch)
tree3fe4ee89e373bf29f868d70430098b7dc625c16f /drivers/usb/host/ehci.h
parentUSB: fsl_qe_udc: Check for muram allocation errors (diff)
downloadlinux-796bcae7361c28cf825780f6f1aac9dd3411394e.tar.xz
linux-796bcae7361c28cf825780f6f1aac9dd3411394e.zip
USB: powerpc: Workaround for the PPC440EPX USBH_23 errata [take 3]
A published errata for ppc440epx states, that when running Linux with both EHCI and OHCI modules loaded, the EHCI module experiences a fatal error when a high-speed device is connected to the USB2.0, and functions normally if OHCI module is not loaded. There used to be recommendation to use only hi-speed or full-speed devices with specific conditions, when respective module was unloaded. Later, it was observed that ohci suspend is enough to keep things going, and it was turned into workaround, as explained below. Quote from original descriprion: The 440EPx USB 2.0 Host controller is an EHCI compliant controller. In USB 2.0 Host controllers, each EHCI controller has one or more companion controllers, which may be OHCI or UHCI. An USB 2.0 Host controller will contain one or more ports. For each port, only one of the controllers is connected at any one time. In the 440EPx, there is only one OHCI companion controller, and only one USB 2.0 Host port. All ports on an USB 2.0 controller default to the companion controller. If you load only an ohci driver, it will have control of the ports and any deviceplugged in will operate, although high speed devices will be forced to operate at full speed. When an ehci driver is loaded, it explicitly takes control of the ports. If there is a device connected, and / or every time there is a new device connected, the ehci driver determines if the device is high speed or not. If it is high speed, the driver retains control of the port. If it is not, the driver explicitly gives the companion controller control of the port. The is a software workaround that uses Initial version of the software workaround was posted to linux-usb-devel: http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg54019.html and later available from amcc.com: http://www.amcc.com/Embedded/Downloads/download.html?cat=1&family=15&ins=2 The patch below is generally based on the latter, but reworked to powerpc/of_device USB drivers, and uses a few devicetree inquiries to get rid of (some) hardcoded defines. Signed-off-by: Vitaly Bordug <vitb@kernel.crashing.org> Signed-off-by: Stefan Roese <sr@denx.de> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci.h')
-rw-r--r--drivers/usb/host/ehci.h34
1 files changed, 34 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index c7d4b5a06bdb..fb7054ccf4fc 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -120,6 +120,16 @@ struct ehci_hcd { /* one per controller */
unsigned has_fsl_port_bug:1; /* FreeScale */
unsigned big_endian_mmio:1;
unsigned big_endian_desc:1;
+ unsigned has_amcc_usb23:1;
+
+ /* required for usb32 quirk */
+ #define OHCI_CTRL_HCFS (3 << 6)
+ #define OHCI_USB_OPER (2 << 6)
+ #define OHCI_USB_SUSPEND (3 << 6)
+
+ #define OHCI_HCCTRL_OFFSET 0x4
+ #define OHCI_HCCTRL_LEN 0x4
+ __hc32 *ohci_hcctrl_reg;
u8 sbrn; /* packed release number */
@@ -636,6 +646,30 @@ static inline void ehci_writel(const struct ehci_hcd *ehci,
#endif
}
+/*
+ * On certain ppc-44x SoC there is a HW issue, that could only worked around with
+ * explicit suspend/operate of OHCI. This function hereby makes sense only on that arch.
+ * Other common bits are dependant on has_amcc_usb23 quirk flag.
+ */
+#ifdef CONFIG_44x
+static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
+{
+ u32 hc_control;
+
+ hc_control = (readl_be(ehci->ohci_hcctrl_reg) & ~OHCI_CTRL_HCFS);
+ if (operational)
+ hc_control |= OHCI_USB_OPER;
+ else
+ hc_control |= OHCI_USB_SUSPEND;
+
+ writel_be(hc_control, ehci->ohci_hcctrl_reg);
+ (void) readl_be(ehci->ohci_hcctrl_reg);
+}
+#else
+static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
+{ }
+#endif
+
/*-------------------------------------------------------------------------*/
/*