aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-03-21 09:25:47 -0800
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-21 09:25:47 -0800
commit2bf2154c6bb5599e3ec3f73c34861a0b12aa839e (patch)
tree62691bd915e2e3c2e6648306d3fb893f7a1dc57e /drivers/usb/host
parent08a4ecee986dd98e86090ff5faac4782b6765aed (diff)
parent71a8924bee63d891f6256d560e32416a458440b3 (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (81 commits) [PATCH] USB: omninet: fix up debugging comments [PATCH] USB serial: add navman driver [PATCH] USB: Fix irda-usb use after use [PATCH] USB: rtl8150 small fix [PATCH] USB: ftdi_sio: add Icom ID1 USB product and vendor ids [PATCH] USB: cp2101: add new device IDs [PATCH] USB: fix check_ctrlrecip to allow control transfers in state ADDRESS [PATCH] USB: vicam.c: fix a NULL pointer dereference [PATCH] USB: ZC0301 driver bugfix [PATCH] USB: add support for Creativelabs Silvercrest USB keyboard [PATCH] USB: storage: new unusual_devs.h entry: Mitsumi 7in1 Card Reader [PATCH] USB: storage: unusual_devs.h entry 0420:0001 [PATCH] USB: storage: another unusual_devs.h entry [PATCH] USB: storage: sandisk unusual_devices entry [PATCH] USB: fix initdata issue in isp116x-hcd [PATCH] USB: usbcore: usb_set_configuration oops (NULL ptr dereference) [PATCH] USB: usbcore: Don't assume a USB configuration includes any interfaces [PATCH] USB: ub 03 drop stall clearing [PATCH] USB: ub 02 remove diag [PATCH] USB: ub 01 remove first_open ...
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/usb/host/ehci-au1xxx.c297
-rw-r--r--drivers/usb/host/ehci-fsl.c366
-rw-r--r--drivers/usb/host/ehci-fsl.h37
-rw-r--r--drivers/usb/host/ehci-hcd.c13
-rw-r--r--drivers/usb/host/ehci-hub.c4
-rw-r--r--drivers/usb/host/ehci-mem.c11
-rw-r--r--drivers/usb/host/ehci-pci.c25
-rw-r--r--drivers/usb/host/ehci-q.c17
-rw-r--r--drivers/usb/host/ehci-sched.c20
-rw-r--r--drivers/usb/host/ehci.h18
-rw-r--r--drivers/usb/host/hc_crisv10.c12
-rw-r--r--drivers/usb/host/isp116x-hcd.c5
-rw-r--r--drivers/usb/host/ohci-at91.c306
-rw-r--r--drivers/usb/host/ohci-au1xxx.c102
-rw-r--r--drivers/usb/host/ohci-hcd.c54
-rw-r--r--drivers/usb/host/ohci-hub.c12
-rw-r--r--drivers/usb/host/ohci-pci.c15
-rw-r--r--drivers/usb/host/sl811-hcd.c3
-rw-r--r--drivers/usb/host/uhci-debug.c356
-rw-r--r--drivers/usb/host/uhci-hcd.c127
-rw-r--r--drivers/usb/host/uhci-hcd.h188
-rw-r--r--drivers/usb/host/uhci-hub.c21
-rw-r--r--drivers/usb/host/uhci-q.c1294
24 files changed, 2129 insertions, 1176 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index be3fd9bce57..e27b79a3c05 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 00000000000..63eadeec132
--- /dev/null
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -0,0 +1,297 @@
+/*
+ * EHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * Bus Glue for AMD Alchemy Au1xxx
+ *
+ * Based on "ohci-au1xxx.c" by Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Modified for AMD Alchemy Au1200 EHC
+ * by K.Boge <karsten.boge@amd.com>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach-au1x00/au1000.h>
+
+#ifndef CONFIG_SOC_AU1200
+#error "this Alchemy chip doesn't have EHCI"
+#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;
+ struct ehci_hcd *ehci;
+
+#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
+
+ /* Au1200 AB USB does not support coherent memory */
+ if (!(read_c0_prid() & 0xff)) {
+ pr_info("%s: this is chip revision AB!\n", dev->dev.name);
+ pr_info("%s: update your board or re-configure the kernel\n",
+ dev->dev.name);
+ return -ENODEV;
+ }
+#endif
+
+ au1xxx_start_ehc(dev);
+
+ 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;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ /* 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;
+
+ 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);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ au1xxx_stop_ehc(dev);
+}
+
+/*-------------------------------------------------------------------------*/
+
+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,
+ .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_PM
+ .hub_suspend = ehci_hub_suspend,
+ .hub_resume = ehci_hub_resume,
+#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-fsl.c b/drivers/usb/host/ehci-fsl.c
new file mode 100644
index 00000000000..f985f121a24
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.c
@@ -0,0 +1,366 @@
+/*
+ * (C) Copyright David Brownell 2000-2002
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Ported to 834x by Randy Vinson <rvinson@mvista.com> using code provided
+ * by Hunter Wu.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+
+#include "ehci-fsl.h"
+
+/* FIXME: Power Managment is un-ported so temporarily disable it */
+#undef CONFIG_PM
+
+/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_fsl_probe - initialize FSL-based HCDs
+ * @drvier: Driver to be used for this HCD
+ * @pdev: USB Host Controller being probed
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller.
+ *
+ */
+int usb_hcd_fsl_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int retval;
+ unsigned int temp;
+
+ pr_debug("initializing FSL-SOC USB Controller\n");
+
+ /* Need platform data for setup */
+ pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "No platform data for %s.\n", pdev->dev.bus_id);
+ return -ENODEV;
+ }
+
+ /*
+ * This is a host mode driver, verify that we're supposed to be
+ * in host mode.
+ */
+ if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
+ (pdata->operating_mode == FSL_USB2_MPH_HOST))) {
+ dev_err(&pdev->dev,
+ "Non Host Mode configured for %s. Wrong driver linked.\n",
+ pdev->dev.bus_id);
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ pdev->dev.bus_id);
+ return -ENODEV;
+ }
+ irq = res->start;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ pdev->dev.bus_id);
+ retval = -ENODEV;
+ goto err2;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto err2;
+ }
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto err3;
+ }
+
+ /* Enable USB controller */
+ temp = in_be32(hcd->regs + 0x500);
+ out_be32(hcd->regs + 0x500, temp | 0x4);
+
+ /* Set to Host mode */
+ temp = in_le32(hcd->regs + 0x1a8);
+ out_le32(hcd->regs + 0x1a8, temp | 0x3);
+
+ retval = usb_add_hcd(hcd, irq, SA_SHIRQ);
+ if (retval != 0)
+ goto err4;
+ return retval;
+
+ err4:
+ iounmap(hcd->regs);
+ err3:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err2:
+ usb_put_hcd(hcd);
+ err1:
+ dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval);
+ return retval;
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_fsl_probe().
+ *
+ */
+void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev)
+{
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+static void mpc83xx_setup_phy(struct ehci_hcd *ehci,
+ enum fsl_usb2_phy_modes phy_mode,
+ unsigned int port_offset)
+{
+ u32 portsc = 0;
+ switch (phy_mode) {
+ case FSL_USB2_PHY_ULPI:
+ portsc |= PORT_PTS_ULPI;
+ break;
+ case FSL_USB2_PHY_SERIAL:
+ portsc |= PORT_PTS_SERIAL;
+ break;
+ case FSL_USB2_PHY_UTMI_WIDE:
+ portsc |= PORT_PTS_PTW;
+ /* fall through */
+ case FSL_USB2_PHY_UTMI:
+ portsc |= PORT_PTS_UTMI;
+ break;
+ case FSL_USB2_PHY_NONE:
+ break;
+ }
+ writel(portsc, &ehci->regs->port_status[port_offset]);
+}
+
+static void mpc83xx_usb_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct fsl_usb2_platform_data *pdata;
+ void __iomem *non_ehci = hcd->regs;
+
+ pdata =
+ (struct fsl_usb2_platform_data *)hcd->self.controller->
+ platform_data;
+ /* Enable PHY interface in the control reg. */
+ out_be32(non_ehci + FSL_SOC_USB_CTRL, 0x00000004);
+ out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
+
+ if (pdata->operating_mode == FSL_USB2_DR_HOST)
+ mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
+
+ if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
+ unsigned int chip, rev, svr;
+
+ svr = mfspr(SPRN_SVR);
+ chip = svr >> 16;
+ rev = (svr >> 4) & 0xf;
+
+ /* Deal with USB Erratum #14 on MPC834x Rev 1.0 & 1.1 chips */
+ if ((rev == 1) && (chip >= 0x8050) && (chip <= 0x8055))
+ ehci->has_fsl_port_bug = 1;
+
+ if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
+ mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
+ if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
+ mpc83xx_setup_phy(ehci, pdata->phy_mode, 1);
+ }
+
+ /* put controller in host mode. */
+ writel(0x00000003, non_ehci + FSL_SOC_USB_USBMODE);
+ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
+ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
+ out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
+}
+
+/* called after powerup, by probe or system-pm "wakeup" */
+static int ehci_fsl_reinit(struct ehci_hcd *ehci)
+{
+ mpc83xx_usb_setup(ehci_to_hcd(ehci));
+ ehci_port_power(ehci, 0);
+
+ return 0;
+}
+
+/* called during probe() after chip reset completes */
+static int ehci_fsl_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ /* EHCI registers start at offset 0x100 */
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(readl(&ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->is_tdi_rh_tt = 1;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+
+ retval = ehci_fsl_reinit(ehci);
+ return retval;
+}
+
+static const struct hc_driver ehci_fsl_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Freescale On-Chip EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_fsl_setup,
+ .start = ehci_run,
+#ifdef CONFIG_PM
+ .suspend = ehci_bus_suspend,
+ .resume = ehci_bus_resume,
+#endif
+ .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,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+};
+
+static int ehci_fsl_drv_probe(struct platform_device *pdev)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
+}
+
+static int ehci_fsl_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_hcd_fsl_remove(hcd, pdev);
+
+ return 0;
+}
+
+static struct platform_driver ehci_fsl_dr_driver = {
+ .probe = ehci_fsl_drv_probe,
+ .remove = ehci_fsl_drv_remove,
+ .driver = {
+ .name = "fsl-usb2-dr",
+ },
+};
+
+static struct platform_driver ehci_fsl_mph_driver = {
+ .probe = ehci_fsl_drv_probe,
+ .remove = ehci_fsl_drv_remove,
+ .driver = {
+ .name = "fsl-usb2-mph",
+ },
+};
+
+static int __init ehci_fsl_init(void)
+{
+ int retval;
+
+ pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
+ hcd_name,
+ sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
+ sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
+
+ retval = platform_driver_register(&ehci_fsl_dr_driver);
+ if (retval)
+ return retval;
+
+ return platform_driver_register(&ehci_fsl_mph_driver);
+}
+
+static void __exit ehci_fsl_cleanup(void)
+{
+ platform_driver_unregister(&ehci_fsl_mph_driver);
+ platform_driver_unregister(&ehci_fsl_dr_driver);
+}
+
+module_init(ehci_fsl_init);
+module_exit(ehci_fsl_cleanup);
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
new file mode 100644
index 00000000000..caac0d1967d
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2005 freescale semiconductor
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _EHCI_FSL_H
+#define _EHCI_FSL_H
+
+/* offsets for the non-ehci registers in the FSL SOC USB controller */
+#define FSL_SOC_USB_ULPIVP 0x170
+#define FSL_SOC_USB_PORTSC1 0x184
+#define PORT_PTS_MSK (3<<30)
+#define PORT_PTS_UTMI (0<<30)
+#define PORT_PTS_ULPI (2<<30)
+#define PORT_PTS_SERIAL (3<<30)
+#define PORT_PTS_PTW (1<<28)
+#define FSL_SOC_USB_PORTSC2 0x188
+#define FSL_SOC_USB_USBMODE 0x1a8
+#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
+#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
+#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
+#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */
+#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */
+#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
+#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 9dd3d14c64f..79f2d8b9bfb 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -889,8 +889,19 @@ MODULE_LICENSE ("GPL");
#ifdef CONFIG_PCI
#include "ehci-pci.c"
+#define EHCI_BUS_GLUED
#endif
-#if !defined(CONFIG_PCI)
+#ifdef CONFIG_PPC_83xx
+#include "ehci-fsl.c"
+#define EHCI_BUS_GLUED
+#endif
+
+#ifdef CONFIG_SOC_AU1X00
+#include "ehci-au1xxx.c"
+#define EHCI_BUS_GLUED
+#endif
+
+#ifndef EHCI_BUS_GLUED
#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 69b0b9be7a6..d03e3cad5ca 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -359,6 +359,8 @@ static int ehci_hub_control (
case USB_PORT_FEAT_SUSPEND:
if (temp & PORT_RESET)
goto error;
+ if (ehci->no_selective_suspend)
+ break;
if (temp & PORT_SUSPEND) {
if ((temp & PORT_PE) == 0)
goto error;
@@ -514,6 +516,8 @@ static int ehci_hub_control (
temp &= ~PORT_RWC_BITS;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
+ if (ehci->no_selective_suspend)
+ break;
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 91c2ab43cbc..766061e0260 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -75,7 +75,6 @@ static void qh_destroy (struct kref *kref)
}
if (qh->dummy)
ehci_qtd_free (ehci, qh->dummy);
- usb_put_dev (qh->dev);
dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
}
@@ -221,13 +220,9 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
ehci->periodic [i] = EHCI_LIST_END;
/* software shadow of hardware table */
- ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags);
- if (ehci->pshadow == NULL) {
- goto fail;
- }
- memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
-
- return 0;
+ ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
+ if (ehci->pshadow != NULL)
+ return 0;
fail:
ehci_dbg (ehci, "couldn't init memory\n");
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 3a6687df559..1e03f1a5a5f 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -106,11 +106,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
}
break;
case PCI_VENDOR_ID_NVIDIA:
+ switch (pdev->device) {
/* NVidia reports that certain chips don't handle
* QH, ITD, or SITD addresses above 2GB. (But TD,
* data buffer, and periodic schedule are normal.)
*/
- switch (pdev->device) {
case 0x003c: /* MCP04 */
case 0x005b: /* CK804 */
case 0x00d8: /* CK8 */
@@ -120,6 +120,14 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
ehci_warn(ehci, "can't enable NVidia "
"workaround for >2GB RAM\n");
break;
+ /* Some NForce2 chips have problems with selective suspend;
+ * fixed in newer silicon.
+ */
+ case 0x0068:
+ pci_read_config_dword(pdev, PCI_REVISION_ID, &temp);
+ if ((temp & 0xff) < 0xa4)
+ ehci->no_selective_suspend = 1;
+ break;
}
break;
}
@@ -163,6 +171,21 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
device_init_wakeup(&pdev->dev, 1);
}
+#ifdef CONFIG_USB_SUSPEND
+ /* REVISIT: the controller works fine for wakeup iff the root hub
+ * itself is "globally" suspended, but usbcore currently doesn't
+ * understand such things.
+ *
+ * System suspend currently expects to be able to suspend the entire
+ * device tree, device-at-a-time. If we failed selective suspend
+ * reports, system suspend would fail; so the root hub code must claim
+ * success. That's lying to usbcore, and it matters for for runtime
+ * PM scenarios with selective suspend and remote wakeup...
+ */
+ if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev))
+ ehci_warn(ehci, "selective suspend/wakeup unavailable\n");
+#endif
+
retval = ehci_pci_reinit(ehci, pdev);
done:
return retval;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 9b13bf2fa98..e469221e7ec 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -702,7 +702,7 @@ qh_make (
}
/* support for tt scheduling, and access to toggles */
- qh->dev = usb_get_dev (urb->dev);
+ qh->dev = urb->dev;
/* using TT? */
switch (urb->dev->speed) {
@@ -721,7 +721,14 @@ qh_make (
info1 |= maxp << 16;
info2 |= (EHCI_TUNE_MULT_TT << 30);
- info2 |= urb->dev->ttport << 23;
+
+ /* Some Freescale processors have an erratum in which the
+ * port number in the queue head was 0..N-1 instead of 1..N.
+ */
+ if (ehci_has_fsl_portno_bug(ehci))
+ info2 |= (urb->dev->ttport-1) << 23;
+ else
+ info2 |= urb->dev->ttport << 23;
/* set the address of the TT; for TDI's integrated
* root hub tt, leave it zeroed.
@@ -1015,12 +1022,14 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* stop async schedule right now? */
if (unlikely (qh == ehci->async)) {
/* can't get here without STS_ASS set */
- if (ehci_to_hcd(ehci)->state != HC_STATE_HALT) {
+ if (ehci_to_hcd(ehci)->state != HC_STATE_HALT
+ && !ehci->reclaim) {
+ /* ... and CMD_IAAD clear */
writel (cmd & ~CMD_ASE, &ehci->regs->command);
wmb ();
// handshake later, if we need to
+ timer_action_done (ehci, TIMER_ASYNC_OFF);