Merge tag 'usb-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 Jul 2012 17:23:47 +0000 (10:23 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 Jul 2012 17:23:47 +0000 (10:23 -0700)
Pull USB patches from Greg Kroah-Hartman:
 "Here's the big USB patch set for the 3.6-rc1 merge window.

  Lots of little changes in here, primarily for gadget controllers and
  drivers.  There's some scsi changes that I think also went in through
  the scsi tree, but they merge just fine.  All of these patches have
  been in the linux-next tree for a while now.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
Fix up trivial conflicts in include/scsi/scsi_device.h (same libata
conflict that Jeff had already encountered)

* tag 'usb-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (207 commits)
  usb: Add USB_QUIRK_RESET_RESUME for all Logitech UVC webcams
  usb: Add quirk detection based on interface information
  usb: s3c-hsotg: Add header file protection macros in s3c-hsotg.h
  USB: ehci-s5p: Add vbus setup function to the s5p ehci glue layer
  USB: add USB_VENDOR_AND_INTERFACE_INFO() macro
  USB: notify phy when root hub port connect change
  USB: remove 8 bytes of padding from usb_host_interface on 64 bit builds
  USB: option: add ZTE MF821D
  USB: sierra: QMI mode MC7710 moved to qcserial
  USB: qcserial: adding Sierra Wireless devices
  USB: qcserial: support generic Qualcomm serial ports
  USB: qcserial: make probe more flexible
  USB: qcserial: centralize probe exit path
  USB: qcserial: consolidate usb_set_interface calls
  USB: ehci-s5p: Add support for device tree
  USB: ohci-exynos: Add support for device tree
  USB: ehci-omap: fix compile failure(v1)
  usb: host: tegra: pass correct pointer in ehci_setup()
  USB: ehci-fsl: Update ifdef check to work on 64-bit ppc
  USB: serial: keyspan: Removed unrequired parentheses.
  ...

159 files changed:
Documentation/ABI/testing/sysfs-bus-usb
Documentation/devicetree/bindings/usb/ci13xxx-imx.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/mxs-phy.txt [new file with mode: 0644]
Documentation/usb/mass-storage.txt [new file with mode: 0644]
arch/arm/mach-omap2/clock3xxx_data.c
drivers/power/ab8500_charger.c
drivers/power/isp1704_charger.c
drivers/power/pda_power.c
drivers/power/twl4030_charger.c
drivers/usb/chipidea/Kconfig
drivers/usb/chipidea/Makefile
drivers/usb/chipidea/ci.h
drivers/usb/chipidea/ci13xxx_imx.c [new file with mode: 0644]
drivers/usb/chipidea/ci13xxx_msm.c
drivers/usb/chipidea/ci13xxx_pci.c
drivers/usb/chipidea/core.c
drivers/usb/chipidea/debug.c
drivers/usb/chipidea/host.c
drivers/usb/chipidea/udc.c
drivers/usb/class/cdc-acm.c
drivers/usb/class/cdc-wdm.c
drivers/usb/core/devio.c
drivers/usb/core/driver.c
drivers/usb/core/file.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/quirks.c
drivers/usb/core/sysfs.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/dwc3-exynos.c
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/gadget.h
drivers/usb/early/ehci-dbgp.c
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/acm_ms.c
drivers/usb/gadget/at91_udc.c
drivers/usb/gadget/composite.c
drivers/usb/gadget/f_fs.c
drivers/usb/gadget/f_hid.c
drivers/usb/gadget/f_mass_storage.c
drivers/usb/gadget/f_uvc.c
drivers/usb/gadget/f_uvc.h
drivers/usb/gadget/fsl_mxc_udc.c
drivers/usb/gadget/fsl_udc_core.c
drivers/usb/gadget/imx_udc.c
drivers/usb/gadget/lpc32xx_udc.c
drivers/usb/gadget/mv_u3d.h [new file with mode: 0644]
drivers/usb/gadget/mv_u3d_core.c [new file with mode: 0644]
drivers/usb/gadget/mv_udc_core.c
drivers/usb/gadget/omap_udc.c
drivers/usb/gadget/pch_udc.c
drivers/usb/gadget/pxa25x_udc.c
drivers/usb/gadget/pxa27x_udc.c
drivers/usb/gadget/s3c-hsotg.c
drivers/usb/gadget/s3c-hsudc.c
drivers/usb/gadget/storage_common.c
drivers/usb/gadget/u_ether.c
drivers/usb/gadget/uvc.h
drivers/usb/gadget/webcam.c
drivers/usb/host/Kconfig
drivers/usb/host/ehci-atmel.c
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ehci-cns3xxx.c
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-grlib.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-ixp4xx.c
drivers/usb/host/ehci-mem.c
drivers/usb/host/ehci-msm.c
drivers/usb/host/ehci-mv.c
drivers/usb/host/ehci-mxc.c
drivers/usb/host/ehci-octeon.c
drivers/usb/host/ehci-omap.c
drivers/usb/host/ehci-orion.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-platform.c
drivers/usb/host/ehci-pmcmsp.c
drivers/usb/host/ehci-ppc-of.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-s5p.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci-sead3.c
drivers/usb/host/ehci-sh.c
drivers/usb/host/ehci-spear.c
drivers/usb/host/ehci-tegra.c
drivers/usb/host/ehci-timer.c [new file with mode: 0644]
drivers/usb/host/ehci-vt8500.c
drivers/usb/host/ehci-w90x900.c
drivers/usb/host/ehci-xilinx-of.c
drivers/usb/host/ehci-xls.c
drivers/usb/host/ehci.h
drivers/usb/host/fhci-dbg.c
drivers/usb/host/fhci-hcd.c
drivers/usb/host/fhci-hub.c
drivers/usb/host/fhci-sched.c
drivers/usb/host/fhci-tds.c
drivers/usb/host/fhci.h
drivers/usb/host/imx21-hcd.c
drivers/usb/host/ohci-exynos.c
drivers/usb/host/ohci-nxp.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci.h
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci.c
drivers/usb/musb/am35x.c
drivers/usb/musb/blackfin.c
drivers/usb/musb/da8xx.c
drivers/usb/musb/davinci.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h
drivers/usb/musb/musb_dsps.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/omap2430.c
drivers/usb/musb/tusb6010.c
drivers/usb/musb/ux500.c
drivers/usb/otg/Kconfig
drivers/usb/otg/Makefile
drivers/usb/otg/ab8500-usb.c
drivers/usb/otg/fsl_otg.c
drivers/usb/otg/gpio_vbus.c
drivers/usb/otg/isp1301_omap.c
drivers/usb/otg/msm_otg.c
drivers/usb/otg/mv_otg.c
drivers/usb/otg/mxs-phy.c [new file with mode: 0644]
drivers/usb/otg/nop-usb-xceiv.c
drivers/usb/otg/otg.c
drivers/usb/otg/twl4030-usb.c
drivers/usb/otg/twl6030-usb.c
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/serial/keyspan.c
drivers/usb/serial/option.c
drivers/usb/serial/qcserial.c
drivers/usb/serial/quatech2.c
drivers/usb/serial/sierra.c
drivers/usb/storage/protocol.c
drivers/usb/storage/uas.c
include/linux/mod_devicetable.h
include/linux/platform_data/s3c-hsotg.h
include/linux/usb.h
include/linux/usb/chipidea.h
include/linux/usb/ehci_def.h
include/linux/usb/hcd.h
include/linux/usb/musb-omap.h [new file with mode: 0644]
include/linux/usb/otg.h
include/linux/usb/renesas_usbhs.h
include/linux/usb/uas.h
include/linux/usbdevice_fs.h
scripts/mod/file2alias.c
tools/usb/testusb.c

index 6df4e6f..5f75f8f 100644 (file)
@@ -208,3 +208,15 @@ Description:
                such as ACPI. This file will read either "removable" or
                "fixed" if the information is available, and "unknown"
                otherwise.
+
+What:          /sys/bus/usb/devices/.../ltm_capable
+Date:          July 2012
+Contact:       Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Description:
+               USB 3.0 devices may optionally support Latency Tolerance
+               Messaging (LTM).  They indicate their support by setting a bit
+               in the bmAttributes field of their SuperSpeed BOS descriptors.
+               If that bit is set for the device, ltm_capable will read "yes".
+               If the device doesn't support LTM, the file will read "no".
+               The file will be present for all speeds of USB devices, and will
+               always read "no" for USB 1.1 and USB 2.0 devices.
diff --git a/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt
new file mode 100644 (file)
index 0000000..2c29041
--- /dev/null
@@ -0,0 +1,18 @@
+* Freescale i.MX ci13xxx usb controllers
+
+Required properties:
+- compatible: Should be "fsl,imx27-usb"
+- reg: Should contain registers location and length
+- interrupts: Should contain controller interrupt
+
+Optional properties:
+- fsl,usbphy: phandler of usb phy that connects to the only one port
+- vbus-supply: regulator for vbus
+
+Examples:
+usb@02184000 { /* USB OTG */
+       compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+       reg = <0x02184000 0x200>;
+       interrupts = <0 43 0x04>;
+       fsl,usbphy = <&usbphy1>;
+};
diff --git a/Documentation/devicetree/bindings/usb/mxs-phy.txt b/Documentation/devicetree/bindings/usb/mxs-phy.txt
new file mode 100644 (file)
index 0000000..5835b27
--- /dev/null
@@ -0,0 +1,13 @@
+* Freescale MXS USB Phy Device
+
+Required properties:
+- compatible: Should be "fsl,imx23-usbphy"
+- reg: Should contain registers location and length
+- interrupts: Should contain phy interrupt
+
+Example:
+usbphy1: usbphy@020c9000 {
+       compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
+       reg = <0x020c9000 0x1000>;
+       interrupts = <0 44 0x04>;
+};
diff --git a/Documentation/usb/mass-storage.txt b/Documentation/usb/mass-storage.txt
new file mode 100644 (file)
index 0000000..e9b9334
--- /dev/null
@@ -0,0 +1,226 @@
+* Overview
+
+  Mass Storage Gadget (or MSG) acts as a USB Mass Storage device,
+  appearing to the host as a disk or a CD-ROM drive.  It supports
+  multiple logical units (LUNs).  Backing storage for each LUN is
+  provided by a regular file or a block device, access can be limited
+  to read-only, and gadget can indicate that it is removable and/or
+  CD-ROM (the latter implies read-only access).
+
+  Its requirements are modest; only a bulk-in and a bulk-out endpoint
+  are needed.  The memory requirement amounts to two 16K buffers.
+  Support is included for full-speed, high-speed and SuperSpeed
+  operation.
+
+  Note that the driver is slightly non-portable in that it assumes
+  a single memory/DMA buffer will be useable for bulk-in and bulk-out
+  endpoints.  With most device controllers this is not an issue, but
+  there may be some with hardware restrictions that prevent a buffer
+  from being used by more than one endpoint.
+
+  This document describes how to use the gadget from user space, its
+  relation to mass storage function (or MSF) and different gadgets
+  using it, and how it differs from File Storage Gadget (or FSG).  It
+  will talk only briefly about how to use MSF within composite
+  gadgets.
+
+* Module parameters
+
+  The mass storage gadget accepts the following mass storage specific
+  module parameters:
+
+  - file=filename[,filename...]
+
+    This parameter lists paths to files or block devices used for
+    backing storage for each logical unit.  There may be at most
+    FSG_MAX_LUNS (8) LUNs set.  If more files are specified, they will
+    be silently ignored.  See also “luns” parameter.
+
+    *BEWARE* that if a file is used as a backing storage, it may not
+    be modified by any other process.  This is because the host
+    assumes the data does not change without its knowledge.  It may be
+    read, but (if the logical unit is writable) due to buffering on
+    the host side, the contents are not well defined.
+
+    The size of the logical unit will be rounded down to a full
+    logical block.  The logical block size is 2048 bytes for LUNs
+    simulating CD-ROM, block size of the device if the backing file is
+    a block device, or 512 bytes otherwise.
+
+  - removable=b[,b...]
+
+    This parameter specifies whether each logical unit should be
+    removable.  “b” here is either “y”, “Y” or “1” for true or “n”,
+    “N” or “0” for false.
+
+    If this option is set for a logical unit, gadget will accept an
+    “eject” SCSI request (Start/Stop Unit).  When it is sent, the
+    backing file will be closed to simulate ejection and the logical
+    unit will not be mountable by the host until a new backing file is
+    specified by userspace on the device (see “sysfs entries”
+    section).
+
+    If a logical unit is not removable (the default), a backing file
+    must be specified for it with the “file” parameter as the module
+    is loaded.  The same applies if the module is built in, no
+    exceptions.
+
+    The default value of the flag is false, *HOWEVER* it used to be
+    true.  This has been changed to better match File Storage Gadget
+    and because it seems like a saner default after all.  Thus to
+    maintain compatibility with older kernels, it's best to specify
+    the default values.  Also, if one relied on old default, explicit
+    “n” needs to be specified now.
+
+    Note that “removable” means the logical unit's media can be
+    ejected or removed (as is true for a CD-ROM drive or a card
+    reader).  It does *not* mean that the entire gadget can be
+    unplugged from the host; the proper term for that is
+    “hot-unpluggable”.
+
+  - cdrom=b[,b...]
+
+    This parameter specifies whether each logical unit should simulate
+    CD-ROM.  The default is false.
+
+  - ro=b[,b...]
+
+    This parameter specifies whether each logical unit should be
+    reported as read only.  This will prevent host from modifying the
+    backing files.
+
+    Note that if this flag for given logical unit is false but the
+    backing file could not be opened in read/write mode, the gadget
+    will fall back to read only mode anyway.
+
+    The default value for non-CD-ROM logical units is false; for
+    logical units simulating CD-ROM it is forced to true.
+
+  - nofua=b[,b...]
+
+    This parameter specifies whether FUA flag should be ignored in SCSI
+    Write10 and Write12 commands sent to given logical units.
+
+    MS Windows mounts removable storage in “Removal optimised mode” by
+    default.  All the writes to the media are synchronous, which is
+    achieved by setting the FUA (Force Unit Access) bit in SCSI
+    Write(10,12) commands.  This forces each write to wait until the
+    data has actually been written out and prevents I/O requests
+    aggregation in block layer dramatically decreasing performance.
+
+    Note that this may mean that if the device is powered from USB and
+    the user unplugs the device without unmounting it first (which at
+    least some Windows users do), the data may be lost.
+
+    The default value is false.
+
+  - luns=N
+
+    This parameter specifies number of logical units the gadget will
+    have.  It is limited by FSG_MAX_LUNS (8) and higher value will be
+    capped.
+
+    If this parameter is provided, and the number of files specified
+    in “file” argument is greater then the value of “luns”, all excess
+    files will be ignored.
+
+    If this parameter is not present, the number of logical units will
+    be deduced from the number of files specified in the “file”
+    parameter.  If the file parameter is missing as well, one is
+    assumed.
+
+  - stall=b
+
+    Specifies whether the gadget is allowed to halt bulk endpoints.
+    The default is determined according to the type of USB device
+    controller, but usually true.
+
+  In addition to the above, the gadget also accepts the following
+  parameters defined by the composite framework (they are common to
+  all composite gadgets so just a quick listing):
+
+  - idVendor      -- USB Vendor ID (16 bit integer)
+  - idProduct     -- USB Product ID (16 bit integer)
+  - bcdDevice     -- USB Device version (BCD) (16 bit integer)
+  - iManufacturer -- USB Manufacturer string (string)
+  - iProduct      -- USB Product string (string)
+  - iSerialNumber -- SerialNumber string (sting)
+
+* sysfs entries
+
+  For each logical unit, the gadget creates a directory in the sysfs
+  hierarchy.  Inside of it the following three files are created:
+
+  - file
+
+    When read it returns the path to the backing file for the given
+    logical unit.  If there is no backing file (possible only if the
+    logical unit is removable), the content is empty.
+
+    When written into, it changes the backing file for given logical
+    unit.  This change can be performed even if given logical unit is
+    not specified as removable (but that may look strange to the
+    host).  It may fail, however, if host disallowed medium removal
+    with the Prevent-Allow Medium Removal SCSI command.
+
+  - ro
+
+    Reflects the state of ro flag for the given logical unit.  It can
+    be read any time, and written to when there is no backing file
+    open for given logical unit.
+
+  - nofua
+
+    Reflects the state of nofua flag for given logical unit.  It can
+    be read and written.
+
+  Other then those, as usual, the values of module parameters can be
+  read from /sys/module/g_mass_storage/parameters/* files.
+
+* Other gadgets using mass storage function
+
+  The Mass Storage Gadget uses the Mass Storage Function to handle
+  mass storage protocol.  As a composite function, MSF may be used by
+  other gadgets as well (eg. g_multi and acm_ms).
+
+  All of the information in previous sections are valid for other
+  gadgets using MSF, except that support for mass storage related
+  module parameters may be missing, or the parameters may have
+  a prefix.  To figure out whether any of this is true one needs to
+  consult the gadget's documentation or its source code.
+
+  For examples of how to include mass storage function in gadgets, one
+  may take a look at mass_storage.c, acm_ms.c and multi.c (sorted by
+  complexity).
+
+* Relation to file storage gadget
+
+  The Mass Storage Function and thus the Mass Storage Gadget has been
+  based on the File Storage Gadget.  The difference between the two is
+  that MSG is a composite gadget (ie. uses the composite framework)
+  while file storage gadget is a traditional gadget.  From userspace
+  point of view this distinction does not really matter, but from
+  kernel hacker's point of view, this means that (i) MSG does not
+  duplicate code needed for handling basic USB protocol commands and
+  (ii) MSF can be used in any other composite gadget.
+
+  Because of that, File Storage Gadget has been deprecated and
+  scheduled to be removed in Linux 3.8.  All users need to transition
+  to the Mass Storage Gadget by that time.  The two gadgets behave
+  mostly the same from the outside except:
+
+  1. In FSG the “removable” and “cdrom” module parameters set the flag
+     for all logical units whereas in MSG they accept a list of y/n
+     values for each logical unit.  If one uses only a single logical
+     unit this does not matter, but if there are more, the y/n value
+     needs to be repeated for each logical unit.
+
+  2. FSG's “serial”, “vendor”, “product” and “release” module
+     parameters are handled in MSG by the composite layer's parameters
+     named respectively: “iSerialnumber”, “idVendor”, “idProduct” and
+     “bcdDevice”.
+
+  3. MSG does not support FSG's test mode, thus “transport”,
+     “protocol” and “buflen” FSG's module parameters are not
+     supported.  MSG always uses SCSI protocol with bulk only
+     transport mode and 16 KiB buffers.
index 91b3d5c..83bed9a 100644 (file)
@@ -3376,15 +3376,15 @@ static struct omap_clk omap3xxx_clks[] = {
        CLK(NULL,       "usbhost_48m_fck", &usbhost_48m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
        CLK(NULL,       "usbhost_ick",  &usbhost_ick,   CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
        CLK("usbhs_omap",       "usbhost_ick",  &usbhost_ick,   CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
-       CLK("usbhs_omap",       "utmi_p1_gfclk",        &dummy_ck,      CK_3XXX),
-       CLK("usbhs_omap",       "utmi_p2_gfclk",        &dummy_ck,      CK_3XXX),
-       CLK("usbhs_omap",       "xclk60mhsp1_ck",       &dummy_ck,      CK_3XXX),
-       CLK("usbhs_omap",       "xclk60mhsp2_ck",       &dummy_ck,      CK_3XXX),
-       CLK("usbhs_omap",       "usb_host_hs_utmi_p1_clk",      &dummy_ck,      CK_3XXX),
-       CLK("usbhs_omap",       "usb_host_hs_utmi_p2_clk",      &dummy_ck,      CK_3XXX),
+       CLK(NULL,       "utmi_p1_gfclk",        &dummy_ck,      CK_3XXX),
+       CLK(NULL,       "utmi_p2_gfclk",        &dummy_ck,      CK_3XXX),
+       CLK(NULL,       "xclk60mhsp1_ck",       &dummy_ck,      CK_3XXX),
+       CLK(NULL,       "xclk60mhsp2_ck",       &dummy_ck,      CK_3XXX),
+       CLK(NULL,       "usb_host_hs_utmi_p1_clk",      &dummy_ck,      CK_3XXX),
+       CLK(NULL,       "usb_host_hs_utmi_p2_clk",      &dummy_ck,      CK_3XXX),
        CLK("usbhs_omap",       "usb_tll_hs_usb_ch0_clk",       &dummy_ck,      CK_3XXX),
        CLK("usbhs_omap",       "usb_tll_hs_usb_ch1_clk",       &dummy_ck,      CK_3XXX),
-       CLK("usbhs_omap",       "init_60m_fclk",        &dummy_ck,      CK_3XXX),
+       CLK(NULL,       "init_60m_fclk",        &dummy_ck,      CK_3XXX),
        CLK(NULL,       "usim_fck",     &usim_fck,      CK_3430ES2PLUS | CK_36XX),
        CLK(NULL,       "gpt1_fck",     &gpt1_fck,      CK_3XXX),
        CLK(NULL,       "wkup_32k_fck", &wkup_32k_fck,  CK_3XXX),
index d2303d0..d4f0c98 100644 (file)
@@ -2517,7 +2517,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
                dev_err(di->dev, "%s mask and set failed\n", __func__);
 
        usb_unregister_notifier(di->usb_phy, &di->nb);
-       usb_put_transceiver(di->usb_phy);
+       usb_put_phy(di->usb_phy);
 
        /* Delete the work queue */
        destroy_workqueue(di->charger_wq);
@@ -2688,8 +2688,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
                goto free_ac;
        }
 
-       di->usb_phy = usb_get_transceiver();
-       if (!di->usb_phy) {
+       di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(di->usb_phy)) {
                dev_err(di->dev, "failed to get usb transceiver\n");
                ret = -EINVAL;
                goto free_usb;
@@ -2747,7 +2747,7 @@ free_irq:
                free_irq(irq, di);
        }
 put_usb_phy:
-       usb_put_transceiver(di->usb_phy);
+       usb_put_phy(di->usb_phy);
 free_usb:
        power_supply_unregister(&di->usb_chg.psy);
 free_ac:
index e5ccd29..1229119 100644 (file)
@@ -415,8 +415,8 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
        if (!isp)
                return -ENOMEM;
 
-       isp->phy = usb_get_transceiver();
-       if (!isp->phy)
+       isp->phy = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(isp->phy))
                goto fail0;
 
        isp->dev = &pdev->dev;
@@ -475,7 +475,7 @@ fail2:
        power_supply_unregister(&isp->psy);
 fail1:
        isp1704_charger_set_power(isp, 0);
-       usb_put_transceiver(isp->phy);
+       usb_put_phy(isp->phy);
 fail0:
        kfree(isp);
 
@@ -490,7 +490,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev)
 
        usb_unregister_notifier(isp->phy, &isp->nb);
        power_supply_unregister(&isp->psy);
-       usb_put_transceiver(isp->phy);
+       usb_put_phy(isp->phy);
        isp1704_charger_set_power(isp, 0);
        kfree(isp);
 
index 214468f..8dbcd53 100644 (file)
@@ -321,12 +321,12 @@ static int pda_power_probe(struct platform_device *pdev)
        }
 
 #ifdef CONFIG_USB_OTG_UTILS
-       transceiver = usb_get_transceiver();
-       if (transceiver && !pdata->is_usb_online) {
-               pdata->is_usb_online = otg_is_usb_online;
-       }
-       if (transceiver && !pdata->is_ac_online) {
-               pdata->is_ac_online = otg_is_ac_online;
+       transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (!IS_ERR_OR_NULL(transceiver)) {
+               if (!pdata->is_usb_online)
+                       pdata->is_usb_online = otg_is_usb_online;
+               if (!pdata->is_ac_online)
+                       pdata->is_ac_online = otg_is_ac_online;
        }
 #endif
 
@@ -373,7 +373,7 @@ static int pda_power_probe(struct platform_device *pdev)
        }
 
 #ifdef CONFIG_USB_OTG_UTILS
-       if (transceiver && pdata->use_otg_notifier) {
+       if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) {
                otg_nb.notifier_call = otg_handle_notification;
                ret = usb_register_notifier(transceiver, &otg_nb);
                if (ret) {
@@ -408,8 +408,8 @@ usb_supply_failed:
        if (pdata->is_ac_online && ac_irq)
                free_irq(ac_irq->start, &pda_psy_ac);
 #ifdef CONFIG_USB_OTG_UTILS
-       if (transceiver)
-               usb_put_transceiver(transceiver);
+       if (!IS_ERR_OR_NULL(transceiver))
+               usb_put_phy(transceiver);
 #endif
 ac_irq_failed:
        if (pdata->is_ac_online)
@@ -443,8 +443,8 @@ static int pda_power_remove(struct platform_device *pdev)
        if (pdata->is_ac_online)
                power_supply_unregister(&pda_psy_ac);
 #ifdef CONFIG_USB_OTG_UTILS
-       if (transceiver)
-               usb_put_transceiver(transceiver);
+       if (!IS_ERR_OR_NULL(transceiver))
+               usb_put_phy(transceiver);
 #endif
        if (ac_draw) {
                regulator_put(ac_draw);
index fdad850..7cacbaa 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/i2c/twl.h>
@@ -479,8 +480,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
 
        INIT_WORK(&bci->work, twl4030_bci_usb_work);
 
-       bci->transceiver = usb_get_transceiver();
-       if (bci->transceiver != NULL) {
+       bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (!IS_ERR_OR_NULL(bci->transceiver)) {
                bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
                usb_register_notifier(bci->transceiver, &bci->usb_nb);
        }
@@ -507,9 +508,9 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
        return 0;
 
 fail_unmask_interrupts:
-       if (bci->transceiver != NULL) {
+       if (!IS_ERR_OR_NULL(bci->transceiver)) {
                usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
-               usb_put_transceiver(bci->transceiver);
+               usb_put_phy(bci->transceiver);
        }
        free_irq(bci->irq_bci, bci);
 fail_bci_irq:
@@ -538,9 +539,9 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
        twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
                         TWL4030_INTERRUPTS_BCIIMR2A);
 
-       if (bci->transceiver != NULL) {
+       if (!IS_ERR_OR_NULL(bci->transceiver)) {
                usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
-               usb_put_transceiver(bci->transceiver);
+               usb_put_phy(bci->transceiver);
        }
        free_irq(bci->irq_bci, bci);
        free_irq(bci->irq_chg, bci);
index fd36dc8..8337fb5 100644 (file)
@@ -20,6 +20,7 @@ config USB_CHIPIDEA_UDC
 
 config USB_CHIPIDEA_HOST
        bool "ChipIdea host controller"
+       select USB_EHCI_ROOT_HUB_TT
        help
          Say Y here to enable host controller functionality of the
          ChipIdea driver.
index cc34937..5c66d9c 100644 (file)
@@ -5,10 +5,15 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC)    += udc.o
 ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST)    += host.o
 ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG)   += debug.o
 
+# Glue/Bridge layers go here
+
+obj-$(CONFIG_USB_CHIPIDEA)     += ci13xxx_msm.o
+
+# PCI doesn't provide stubs, need to check
 ifneq ($(CONFIG_PCI),)
        obj-$(CONFIG_USB_CHIPIDEA)      += ci13xxx_pci.o
 endif
 
-ifneq ($(CONFIG_ARCH_MSM),)
-       obj-$(CONFIG_USB_CHIPIDEA)      += ci13xxx_msm.o
+ifneq ($(CONFIG_OF_DEVICE),)
+       obj-$(CONFIG_USB_CHIPIDEA)      += ci13xxx_imx.o
 endif
index 50911f8..d738603 100644 (file)
@@ -36,7 +36,7 @@
  * @name: string description of the endpoint
  * @qh: queue head for this endpoint
  * @wedge: is the endpoint wedged
- * @udc: pointer to the controller
+ * @ci: pointer to the controller
  * @lock: pointer to controller's spinlock
  * @td_pool: pointer to controller's TD pool
  */
@@ -54,7 +54,7 @@ struct ci13xxx_ep {
        int                                     wedge;
 
        /* global resources */
-       struct ci13xxx                          *udc;
+       struct ci13xxx                          *ci;
        spinlock_t                              *lock;
        struct dma_pool                         *td_pool;
 };
@@ -125,7 +125,7 @@ struct hw_bank {
  * @remote_wakeup: host-enabled remote wakeup
  * @suspended: suspended by host
  * @test_mode: the selected test mode
- * @udc_driver: platform specific information supplied by parent device
+ * @platdata: platform specific information supplied by parent device
  * @vbus_active: is VBUS active
  * @transceiver: pointer to USB PHY, if any
  * @hcd: pointer to usb_hcd for ehci host driver
@@ -158,8 +158,10 @@ struct ci13xxx {
        u8                              suspended;
        u8                              test_mode;
 
-       struct ci13xxx_udc_driver       *udc_driver;
+       struct ci13xxx_platform_data    *platdata;
        int                             vbus_active;
+       /* FIXME: some day, we'll not use global phy */
+       bool                            global_phy;
        struct usb_phy                  *transceiver;
        struct usb_hcd                  *hcd;
 };
@@ -250,9 +252,9 @@ static inline int ffs_nr(u32 x)
  *
  * This function returns register contents
  */
-static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask)
+static inline u32 hw_read(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask)
 {
-       return ioread32(udc->hw_bank.regmap[reg]) & mask;
+       return ioread32(ci->hw_bank.regmap[reg]) & mask;
 }
 
 /**
@@ -261,14 +263,14 @@ static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask)
  * @mask: bitfield mask
  * @data: new value
  */
-static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg,
+static inline void hw_write(struct ci13xxx *ci, enum ci13xxx_regs reg,
                            u32 mask, u32 data)
 {
        if (~mask)
-               data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask)
+               data = (ioread32(ci->hw_bank.regmap[reg]) & ~mask)
                        | (data & mask);
 
-       iowrite32(data, udc->hw_bank.regmap[reg]);
+       iowrite32(data, ci->hw_bank.regmap[reg]);
 }
 
 /**
@@ -278,12 +280,12 @@ static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg,
  *
  * This function returns register contents
  */
-static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg,
+static inline u32 hw_test_and_clear(struct ci13xxx *ci, enum ci13xxx_regs reg,
                                    u32 mask)
 {
-       u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask;
+       u32 val = ioread32(ci->hw_bank.regmap[reg]) & mask;
 
-       iowrite32(val, udc->hw_bank.regmap[reg]);
+       iowrite32(val, ci->hw_bank.regmap[reg]);
        return val;
 }
 
@@ -295,12 +297,12 @@ static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg,
  *
  * This function returns register contents
  */
-static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg,
+static inline u32 hw_test_and_write(struct ci13xxx *ci, enum ci13xxx_regs reg,
                                    u32 mask, u32 data)
 {
-       u32 val = hw_read(udc, reg, ~0);
+       u32 val = hw_read(ci, reg, ~0);
 
-       hw_write(udc, reg, mask, data);
+       hw_write(ci, reg, mask, data);
        return (val & mask) >> ffs_nr(mask);
 }
 
diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c
new file mode 100644 (file)
index 0000000..ef60d06
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright (C) 2012 Marek Vasut <marex@denx.de>
+ * on behalf of DENX Software Engineering GmbH
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/chipidea.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include "ci.h"
+
+#define pdev_to_phy(pdev) \
+       ((struct usb_phy *)platform_get_drvdata(pdev))
+
+struct ci13xxx_imx_data {
+       struct device_node *phy_np;
+       struct usb_phy *phy;
+       struct platform_device *ci_pdev;
+       struct clk *clk;
+       struct regulator *reg_vbus;
+};
+
+static struct ci13xxx_platform_data ci13xxx_imx_platdata __devinitdata  = {
+       .name                   = "ci13xxx_imx",
+       .flags                  = CI13XXX_REQUIRE_TRANSCEIVER |
+                                 CI13XXX_PULLUP_ON_VBUS |
+                                 CI13XXX_DISABLE_STREAMING,
+       .capoffset              = DEF_CAPOFFSET,
+};
+
+static int __devinit ci13xxx_imx_probe(struct platform_device *pdev)
+{
+       struct ci13xxx_imx_data *data;
+       struct platform_device *plat_ci, *phy_pdev;
+       struct device_node *phy_np;
+       struct resource *res;
+       struct regulator *reg_vbus;
+       int ret;
+
+       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               dev_err(&pdev->dev, "Failed to allocate CI13xxx-IMX data!\n");
+               return -ENOMEM;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "Can't get device resources!\n");
+               return -ENOENT;
+       }
+
+       data->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(data->clk)) {
+               dev_err(&pdev->dev,
+                       "Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
+               return PTR_ERR(data->clk);
+       }
+
+       ret = clk_prepare_enable(data->clk);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Failed to prepare or enable clock, err=%d\n", ret);
+               return ret;
+       }
+
+       phy_np = of_parse_phandle(pdev->dev.of_node, "fsl,usbphy", 0);
+       if (phy_np) {
+               data->phy_np = phy_np;
+               phy_pdev = of_find_device_by_node(phy_np);
+               if (phy_pdev) {
+                       struct usb_phy *phy;
+                       phy = pdev_to_phy(phy_pdev);
+                       if (phy &&
+                           try_module_get(phy_pdev->dev.driver->owner)) {
+                               usb_phy_init(phy);
+                               data->phy = phy;
+                       }
+               }
+       }
+
+       /* we only support host now, so enable vbus here */
+       reg_vbus = devm_regulator_get(&pdev->dev, "vbus");
+       if (!IS_ERR(reg_vbus)) {
+               ret = regulator_enable(reg_vbus);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "Failed to enable vbus regulator, err=%d\n",
+                               ret);
+                       goto put_np;
+               }
+               data->reg_vbus = reg_vbus;
+       } else {
+               reg_vbus = NULL;
+       }
+
+       ci13xxx_imx_platdata.phy = data->phy;
+
+       if (!pdev->dev.dma_mask) {
+               pdev->dev.dma_mask = devm_kzalloc(&pdev->dev,
+                                     sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
+               if (!pdev->dev.dma_mask) {
+                       ret = -ENOMEM;
+                       dev_err(&pdev->dev, "Failed to alloc dma_mask!\n");
+                       goto err;
+               }
+               *pdev->dev.dma_mask = DMA_BIT_MASK(32);
+               dma_set_coherent_mask(&pdev->dev, *pdev->dev.dma_mask);
+       }
+       plat_ci = ci13xxx_add_device(&pdev->dev,
+                               pdev->resource, pdev->num_resources,
+                               &ci13xxx_imx_platdata);
+       if (IS_ERR(plat_ci)) {
+               ret = PTR_ERR(plat_ci);
+               dev_err(&pdev->dev,
+                       "Can't register ci_hdrc platform device, err=%d\n",
+                       ret);
+               goto err;
+       }
+
+       data->ci_pdev = plat_ci;
+       platform_set_drvdata(pdev, data);
+
+       pm_runtime_no_callbacks(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+
+       return 0;
+
+err:
+       if (reg_vbus)
+               regulator_disable(reg_vbus);
+put_np:
+       if (phy_np)
+               of_node_put(phy_np);
+       clk_disable_unprepare(data->clk);
+       return ret;
+}
+
+static int __devexit ci13xxx_imx_remove(struct platform_device *pdev)
+{
+       struct ci13xxx_imx_data *data = platform_get_drvdata(pdev);
+
+       pm_runtime_disable(&pdev->dev);
+       ci13xxx_remove_device(data->ci_pdev);
+
+       if (data->reg_vbus)
+               regulator_disable(data->reg_vbus);
+
+       if (data->phy) {
+               usb_phy_shutdown(data->phy);
+               module_put(data->phy->dev->driver->owner);
+       }
+
+       of_node_put(data->phy_np);
+
+       clk_disable_unprepare(data->clk);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static const struct of_device_id ci13xxx_imx_dt_ids[] = {
+       { .compatible = "fsl,imx27-usb", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ci13xxx_imx_dt_ids);
+
+static struct platform_driver ci13xxx_imx_driver = {
+       .probe = ci13xxx_imx_probe,
+       .remove = __devexit_p(ci13xxx_imx_remove),
+       .driver = {
+               .name = "imx_usb",
+               .owner = THIS_MODULE,
+               .of_match_table = ci13xxx_imx_dt_ids,
+        },
+};
+
+module_platform_driver(ci13xxx_imx_driver);
+
+MODULE_ALIAS("platform:imx-usb");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CI13xxx i.MX USB binding");
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
index 958069e..b01feb3 100644 (file)
 
 #include "ci.h"
 
-#define MSM_USB_BASE   (udc->hw_bank.abs)
+#define MSM_USB_BASE   (ci->hw_bank.abs)
 
-static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
+static void ci13xxx_msm_notify_event(struct ci13xxx *ci, unsigned event)
 {
-       struct device *dev = udc->gadget.dev.parent;
+       struct device *dev = ci->gadget.dev.parent;
        int val;
 
        switch (event) {
@@ -34,18 +34,18 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
                 * Put the transceiver in non-driving mode. Otherwise host
                 * may not detect soft-disconnection.
                 */
-               val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL);
+               val = usb_phy_io_read(ci->transceiver, ULPI_FUNC_CTRL);
                val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
                val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
-               usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
+               usb_phy_io_write(ci->transceiver, val, ULPI_FUNC_CTRL);
                break;
        default:
-               dev_dbg(dev, "unknown ci13xxx_udc event\n");
+               dev_dbg(dev, "unknown ci13xxx event\n");
                break;
        }
 }
 
-static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
+static struct ci13xxx_platform_data ci13xxx_msm_platdata = {
        .name                   = "ci13xxx_msm",
        .flags                  = CI13XXX_REGS_SHARED |
                                  CI13XXX_REQUIRE_TRANSCEIVER |
@@ -55,56 +55,45 @@ static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
        .notify_event           = ci13xxx_msm_notify_event,
 };
 
-static int ci13xxx_msm_probe(struct platform_device *pdev)
+static int __devinit ci13xxx_msm_probe(struct platform_device *pdev)
 {
        struct platform_device *plat_ci;
-       int ret;
 
        dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
 
-       plat_ci = platform_device_alloc("ci_hdrc", -1);
-       if (!plat_ci) {
-               dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n");
-               return -ENOMEM;
+       plat_ci = ci13xxx_add_device(&pdev->dev,
+                               pdev->resource, pdev->num_resources,
+                               &ci13xxx_msm_platdata);
+       if (IS_ERR(plat_ci)) {
+               dev_err(&pdev->dev, "ci13xxx_add_device failed!\n");
+               return PTR_ERR(plat_ci);
        }
 
-       ret = platform_device_add_resources(plat_ci, pdev->resource,
-                                           pdev->num_resources);
-       if (ret) {
-               dev_err(&pdev->dev, "can't add resources to platform device\n");
-               goto put_platform;
-       }
-
-       ret = platform_device_add_data(plat_ci, &ci13xxx_msm_udc_driver,
-                                      sizeof(ci13xxx_msm_udc_driver));
-       if (ret)
-               goto put_platform;
-
-       ret = platform_device_add(plat_ci);
-       if (ret)
-               goto put_platform;
+       platform_set_drvdata(pdev, plat_ci);
 
        pm_runtime_no_callbacks(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
 
        return 0;
+}
+
+static int __devexit ci13xxx_msm_remove(struct platform_device *pdev)
+{
+       struct platform_device *plat_ci = platform_get_drvdata(pdev);
 
-put_platform:
-       platform_device_put(plat_ci);
+       pm_runtime_disable(&pdev->dev);
+       ci13xxx_remove_device(plat_ci);
 
-       return ret;
+       return 0;
 }
 
 static struct platform_driver ci13xxx_msm_driver = {
        .probe = ci13xxx_msm_probe,
+       .remove = __devexit_p(ci13xxx_msm_remove),
        .driver = { .name = "msm_hsusb", },
 };
-MODULE_ALIAS("platform:msm_hsusb");
 
-static int __init ci13xxx_msm_init(void)
-{
-       return platform_driver_register(&ci13xxx_msm_driver);
-}
-module_init(ci13xxx_msm_init);
+module_platform_driver(ci13xxx_msm_driver);
 
+MODULE_ALIAS("platform:msm_hsusb");
 MODULE_LICENSE("GPL v2");
index e3dab27..918e149 100644 (file)
 /******************************************************************************
  * PCI block
  *****************************************************************************/
-struct ci13xxx_udc_driver pci_driver = {
+struct ci13xxx_platform_data pci_platdata = {
        .name           = UDC_DRIVER_NAME,
        .capoffset      = DEF_CAPOFFSET,
 };
 
-struct ci13xxx_udc_driver langwell_pci_driver = {
+struct ci13xxx_platform_data langwell_pci_platdata = {
        .name           = UDC_DRIVER_NAME,
        .capoffset      = 0,
 };
 
-struct ci13xxx_udc_driver penwell_pci_driver = {
+struct ci13xxx_platform_data penwell_pci_platdata = {
        .name           = UDC_DRIVER_NAME,
        .capoffset      = 0,
        .power_budget   = 200,
@@ -51,12 +51,12 @@ struct ci13xxx_udc_driver penwell_pci_driver = {
 static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
                                       const struct pci_device_id *id)
 {
-       struct ci13xxx_udc_driver *driver = (void *)id->driver_data;
+       struct ci13xxx_platform_data *platdata = (void *)id->driver_data;
        struct platform_device *plat_ci;
        struct resource res[3];
        int retval = 0, nres = 2;
 
-       if (!driver) {
+       if (!platdata) {
                dev_err(&pdev->dev, "device doesn't provide driver data\n");
                return -ENODEV;
        }
@@ -75,13 +75,6 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
        pci_set_master(pdev);
        pci_try_set_mwi(pdev);
 
-       plat_ci = platform_device_alloc("ci_hdrc", -1);
-       if (!plat_ci) {
-               dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n");
-               retval = -ENOMEM;
-               goto disable_device;
-       }
-
        memset(res, 0, sizeof(res));
        res[0].start    = pci_resource_start(pdev, 0);
        res[0].end      = pci_resource_end(pdev, 0);
@@ -89,32 +82,17 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
        res[1].start    = pdev->irq;
        res[1].flags    = IORESOURCE_IRQ;
 
-       retval = platform_device_add_resources(plat_ci, res, nres);
-       if (retval) {
-               dev_err(&pdev->dev, "can't add resources to platform device\n");
-               goto put_platform;
+       plat_ci = ci13xxx_add_device(&pdev->dev, res, nres, platdata);
+       if (IS_ERR(plat_ci)) {
+               dev_err(&pdev->dev, "ci13xxx_add_device failed!\n");
+               retval = PTR_ERR(plat_ci);
+               goto disable_device;
        }
 
-       retval = platform_device_add_data(plat_ci, driver, sizeof(*driver));
-       if (retval)
-               goto put_platform;
-
-       dma_set_coherent_mask(&plat_ci->dev, pdev->dev.coherent_dma_mask);
-       plat_ci->dev.dma_mask = pdev->dev.dma_mask;
-       plat_ci->dev.dma_parms = pdev->dev.dma_parms;
-       plat_ci->dev.parent = &pdev->dev;
-
        pci_set_drvdata(pdev, plat_ci);
 
-       retval = platform_device_add(plat_ci);
-       if (retval)
-               goto put_platform;
-
        return 0;
 
- put_platform:
-       pci_set_drvdata(pdev, NULL);
-       platform_device_put(plat_ci);
  disable_device:
        pci_disable_device(pdev);
  done:
@@ -133,7 +111,7 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
 {
        struct platform_device *plat_ci = pci_get_drvdata(pdev);
 
-       platform_device_unregister(plat_ci);
+       ci13xxx_remove_device(plat_ci);
        pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
 }
@@ -147,19 +125,19 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
 static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
        {
                PCI_DEVICE(0x153F, 0x1004),
-               .driver_data = (kernel_ulong_t)&pci_driver,
+               .driver_data = (kernel_ulong_t)&pci_platdata,
        },
        {
                PCI_DEVICE(0x153F, 0x1006),
-               .driver_data = (kernel_ulong_t)&pci_driver,
+               .driver_data = (kernel_ulong_t)&pci_platdata,
        },
        {
                PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811),
-               .driver_data = (kernel_ulong_t)&langwell_pci_driver,
+               .driver_data = (kernel_ulong_t)&langwell_pci_platdata,
        },
        {
                PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829),
-               .driver_data = (kernel_ulong_t)&penwell_pci_driver,
+               .driver_data = (kernel_ulong_t)&penwell_pci_platdata,
        },
        { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
 };
index 15e03b3..1083585 100644 (file)
@@ -56,6 +56,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/module.h>
+#include <linux/idr.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/irq.h>
@@ -179,7 +180,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base)
        ci->hw_bank.abs = base;
 
        ci->hw_bank.cap = ci->hw_bank.abs;
-       ci->hw_bank.cap += ci->udc_driver->capoffset;
+       ci->hw_bank.cap += ci->platdata->capoffset;
        ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap);
 
        hw_alloc_regmap(ci, false);
@@ -227,11 +228,11 @@ int hw_device_reset(struct ci13xxx *ci, u32 mode)
                udelay(10);             /* not RTOS friendly */
 
 
-       if (ci->udc_driver->notify_event)
-               ci->udc_driver->notify_event(ci,
+       if (ci->platdata->notify_event)
+               ci->platdata->notify_event(ci,
                        CI13XXX_CONTROLLER_RESET_EVENT);
 
-       if (ci->udc_driver->flags & CI13XXX_DISABLE_STREAMING)
+       if (ci->platdata->flags & CI13XXX_DISABLE_STREAMING)
                hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
 
        /* USBMODE should be configured step by step */
@@ -332,6 +333,59 @@ static irqreturn_t ci_irq(int irq, void *data)
        return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci);
 }
 
+static DEFINE_IDA(ci_ida);
+
+struct platform_device *ci13xxx_add_device(struct device *dev,
+                       struct resource *res, int nres,
+                       struct ci13xxx_platform_data *platdata)
+{
+       struct platform_device *pdev;
+       int id, ret;
+
+       id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL);
+       if (id < 0)
+               return ERR_PTR(id);
+
+       pdev = platform_device_alloc("ci_hdrc", id);
+       if (!pdev) {
+               ret = -ENOMEM;
+               goto put_id;
+       }
+
+       pdev->dev.parent = dev;
+       pdev->dev.dma_mask = dev->dma_mask;
+       pdev->dev.dma_parms = dev->dma_parms;
+       dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask);
+
+       ret = platform_device_add_resources(pdev, res, nres);
+       if (ret)
+               goto err;
+
+       ret = platform_device_add_data(pdev, platdata, sizeof(*platdata));
+       if (ret)
+               goto err;
+
+       ret = platform_device_add(pdev);
+       if (ret)
+               goto err;
+
+       return pdev;
+
+err:
+       platform_device_put(pdev);
+put_id:
+       ida_simple_remove(&ci_ida, id);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ci13xxx_add_device);
+
+void ci13xxx_remove_device(struct platform_device *pdev)
+{
+       platform_device_unregister(pdev);
+       ida_simple_remove(&ci_ida, pdev->id);
+}
+EXPORT_SYMBOL_GPL(ci13xxx_remove_device);
+
 static int __devinit ci_hdrc_probe(struct platform_device *pdev)
 {
        struct device   *dev = &pdev->dev;
@@ -364,7 +418,11 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev)
        }
 
        ci->dev = dev;
-       ci->udc_driver = dev->platform_data;
+       ci->platdata = dev->platform_data;
+       if (ci->platdata->phy)
+               ci->transceiver = ci->platdata->phy;
+       else
+               ci->global_phy = true;
 
        ret = hw_device_init(ci, base);
        if (ret < 0) {
@@ -419,7 +477,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev)
        }
 
        platform_set_drvdata(pdev, ci);
-       ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->udc_driver->name,
+       ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name,
                          ci);
        if (ret)
                goto stop;
index c4b3e15..c6f50a2 100644 (file)
@@ -68,15 +68,15 @@ void dbg_interrupt(u32 intmask)
  *
  * This function returns number of registers read
  */
-static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size)
+static size_t hw_register_read(struct ci13xxx *ci, u32 *buf, size_t size)
 {
        unsigned i;
 
-       if (size > udc->hw_bank.size)
-               size = udc->hw_bank.size;
+       if (size > ci->hw_bank.size)
+               size = ci->hw_bank.size;
 
        for (i = 0; i < size; i++)
-               buf[i] = hw_read(udc, i * sizeof(u32), ~0);
+               buf[i] = hw_read(ci, i * sizeof(u32), ~0);
 
        return size;
 }
@@ -88,18 +88,18 @@ static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size)
  *
  * This function returns an error code
  */
-static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data)
+static int hw_register_write(struct ci13xxx *ci, u16 addr, u32 data)
 {
        /* align */
        addr /= sizeof(u32);
 
-       if (addr >= udc->hw_bank.size)
+       if (addr >= ci->hw_bank.size)
                return -EINVAL;
 
        /* align */
        addr *= sizeof(u32);
 
-       hw_write(udc, addr, ~0, data);
+       hw_write(ci, addr, ~0, data);
        return 0;
 }
 
@@ -110,13 +110,13 @@ static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data)
  *
  * This function returns an error code
  */
-static int hw_intr_clear(struct ci13xxx *udc, int n)
+static int hw_intr_clear(struct ci13xxx *ci, int n)
 {
        if (n >= REG_BITS)
                return -EINVAL;
 
-       hw_write(udc, OP_USBINTR, BIT(n), 0);
-       hw_write(udc, OP_USBSTS,  BIT(n), BIT(n));
+       hw_write(ci, OP_USBINTR, BIT(n), 0);
+       hw_write(ci, OP_USBSTS,  BIT(n), BIT(n));
        return 0;
 }
 
@@ -127,15 +127,15 @@ static int hw_intr_clear(struct ci13xxx *udc, int n)
  *
  * This function returns an error code
  */
-static int hw_intr_force(struct ci13xxx *udc, int n)
+static int hw_intr_force(struct ci13xxx *ci, int n)
 {
        if (n >= REG_BITS)
                return -EINVAL;
 
-       hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
-       hw_write(udc, OP_USBINTR,  BIT(n), BIT(n));
-       hw_write(udc, OP_USBSTS,   BIT(n), BIT(n));
-       hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0);
+       hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
+       hw_write(ci, OP_USBINTR,  BIT(n), BIT(n));
+       hw_write(ci, OP_USBSTS,   BIT(n), BIT(n));
+       hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, 0);
        return 0;
 }
 
@@ -147,12 +147,12 @@ static int hw_intr_force(struct ci13xxx *udc, int n)
 static ssize_t show_device(struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
-       struct usb_gadget *gadget = &udc->gadget;
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
+       struct usb_gadget *gadget = &ci->gadget;
        int n = 0;
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "[%s] EINVAL\n", __func__);
+               dev_err(ci->dev, "[%s] EINVAL\n", __func__);
                return 0;
        }
 
@@ -188,8 +188,8 @@ static DEVICE_ATTR(device, S_IRUSR, show_device, NULL);
 static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
-       struct usb_gadget_driver *driver = udc->driver;
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
+       struct usb_gadget_driver *driver = ci->driver;
        int n = 0;
 
        if (attr == NULL || buf == NULL) {
@@ -412,22 +412,22 @@ static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events);
 static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
        unsigned long flags;
        u32 intr;
        unsigned i, j, n = 0;
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "[%s] EINVAL\n", __func__);
+               dev_err(ci->dev, "[%s] EINVAL\n", __func__);
                return 0;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
 
        /*n += scnprintf(buf + n, PAGE_SIZE - n,
-                      "status = %08x\n", hw_read_intr_status(udc));
+                      "status = %08x\n", hw_read_intr_status(ci));
        n += scnprintf(buf + n, PAGE_SIZE - n,
-       "enable = %08x\n", hw_read_intr_enable(udc));*/
+       "enable = %08x\n", hw_read_intr_enable(ci));*/
 
        n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n",
                       isr_statistics.test);
@@ -471,7 +471,7 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
                        n += scnprintf(buf + n, PAGE_SIZE - n, "\n");
        }
 
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_unlock_irqrestore(&ci->lock, flags);
 
        return n;
 }
@@ -485,31 +485,31 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
 static ssize_t store_inters(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
        unsigned long flags;
        unsigned en, bit;
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "EINVAL\n");
+               dev_err(ci->dev, "EINVAL\n");
                goto done;
        }
 
        if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) {
-               dev_err(udc->dev, "<1|0> <bit>: enable|disable interrupt\n");
+               dev_err(ci->dev, "<1|0> <bit>: enable|disable interrupt\n");
                goto done;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
        if (en) {
-               if (hw_intr_force(udc, bit))
+               if (hw_intr_force(ci, bit))
                        dev_err(dev, "invalid bit number\n");
                else
                        isr_statistics.test++;
        } else {
-               if (hw_intr_clear(udc, bit))
+               if (hw_intr_clear(ci, bit))
                        dev_err(dev, "invalid bit number\n");
        }
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_unlock_irqrestore(&ci->lock, flags);
 
  done:
        return count;
@@ -524,18 +524,18 @@ static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters);
 static ssize_t show_port_test(struct device *dev,
                              struct device_attribute *attr, char *buf)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
        unsigned long flags;
        unsigned mode;
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "EINVAL\n");
+               dev_err(ci->dev, "EINVAL\n");
                return 0;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
-       mode = hw_port_test_get(udc);
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
+       mode = hw_port_test_get(ci);
+       spin_unlock_irqrestore(&ci->lock, flags);
 
        return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode);
 }
@@ -549,24 +549,24 @@ static ssize_t store_port_test(struct device *dev,
                               struct device_attribute *attr,
                               const char *buf, size_t count)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
        unsigned long flags;
        unsigned mode;
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "[%s] EINVAL\n", __func__);
+               dev_err(ci->dev, "[%s] EINVAL\n", __func__);
                goto done;
        }
 
        if (sscanf(buf, "%u", &mode) != 1) {
-               dev_err(udc->dev, "<mode>: set port test mode");
+               dev_err(ci->dev, "<mode>: set port test mode");
                goto done;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
-       if (hw_port_test_set(udc, mode))
-               dev_err(udc->dev, "invalid mode\n");
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
+       if (hw_port_test_set(ci, mode))
+               dev_err(ci->dev, "invalid mode\n");
+       spin_unlock_irqrestore(&ci->lock, flags);
 
  done:
        return count;
@@ -582,20 +582,20 @@ static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR,
 static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
        unsigned long flags;
        unsigned i, j, n = 0;
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "[%s] EINVAL\n", __func__);
+               dev_err(ci->dev, "[%s] EINVAL\n", __func__);
                return 0;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
-       for (i = 0; i < udc->hw_ep_max/2; i++) {
-               struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i];
+       spin_lock_irqsave(&ci->lock, flags);
+       for (i = 0; i < ci->hw_ep_max/2; i++) {
+               struct ci13xxx_ep *mEpRx = &ci->ci13xxx_ep[i];
                struct ci13xxx_ep *mEpTx =
-                       &udc->ci13xxx_ep[i + udc->hw_ep_max/2];
+                       &ci->ci13xxx_ep[i + ci->hw_ep_max/2];
                n += scnprintf(buf + n, PAGE_SIZE - n,
                               "EP=%02i: RX=%08X TX=%08X\n",
                               i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
@@ -606,7 +606,7 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
                                       *((u32 *)mEpTx->qh.ptr + j));
                }
        }
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_unlock_irqrestore(&ci->lock, flags);
 
        return n;
 }
@@ -621,25 +621,25 @@ static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL);
 static ssize_t show_registers(struct device *dev,
                              struct device_attribute *attr, char *buf)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
        unsigned long flags;
        u32 *dump;
        unsigned i, k, n = 0;
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "[%s] EINVAL\n", __func__);
+               dev_err(ci->dev, "[%s] EINVAL\n", __func__);
                return 0;
        }
 
        dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
        if (!dump) {
-               dev_err(udc->dev, "%s: out of memory\n", __func__);
+               dev_err(ci->dev, "%s: out of memory\n", __func__);
                return 0;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
-       k = hw_register_read(udc, dump, DUMP_ENTRIES);
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
+       k = hw_register_read(ci, dump, DUMP_ENTRIES);
+       spin_unlock_irqrestore(&ci->lock, flags);
 
        for (i = 0; i < k; i++) {
                n += scnprintf(buf + n, PAGE_SIZE - n,
@@ -660,24 +660,24 @@ static ssize_t store_registers(struct device *dev,
                               struct device_attribute *attr,
                               const char *buf, size_t count)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
        unsigned long addr, data, flags;
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "[%s] EINVAL\n", __func__);
+               dev_err(ci->dev, "[%s] EINVAL\n", __func__);
                goto done;
        }
 
        if (sscanf(buf, "%li %li", &addr, &data) != 2) {
-               dev_err(udc->dev,
+               dev_err(ci->dev,
                        "<addr> <data>: write data to register address\n");
                goto done;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
-       if (hw_register_write(udc, addr, data))
-               dev_err(udc->dev, "invalid address range\n");
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
+       if (hw_register_write(ci, addr, data))
+               dev_err(ci->dev, "invalid address range\n");
+       spin_unlock_irqrestore(&ci->lock, flags);
 
  done:
        return count;
@@ -693,34 +693,34 @@ static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR,
 static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
                             char *buf)
 {
-       struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+       struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
        unsigned long flags;
        struct list_head   *ptr = NULL;
        struct ci13xxx_req *req = NULL;
        unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
 
        if (attr == NULL || buf == NULL) {
-               dev_err(udc->dev, "[%s] EINVAL\n", __func__);
+               dev_err(ci->dev, "[%s] EINVAL\n", __func__);
                return 0;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
-       for (i = 0; i < udc->hw_ep_max; i++)
-               list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue)
+       spin_lock_irqsave(&ci->lock, flags);
+       for (i = 0; i < ci->hw_ep_max; i++)
+               list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue)
                {
                        req = list_entry(ptr, struct ci13xxx_req, queue);
 
                        n += scnprintf(buf + n, PAGE_SIZE - n,
                                        "EP=%02i: TD=%08X %s\n",
-                                       i % udc->hw_ep_max/2, (u32)req->dma,
-                                       ((i < udc->hw_ep_max/2) ? "RX" : "TX"));
+                                       i % ci->hw_ep_max/2, (u32)req->dma,
+                                       ((i < ci->hw_ep_max/2) ? "RX" : "TX"));
 
                        for (j = 0; j < qSize; j++)
                                n += scnprintf(buf + n, PAGE_SIZE - n,
                                                " %04X:    %08X\n", j,
                                                *((u32 *)req->ptr + j));
                }
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_unlock_irqrestore(&ci->lock, flags);
 
        return n;
 }
index 9eacd21..ebff9f4 100644 (file)
@@ -116,7 +116,8 @@ static int host_start(struct ci13xxx *ci)
        hcd->regs = ci->hw_bank.abs;
        hcd->has_tt = 1;
 
-       hcd->power_budget = ci->udc_driver->power_budget;
+       hcd->power_budget = ci->platdata->power_budget;
+       hcd->phy = ci->transceiver;
 
        ehci = hcd_to_ehci(hcd);
        ehci->caps = ci->hw_bank.cap;
index 51f9694..c7a032a 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/device.h>
 #include <linux/dmapool.h>
 #include <linux/dma-mapping.h>
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/module.h>
@@ -66,11 +67,11 @@ static inline int hw_ep_bit(int num, int dir)
        return num + (dir ? 16 : 0);
 }
 
-static inline int ep_to_bit(struct ci13xxx *udc, int n)
+static inline int ep_to_bit(struct ci13xxx *ci, int n)
 {
-       int fill = 16 - udc->hw_ep_max / 2;
+       int fill = 16 - ci->hw_ep_max / 2;
 
-       if (n >= udc->hw_ep_max / 2)
+       if (n >= ci->hw_ep_max / 2)
                n += fill;
 
        return n;
@@ -83,17 +84,17 @@ static inline int ep_to_bit(struct ci13xxx *udc, int n)
  *
  * This function returns an error code
  */
-static int hw_device_state(struct ci13xxx *udc, u32 dma)
+static int hw_device_state(struct ci13xxx *ci, u32 dma)
 {
        if (dma) {
-               hw_write(udc, OP_ENDPTLISTADDR, ~0, dma);
+               hw_write(ci, OP_ENDPTLISTADDR, ~0, dma);
                /* interrupt, error, port change, reset, sleep/suspend */
-               hw_write(udc, OP_USBINTR, ~0,
+               hw_write(ci, OP_USBINTR, ~0,
                             USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
-               hw_write(udc, OP_USBCMD, USBCMD_RS, USBCMD_RS);
+               hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
        } else {
-               hw_write(udc, OP_USBCMD, USBCMD_RS, 0);
-               hw_write(udc, OP_USBINTR, ~0, 0);
+               hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+               hw_write(ci, OP_USBINTR, ~0, 0);
        }
        return 0;
 }
@@ -105,16 +106,16 @@ static int hw_device_state(struct ci13xxx *udc, u32 dma)
  *
  * This function returns an error code
  */
-static int hw_ep_flush(struct ci13xxx *udc, int num, int dir)
+static int hw_ep_flush(struct ci13xxx *ci, int num, int dir)
 {
        int n = hw_ep_bit(num, dir);
 
        do {
                /* flush any pending transfer */
-               hw_write(udc, OP_ENDPTFLUSH, BIT(n), BIT(n));
-               while (hw_read(udc, OP_ENDPTFLUSH, BIT(n)))
+               hw_write(ci, OP_ENDPTFLUSH, BIT(n), BIT(n));
+               while (hw_read(ci, OP_ENDPTFLUSH, BIT(n)))
                        cpu_relax();
-       } while (hw_read(udc, OP_ENDPTSTAT, BIT(n)));
+       } while (hw_read(ci, OP_ENDPTSTAT, BIT(n)));
 
        return 0;
 }
@@ -126,10 +127,10 @@ static int hw_ep_flush(struct ci13xxx *udc, int num, int dir)
  *
  * This function returns an error code
  */
-static int hw_ep_disable(struct ci13xxx *udc, int num, int dir)
+static int hw_ep_disable(struct ci13xxx *ci, int num, int dir)
 {
-       hw_ep_flush(udc, num, dir);
-       hw_write(udc, OP_ENDPTCTRL + num,
+       hw_ep_flush(ci, num, dir);
+       hw_write(ci, OP_ENDPTCTRL + num,
                 dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0);
        return 0;
 }
@@ -142,7 +143,7 @@ static int hw_ep_disable(struct ci13xxx *udc, int num, int dir)
  *
  * This function returns an error code
  */
-static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type)
+static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type)
 {
        u32 mask, data;
 
@@ -165,7 +166,7 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type)
                mask |= ENDPTCTRL_RXE;  /* enable  */
                data |= ENDPTCTRL_RXE;
        }
-       hw_write(udc, OP_ENDPTCTRL + num, mask, data);
+       hw_write(ci, OP_ENDPTCTRL + num, mask, data);
        return 0;
 }
 
@@ -176,11 +177,11 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type)
  *
  * This function returns 1 if endpoint halted
  */
-static int hw_ep_get_halt(struct ci13xxx *udc, int num, int dir)
+static int hw_ep_get_halt(struct ci13xxx *ci, int num, int dir)
 {
        u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
 
-       return hw_read(udc, OP_ENDPTCTRL + num, mask) ? 1 : 0;
+       return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0;
 }
 
 /**
@@ -190,10 +191,10 @@ static int hw_ep_get_halt(struct ci13xxx *udc, int num, int dir)
  *
  * This function returns setup status
  */
-static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n)
+static int hw_test_and_clear_setup_status(struct ci13xxx *ci, int n)
 {
-       n = ep_to_bit(udc, n);
-       return hw_test_and_clear(udc, OP_ENDPTSETUPSTAT, BIT(n));
+       n = ep_to_bit(ci, n);
+       return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n));
 }
 
 /**
@@ -204,18 +205,18 @@ static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n)
  *
  * This function returns an error code
  */
-static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl)
+static int hw_ep_prime(struct ci13xxx *ci, int num, int dir, int is_ctrl)
 {
        int n = hw_ep_bit(num, dir);
 
-       if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num)))
+       if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num)))
                return -EAGAIN;
 
-       hw_write(udc, OP_ENDPTPRIME, BIT(n), BIT(n));
+       hw_write(ci, OP_ENDPTPRIME, BIT(n), BIT(n));
 
-       while (hw_read(udc, OP_ENDPTPRIME, BIT(n)))
+       while (hw_read(ci, OP_ENDPTPRIME, BIT(n)))
                cpu_relax();
-       if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num)))
+       if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num)))
                return -EAGAIN;
 
        /* status shoult be tested according with manual but it doesn't work */
@@ -231,7 +232,7 @@ static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl)
  *
  * This function returns an error code
  */
-static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value)
+static int hw_ep_set_halt(struct ci13xxx *ci, int num, int dir, int value)
 {
        if (value != 0 && value != 1)
                return -EINVAL;
@@ -242,9 +243,9 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value)
                u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
 
                /* data toggle - reserved for EP0 but it's in ESS */
-               hw_write(udc, reg, mask_xs|mask_xr,
+               hw_write(ci, reg, mask_xs|mask_xr,
                          value ? mask_xs : mask_xr);
-       } while (value != hw_ep_get_halt(udc, num, dir));
+       } while (value != hw_ep_get_halt(ci, num, dir));
 
        return 0;
 }
@@ -254,10 +255,10 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value)
  *
  * This function returns true if high speed port
  */
-static int hw_port_is_high_speed(struct ci13xxx *udc)
+static int hw_port_is_high_speed(struct ci13xxx *ci)
 {
-       return udc->hw_bank.lpm ? hw_read(udc, OP_DEVLC, DEVLC_PSPD) :
-               hw_read(udc, OP_PORTSC, PORTSC_HSP);
+       return ci->hw_bank.lpm ? hw_read(ci, OP_DEVLC, DEVLC_PSPD) :
+               hw_read(ci, OP_PORTSC, PORTSC_HSP);
 }
 
 /**
@@ -265,9 +266,9 @@ static int hw_port_is_high_speed(struct ci13xxx *udc)
  *
  * This function returns register data
  */
-static u32 hw_read_intr_enable(struct ci13xxx *udc)
+static u32 hw_read_intr_enable(struct ci13xxx *ci)
 {
-       return hw_read(udc, OP_USBINTR, ~0);
+       return hw_read(ci, OP_USBINTR, ~0);
 }
 
 /**
@@ -275,9 +276,9 @@ static u32 hw_read_intr_enable(struct ci13xxx *udc)
  *
  * This function returns register data
  */
-static u32 hw_read_intr_status(struct ci13xxx *udc)
+static u32 hw_read_intr_status(struct ci13xxx *ci)
 {
-       return hw_read(udc, OP_USBSTS, ~0);
+       return hw_read(ci, OP_USBSTS, ~0);
 }
 
 /**
@@ -287,10 +288,10 @@ static u32 hw_read_intr_status(struct ci13xxx *udc)
  *
  * This function returns complete status
  */
-static int hw_test_and_clear_complete(struct ci13xxx *udc, int n)
+static int hw_test_and_clear_complete(struct ci13xxx *ci, int n)
 {
-       n = ep_to_bit(udc, n);
-       return hw_test_and_clear(udc, OP_ENDPTCOMPLETE, BIT(n));
+       n = ep_to_bit(ci, n);
+       return hw_test_and_clear(ci, OP_ENDPTCOMPLETE, BIT(n));
 }
 
 /**
@@ -299,11 +300,11 @@ static int hw_test_and_clear_complete(struct ci13xxx *udc, int n)
  *
  * This function returns active interrutps
  */
-static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc)
+static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci)
 {
-       u32 reg = hw_read_intr_status(udc) & hw_read_intr_enable(udc);
+       u32 reg = hw_read_intr_status(ci) & hw_read_intr_enable(ci);
 
-       hw_write(udc, OP_USBSTS, ~0, reg);
+       hw_write(ci, OP_USBSTS, ~0, reg);
        return reg;
 }
 
@@ -313,9 +314,9 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc)
  *
  * This function returns guard value
  */
-static int hw_test_and_clear_setup_guard(struct ci13xxx *udc)
+static int hw_test_and_clear_setup_guard(struct ci13xxx *ci)
 {
-       return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, 0);
+       return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, 0);
 }
 
 /**
@@ -324,9 +325,9 @@ static int hw_test_and_clear_setup_guard(struct ci13xxx *udc)
  *
  * This function returns guard value
  */
-static int hw_test_and_set_setup_guard(struct ci13xxx *udc)
+static int hw_test_and_set_setup_guard(struct ci13xxx *ci)
 {
-       return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW);
+       return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW);
 }
 
 /**
@@ -336,9 +337,9 @@ static int hw_test_and_set_setup_guard(struct ci13xxx *udc)
  * This function explicitly sets the address, without the "USBADRA" (advance)
  * feature, which is not supported by older versions of the controller.
  */
-static void hw_usb_set_address(struct ci13xxx *udc, u8 value)
+static void hw_usb_set_address(struct ci13xxx *ci, u8 value)
 {
-       hw_write(udc, OP_DEVICEADDR, DEVICEADDR_USBADR,
+       hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR,
                 value << ffs_nr(DEVICEADDR_USBADR));
 }
 
@@ -348,21 +349,21 @@ static void hw_usb_set_address(struct ci13xxx *udc, u8 value)
  *
  * This function returns an error code
  */
-static int hw_usb_reset(struct ci13xxx *udc)
+static int hw_usb_reset(struct ci13xxx *ci)
 {
-       hw_usb_set_address(udc, 0);
+       hw_usb_set_address(ci, 0);
 
        /* ESS flushes only at end?!? */
-       hw_write(udc, OP_ENDPTFLUSH,    ~0, ~0);
+       hw_write(ci, OP_ENDPTFLUSH,    ~0, ~0);
 
        /* clear setup token semaphores */
-       hw_write(udc, OP_ENDPTSETUPSTAT, 0,  0);
+       hw_write(ci, OP_ENDPTSETUPSTAT, 0,  0);
 
        /* clear complete status */
-       hw_write(udc, OP_ENDPTCOMPLETE,  0,  0);
+       hw_write(ci, OP_ENDPTCOMPLETE,  0,  0);
 
        /* wait until all bits cleared */
-       while (hw_read(udc, OP_ENDPTPRIME, ~0))
+       while (hw_read(ci, OP_ENDPTPRIME, ~0))
                udelay(10);             /* not RTOS friendly */
 
        /* reset all endpoints ? */
@@ -394,7 +395,7 @@ static inline u8 _usb_addr(struct ci13xxx_ep *ep)
  */
 static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 {
-       struct ci13xxx *udc = mEp->udc;
+       struct ci13xxx *ci = mEp->ci;
        unsigned i;
        int ret = 0;
        unsigned length = mReq->req.length;
@@ -417,7 +418,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
                if (!mReq->req.no_interrupt)
                        mReq->zptr->token   |= TD_IOC;
        }
-       ret = usb_gadget_map_request(&udc->gadget, &mReq->req, mEp->dir);
+       ret = usb_gadget_map_request(&ci->gadget, &mReq->req, mEp->dir);
        if (ret)
                return ret;
 
@@ -453,13 +454,13 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
                else
                        mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK;
                wmb();
-               if (hw_read(udc, OP_ENDPTPRIME, BIT(n)))
+               if (hw_read(ci, OP_ENDPTPRIME, BIT(n)))
                        goto done;
                do {
-                       hw_write(udc, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW);
-                       tmp_stat = hw_read(udc, OP_ENDPTSTAT, BIT(n));
-               } while (!hw_read(udc, OP_USBCMD, USBCMD_ATDTW));
-               hw_write(udc, OP_USBCMD, USBCMD_ATDTW, 0);
+                       hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW);
+                       tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n));
+               } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW));
+               hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0);
                if (tmp_stat)
                        goto done;
        }
@@ -471,7 +472,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 
        wmb();   /* synchronize before ep prime */
 
-       ret = hw_ep_prime(udc, mEp->num, mEp->dir,
+       ret = hw_ep_prime(ci, mEp->num, mEp->dir,
                           mEp->type == USB_ENDPOINT_XFER_CONTROL);
 done:
        return ret;
@@ -501,7 +502,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 
        mReq->req.status = 0;
 
-       usb_gadget_unmap_request(&mEp->udc->gadget, &mReq->req, mEp->dir);
+       usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir);
 
        mReq->req.status = mReq->ptr->token & TD_STATUS;
        if ((TD_STATUS_HALTED & mReq->req.status) != 0)
@@ -533,7 +534,7 @@ __acquires(mEp->lock)
        if (mEp == NULL)
                return -EINVAL;
 
-       hw_ep_flush(mEp->udc, mEp->num, mEp->dir);
+       hw_ep_flush(mEp->ci, mEp->num, mEp->dir);
 
        while (!list_empty(&mEp->qh.queue)) {
 
@@ -562,33 +563,33 @@ __acquires(mEp->lock)
 static int _gadget_stop_activity(struct usb_gadget *gadget)
 {
        struct usb_ep *ep;
-       struct ci13xxx    *udc = container_of(gadget, struct ci13xxx, gadget);
+       struct ci13xxx    *ci = container_of(gadget, struct ci13xxx, gadget);
        unsigned long flags;
 
-       spin_lock_irqsave(&udc->lock, flags);
-       udc->gadget.speed = USB_SPEED_UNKNOWN;
-       udc->remote_wakeup = 0;
-       udc->suspended = 0;
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
+       ci->gadget.speed = USB_SPEED_UNKNOWN;
+       ci->remote_wakeup = 0;
+       ci->suspended = 0;
+       spin_unlock_irqrestore(&ci->lock, flags);
 
        /* flush all endpoints */
        gadget_for_each_ep(ep, gadget) {
                usb_ep_fifo_flush(ep);
        }
-       usb_ep_fifo_flush(&udc->ep0out->ep);
-       usb_ep_fifo_flush(&udc->ep0in->ep);
+       usb_ep_fifo_flush(&ci->ep0out->ep);
+       usb_ep_fifo_flush(&ci->ep0in->ep);
 
-       if (udc->driver)
-               udc->driver->disconnect(gadget);
+       if (ci->driver)
+               ci->driver->disconnect(gadget);
 
        /* make sure to disable all endpoints */
        gadget_for_each_ep(ep, gadget) {
                usb_ep_disable(ep);
        }
 
-       if (udc->status != NULL) {
-               usb_ep_free_request(&udc->ep0in->ep, udc->status);
-               udc->status = NULL;
+       if (ci->status != NULL) {
+               usb_ep_free_request(&ci->ep0in->ep, ci->status);
+               ci->status = NULL;
        }
 
        return 0;
@@ -599,36 +600,36 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
  *****************************************************************************/
 /**
  * isr_reset_handler: USB reset interrupt handler
- * @udc: UDC device
+ * @ci: UDC device
  *
  * This function resets USB engine after a bus reset occurred
  */
-static void isr_reset_handler(struct ci13xxx *udc)
-__releases(udc->lock)
-__acquires(udc->lock)
+static void isr_reset_handler(struct ci13xxx *ci)
+__releases(ci->lock)
+__acquires(ci->lock)
 {
        int retval;
 
        dbg_event(0xFF, "BUS RST", 0);
 
-       spin_unlock(&udc->lock);
-       retval = _gadget_stop_activity(&udc->gadget);
+       spin_unlock(&ci->lock);
+       retval = _gadget_stop_activity(&ci->gadget);
        if (retval)
                goto done;
 
-       retval = hw_usb_reset(udc);
+       retval = hw_usb_reset(ci);
        if (retval)
                goto done;
 
-       udc->status = usb_ep_alloc_request(&udc->ep0in->ep, GFP_ATOMIC);
-       if (udc->status == NULL)
+       ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC);
+       if (ci->status == NULL)
                retval = -ENOMEM;
 
 done:
-       spin_lock(&udc->lock);
+       spin_lock(&ci->lock);
 
        if (retval)
-               dev_err(udc->dev, "error: %i\n", retval);
+               dev_err(ci->dev, "error: %i\n", retval);
 }
 
 /**
@@ -649,17 +650,17 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
 
 /**
  * isr_get_status_response: get_status request response
- * @udc: udc struct
+ * @ci: ci struct
  * @setup: setup request packet
  *
  * This function returns an error code
  */
-static int isr_get_status_response(struct ci13xxx *udc,
+static int isr_get_status_response(struct ci13xxx *ci,
                                   struct usb_ctrlrequest *setup)
 __releases(mEp->lock)
 __acquires(mEp->lock)
 {
-       struct ci13xxx_ep *mEp = udc->ep0in;
+       struct ci13xxx_ep *mEp = ci->ep0in;
        struct usb_request *req = NULL;
        gfp_t gfp_flags = GFP_ATOMIC;
        int dir, num, retval;
@@ -683,14 +684,14 @@ __acquires(mEp->lock)
 
        if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
                /* Assume that device is bus powered for now. */
-               *(u16 *)req->buf = udc->remote_wakeup << 1;
+               *(u16 *)req->buf = ci->remote_wakeup << 1;
                retval = 0;
        } else if ((setup->bRequestType & USB_RECIP_MASK) \
                   == USB_RECIP_ENDPOINT) {
                dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ?
                        TX : RX;
                num =  le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK;
-               *(u16 *)req->buf = hw_ep_get_halt(udc, num, dir);
+               *(u16 *)req->buf = hw_ep_get_halt(ci, num, dir);
        }
        /* else do nothing; reserved for future use */
 
@@ -722,39 +723,39 @@ __acquires(mEp->lock)
 static void
 isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req)
 {
-       struct ci13xxx *udc = req->context;
+       struct ci13xxx *ci = req->context;
        unsigned long flags;
 
-       if (udc->setaddr) {
-               hw_usb_set_address(udc, udc->address);
-               udc->setaddr = false;
+       if (ci->setaddr) {
+               hw_usb_set_address(ci, ci->address);
+               ci->setaddr = false;
        }
 
-       spin_lock_irqsave(&udc->lock, flags);
-       if (udc->test_mode)
-               hw_port_test_set(udc, udc->test_mode);
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
+       if (ci->test_mode)
+               hw_port_test_set(ci, ci->test_mode);
+       spin_unlock_irqrestore(&ci->lock, flags);
 }
 
 /**
  * isr_setup_status_phase: queues the status phase of a setup transation
- * @udc: udc struct
+ * @ci: ci struct
  *
  * This function returns an error code
  */
-static int isr_setup_status_phase(struct ci13xxx *udc)
+static int isr_setup_status_phase(struct ci13xxx *ci)
 __releases(mEp->lock)
 __acquires(mEp->lock)
 {
        int retval;
        struct ci13xxx_ep *mEp;
 
-       mEp = (udc->ep0_dir == TX) ? udc->ep0out : udc->ep0in;
-       udc->status->context = udc;
-       udc->status->complete = isr_setup_status_complete;
+       mEp = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in;
+       ci->status->context = ci;
+       ci->status->complete = isr_setup_status_complete;
 
        spin_unlock(mEp->lock);
-       retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC);
+       retval = usb_ep_queue(&mEp->ep, ci->status, GFP_ATOMIC);
        spin_lock(mEp->lock);
 
        return retval;
@@ -789,7 +790,7 @@ __acquires(mEp->lock)
                        spin_unlock(mEp->lock);
                        if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
                                        mReq->req.length)
-                               mEpTemp = mEp->udc->ep0in;
+                               mEpTemp = mEp->ci->ep0in;
                        mReq->req.complete(&mEpTemp->ep, &mReq->req);
                        spin_lock(mEp->lock);
                }
@@ -805,48 +806,48 @@ __acquires(mEp->lock)
 
 /**
  * isr_tr_complete_handler: transaction complete interrupt handler
- * @udc: UDC descriptor
+ * @ci: UDC descriptor
  *
  * This function handles traffic events
  */
-static void isr_tr_complete_handler(struct ci13xxx *udc)
-__releases(udc->lock)
-__acquires(udc->lock)
+static void isr_tr_complete_handler(struct ci13xxx *ci)
+__releases(ci->lock)
+__acquires(ci->lock)
 {
        unsigned i;
        u8 tmode = 0;
 
-       for (i = 0; i < udc->hw_ep_max; i++) {
-               struct ci13xxx_ep *mEp  = &udc->ci13xxx_ep[i];
+       for (i = 0; i < ci->hw_ep_max; i++) {
+               struct ci13xxx_ep *mEp  = &ci->ci13xxx_ep[i];
                int type, num, dir, err = -EINVAL;
                struct usb_ctrlrequest req;
 
                if (mEp->ep.desc == NULL)
                        continue;   /* not configured */
 
-               if (hw_test_and_clear_complete(udc, i)) {
+               if (hw_test_and_clear_complete(ci, i)) {
                        err = isr_tr_complete_low(mEp);
                        if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
                                if (err > 0)   /* needs status phase */
-                                       err = isr_setup_status_phase(udc);
+                                       err = isr_setup_status_phase(ci);
                                if (err < 0) {
                                        dbg_event(_usb_addr(mEp),
                                                  "ERROR", err);
-                                       spin_unlock(&udc->lock);
+                                       spin_unlock(&ci->lock);
                                        if (usb_ep_set_halt(&mEp->ep))
-                                               dev_err(udc->dev,
+                                               dev_err(ci->dev,
                                                        "error: ep_set_halt\n");
-                                       spin_lock(&udc->lock);
+                                       spin_lock(&ci->lock);
                                }
                        }
                }
 
                if (mEp->type != USB_ENDPOINT_XFER_CONTROL ||
-                   !hw_test_and_clear_setup_status(udc, i))
+                   !hw_test_and_clear_setup_status(ci, i))
                        continue;
 
                if (i != 0) {
-                       dev_warn(udc->dev, "ctrl traffic at endpoint %d\n", i);
+                       dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i);
                        continue;
                }
 
@@ -854,18 +855,18 @@ __acquires(udc->lock)
                 * Flush data and handshake transactions of previous
                 * setup packet.
                 */
-               _ep_nuke(udc->ep0out);
-               _ep_nuke(udc->ep0in);
+               _ep_nuke(ci->ep0out);
+               _ep_nuke(ci->ep0in);
 
                /* read_setup_packet */
                do {
-                       hw_test_and_set_setup_guard(udc);
+                       hw_test_and_set_setup_guard(ci);
                        memcpy(&req, &mEp->qh.ptr->setup, sizeof(req));
-               } while (!hw_test_and_clear_setup_guard(udc));
+               } while (!hw_test_and_clear_setup_guard(ci));
 
                type = req.bRequestType;
 
-               udc->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
+               ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
 
                dbg_setup(_usb_addr(mEp), &req);
 
@@ -880,23 +881,23 @@ __acquires(udc->lock)
                                dir = num & USB_ENDPOINT_DIR_MASK;
                                num &= USB_ENDPOINT_NUMBER_MASK;
                                if (dir) /* TX */
-                                       num += udc->hw_ep_max/2;
-                               if (!udc->ci13xxx_ep[num].wedge) {
-                                       spin_unlock(&udc->lock);
+                                       num += ci->hw_ep_max/2;
+                               if (!ci->ci13xxx_ep[num].wedge) {
+                                       spin_unlock(&ci->lock);
                                        err = usb_ep_clear_halt(
-                                               &udc->ci13xxx_ep[num].ep);
-                                       spin_lock(&udc->lock);
+                                               &ci->ci13xxx_ep[num].ep);
+                                       spin_lock(&ci->lock);
                                        if (err)
                                                break;
                                }
-                               err = isr_setup_status_phase(udc);
+                               err = isr_setup_status_phase(ci);
                        } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
                                        le16_to_cpu(req.wValue) ==
                                        USB_DEVICE_REMOTE_WAKEUP) {
                                if (req.wLength != 0)
                                        break;
-                               udc->remote_wakeup = 0;
-                               err = isr_setup_status_phase(udc);
+                               ci->remote_wakeup = 0;
+                               err = isr_setup_status_phase(ci);
                        } else {
                                goto delegate;
                        }
@@ -909,7 +910,7 @@ __acquires(udc->lock)
                        if (le16_to_cpu(req.wLength) != 2 ||
                            le16_to_cpu(req.wValue)  != 0)
                                break;
-                       err = isr_get_status_response(udc, &req);
+                       err = isr_get_status_response(ci, &req);
                        break;
                case USB_REQ_SET_ADDRESS:
                        if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
@@ -917,9 +918,9 @@ __acquires(udc->lock)
                        if (le16_to_cpu(req.wLength) != 0 ||
                            le16_to_cpu(req.wIndex)  != 0)
                                break;
-                       udc->address = (u8)le16_to_cpu(req.wValue);
-                       udc->setaddr = true;
-                       err = isr_setup_status_phase(udc);
+                       ci->address = (u8)le16_to_cpu(req.wValue);
+                       ci->setaddr = true;
+                       err = isr_setup_status_phase(ci);
                        break;
                case USB_REQ_SET_FEATURE:
                        if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
@@ -931,20 +932,20 @@ __acquires(udc->lock)
                                dir = num & USB_ENDPOINT_DIR_MASK;
                                num &= USB_ENDPOINT_NUMBER_MASK;
                                if (dir) /* TX */
-                                       num += udc->hw_ep_max/2;
+                                       num += ci->hw_ep_max/2;
 
-                               spin_unlock(&udc->lock);
-                               err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
-                               spin_lock(&udc->lock);
+                               spin_unlock(&ci->lock);
+                               err = usb_ep_set_halt(&ci->ci13xxx_ep[num].ep);
+                               spin_lock(&ci->lock);
                                if (!err)
-                                       isr_setup_status_phase(udc);
+                                       isr_setup_status_phase(ci);
                        } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
                                if (req.wLength != 0)
                                        break;
                                switch (le16_to_cpu(req.wValue)) {
                                case USB_DEVICE_REMOTE_WAKEUP:
-                                       udc->remote_wakeup = 1;
-                                       err = isr_setup_status_phase(udc);
+                                       ci->remote_wakeup = 1;
+                                       err = isr_setup_status_phase(ci);
                                        break;
                                case USB_DEVICE_TEST_MODE:
                                        tmode = le16_to_cpu(req.wIndex) >> 8;
@@ -954,9 +955,9 @@ __acquires(udc->lock)
                                        case TEST_SE0_NAK:
                                        case TEST_PACKET:
                                        case TEST_FORCE_EN:
-                                               udc->test_mode = tmode;
+                                               ci->test_mode = tmode;
                                                err = isr_setup_status_phase(
-                                                               udc);
+                                                               ci);
                                                break;
                                        default:
                                                break;
@@ -971,21 +972,21 @@ __acquires(udc->lock)
                default:
 delegate:
                        if (req.wLength == 0)   /* no data phase */
-                               udc->ep0_dir = TX;
+                               ci->ep0_dir = TX;
 
-                       spin_unlock(&udc->lock);
-                       err = udc->driver->setup(&udc->gadget, &req);
-                       spin_lock(&udc->lock);
+                       spin_unlock(&ci->lock);
+                       err = ci->driver->setup(&ci->gadget, &req);
+                       spin_lock(&ci->lock);
                        break;
                }
 
                if (err < 0) {
                        dbg_event(_usb_addr(mEp), "ERROR", err);
 
-                       spin_unlock(&udc->lock);
+                       spin_unlock(&ci->lock);
                        if (usb_ep_set_halt(&mEp->ep))
-                               dev_err(udc->dev, "error: ep_set_halt\n");
-                       spin_lock(&udc->lock);
+                               dev_err(ci->dev, "error: ep_set_halt\n");
+                       spin_lock(&ci->lock);
                }
        }
 }
@@ -1015,7 +1016,7 @@ static int ep_enable(struct usb_ep *ep,
        mEp->ep.desc = desc;
 
        if (!list_empty(&mEp->qh.queue))
-               dev_warn(mEp->udc->dev, "enabling a non-empty endpoint!\n");
+               dev_warn(mEp->ci->dev, "enabling a non-empty endpoint!\n");
 
        mEp->dir  = usb_endpoint_dir_in(desc) ? TX : RX;
        mEp->num  = usb_endpoint_num(desc);
@@ -1043,7 +1044,7 @@ static int ep_enable(struct usb_ep *ep,
         * is always enabled
         */
        if (mEp->num)
-               retval |= hw_ep_enable(mEp->udc, mEp->num, mEp->dir, mEp->type);
+               retval |= hw_ep_enable(mEp->ci, mEp->num, mEp->dir, mEp->type);
 
        spin_unlock_irqrestore(mEp->lock, flags);
        return retval;
@@ -1074,7 +1075,7 @@ static int ep_disable(struct usb_ep *ep)
                dbg_event(_usb_addr(mEp), "DISABLE", 0);
 
                retval |= _ep_nuke(mEp);
-               retval |= hw_ep_disable(mEp->udc, mEp->num, mEp->dir);
+               retval |= hw_ep_disable(mEp->ci, mEp->num, mEp->dir);
 
                if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
                        mEp->dir = (mEp->dir == TX) ? RX : TX;
@@ -1131,7 +1132,7 @@ static void ep_free_request(struct usb_ep *ep, struct usb_request *req)
        if (ep == NULL || req == NULL) {
                return;
        } else if (!list_empty(&mReq->queue)) {
-               dev_err(mEp->udc->dev, "freeing queued request\n");
+               dev_err(mEp->ci->dev, "freeing queued request\n");
                return;
        }
 
@@ -1156,7 +1157,7 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
 {
        struct ci13xxx_ep  *mEp  = container_of(ep,  struct ci13xxx_ep, ep);
        struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
-       struct ci13xxx *udc = mEp->udc;
+       struct ci13xxx *ci = mEp->ci;
        int retval = 0;
        unsigned long flags;
 
@@ -1167,12 +1168,12 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
 
        if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
                if (req->length)
-                       mEp = (udc->ep0_dir == RX) ?
-                              udc->ep0out : udc->ep0in;
+                       mEp = (ci->ep0_dir == RX) ?
+                              ci->ep0out : ci->ep0in;
                if (!list_empty(&mEp->qh.queue)) {
                        _ep_nuke(mEp);
                        retval = -EOVERFLOW;
-                       dev_warn(mEp->udc->dev, "endpoint ctrl %X nuked\n",
+                       dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n",
                                 _usb_addr(mEp));
                }
        }
@@ -1180,14 +1181,14 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
        /* first nuke then test link, e.g. previous status has not sent */
        if (!list_empty(&mReq->queue)) {
                retval = -EBUSY;
-               dev_err(mEp->udc->dev, "request already in queue\n");
+               dev_err(mEp->ci->dev, "request already in queue\n");
                goto done;
        }
 
        if (req->length > 4 * CI13XXX_PAGE_SIZE) {
                req->length = 4 * CI13XXX_PAGE_SIZE;
                retval = -EMSGSIZE;
-               dev_warn(mEp->udc->dev, "request length truncated\n");
+               dev_warn(mEp->ci->dev, "request length truncated\n");
        }
 
        dbg_queue(_usb_addr(mEp), req, retval);
@@ -1230,12 +1231,12 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
 
        dbg_event(_usb_addr(mEp), "DEQUEUE", 0);
 
-       hw_ep_flush(mEp->udc, mEp->num, mEp->dir);
+       hw_ep_flush(mEp->ci, mEp->num, mEp->dir);
 
        /* pop request */
        list_del_init(&mReq->queue);
 
-       usb_gadget_unmap_request(&mEp->udc->gadget, req, mEp->dir);
+       usb_gadget_unmap_request(&mEp->ci->gadget, req, mEp->dir);
 
        req->status = -ECONNRESET;
 
@@ -1277,7 +1278,7 @@ static int ep_set_halt(struct usb_ep *ep, int value)
        direction = mEp->dir;
        do {
                dbg_event(_usb_addr(mEp), "HALT", value);
-               retval |= hw_ep_set_halt(mEp->udc, mEp->num, mEp->dir, value);
+               retval |= hw_ep_set_halt(mEp->ci, mEp->num, mEp->dir, value);
 
                if (!value)
                        mEp->wedge = 0;
@@ -1325,14 +1326,14 @@ static void ep_fifo_flush(struct usb_ep *ep)
        unsigned long flags;
 
        if (ep == NULL) {
-               dev_err(mEp->udc->dev, "%02X: -EINVAL\n", _usb_addr(mEp));
+               dev_err(mEp->ci->dev, "%02X: -EINVAL\n", _usb_addr(mEp));
                return;
        }
 
        spin_lock_irqsave(mEp->lock, flags);
 
        dbg_event(_usb_addr(mEp), "FFLUSH", 0);
-       hw_ep_flush(mEp->udc, mEp->num, mEp->dir);
+       hw_ep_flush(mEp->ci, mEp->num, mEp->dir);
 
        spin_unlock_irqrestore(mEp->lock, flags);
 }
@@ -1358,30 +1359,30 @@ static const struct usb_ep_ops usb_ep_ops = {
  *****************************************************************************/
 static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
 {
-       struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+       struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget);
        unsigned long flags;
        int gadget_ready = 0;
 
-       if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS))
+       if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS))
                return -EOPNOTSUPP;
 
-       spin_lock_irqsave(&udc->lock, flags);
-       udc->vbus_active = is_active;
-       if (udc->driver)
+       spin_lock_irqsave(&ci->lock, flags);
+       ci->vbus_active = is_active;
+       if (ci->driver)
                gadget_ready = 1;
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_unlock_irqrestore(&ci->lock, flags);
 
        if (gadget_ready) {
                if (is_active) {
                        pm_runtime_get_sync(&_gadget->dev);
-                       hw_device_reset(udc, USBMODE_CM_DC);
-                       hw_device_state(udc, udc->ep0out->qh.dma);
+                       hw_device_reset(ci, USBMODE_CM_DC);
+                       hw_device_state(ci, ci->ep0out->qh.dma);
                } else {
-                       hw_device_state(udc, 0);
-                       if (udc->udc_driver->notify_event)
-                               udc->udc_driver->notify_event(udc,
+                       hw_device_state(ci, 0);
+                       if (ci->platdata->notify_event)
+                               ci->platdata->notify_event(ci,
                                CI13XXX_CONTROLLER_STOPPED_EVENT);
-                       _gadget_stop_activity(&udc->gadget);
+                       _gadget_stop_activity(&ci->gadget);
                        pm_runtime_put_sync(&_gadget->dev);
                }
        }
@@ -1391,31 +1392,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
 
 static int ci13xxx_wakeup(struct usb_gadget *_gadget)
 {
-       struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+       struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget);
        unsigned long flags;
        int ret = 0;
 
-       spin_lock_irqsave(&udc->lock, flags);
-       if (!udc->remote_wakeup) {
+       spin_lock_irqsave(&ci->lock, flags);
+       if (!ci->remote_wakeup) {
                ret = -EOPNOTSUPP;
                goto out;
        }
-       if (!hw_read(udc, OP_PORTSC, PORTSC_SUSP)) {
+       if (!hw_read(ci, OP_PORTSC, PORTSC_SUSP)) {
                ret = -EINVAL;
                goto out;
        }
-       hw_write(udc, OP_PORTSC, PORTSC_FPR, PORTSC_FPR);
+       hw_write(ci, OP_PORTSC, PORTSC_FPR, PORTSC_FPR);
 out:
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_unlock_irqrestore(&ci->lock, flags);
        return ret;
 }
 
 static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
 {
-       struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+       struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget);
 
-       if (udc->transceiver)
-               return usb_phy_set_power(udc->transceiver, mA);
+       if (ci->transceiver)
+               return usb_phy_set_power(ci->transceiver, mA);
        return -ENOTSUPP;
 }
 
@@ -1436,28 +1437,28 @@ static const struct usb_gadget_ops usb_gadget_ops = {
        .udc_stop       = ci13xxx_stop,
 };
 
-static int init_eps(struct ci13xxx *udc)
+static int init_eps(struct ci13xxx *ci)
 {
        int retval = 0, i, j;
 
-       for (i = 0; i < udc->hw_ep_max/2; i++)
+       for (i = 0; i < ci->hw_ep_max/2; i++)
                for (j = RX; j <= TX; j++) {
-                       int k = i + j * udc->hw_ep_max/2;
-                       struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k];
+                       int k = i + j * ci->hw_ep_max/2;
+                       struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[k];
 
                        scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i,
                                        (j == TX)  ? "in" : "out");
 
-                       mEp->udc          = udc;
-                       mEp->lock         = &udc->lock;
-                       mEp->td_pool      = udc->td_pool;
+                       mEp->ci          = ci;
+                       mEp->lock         = &ci->lock;
+                       mEp->td_pool      = ci->td_pool;
 
                        mEp->ep.name      = mEp->name;
                        mEp->ep.ops       = &usb_ep_ops;
                        mEp->ep.maxpacket = CTRL_PAYLOAD_MAX;
 
                        INIT_LIST_HEAD(&mEp->qh.queue);
-                       mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL,
+                       mEp->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL,
                                                     &mEp->qh.dma);
                        if (mEp->qh.ptr == NULL)
                                retval = -ENOMEM;
@@ -1470,14 +1471,14 @@ static int init_eps(struct ci13xxx *udc)
                         */
                        if (i == 0) {
                                if (j == RX)
-                                       udc->ep0out = mEp;
+                                       ci->ep0out = mEp;
                                else
-                                       udc->ep0in = mEp;
+                                       ci->ep0in = mEp;
 
                                continue;
                        }
 
-                       list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list);
+                       list_add_tail(&mEp->ep.ep_list, &ci->gadget.ep_list);
                }
 
        return retval;
@@ -1493,7 +1494,7 @@ static int init_eps(struct ci13xxx *udc)
 static int ci13xxx_start(struct usb_gadget *gadget,
                         struct usb_gadget_driver *driver)
 {
-       struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
+       struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget);
        unsigned long flags;
        int retval = -ENOMEM;
 
@@ -1501,35 +1502,35 @@ static int ci13xxx_start(struct usb_gadget *gadget,
                return -EINVAL;
 
 
-       udc->ep0out->ep.desc = &ctrl_endpt_out_desc;
-       retval = usb_ep_enable(&udc->ep0out->ep);
+       ci->ep0out->ep.desc = &ctrl_endpt_out_desc;
+       retval = usb_ep_enable(&ci->ep0out->ep);
        if (retval)
                return retval;
 
-       udc->ep0in->ep.desc = &ctrl_endpt_in_desc;
-       retval = usb_ep_enable(&udc->ep0in->ep);
+       ci->ep0in->ep.desc = &ctrl_endpt_in_desc;
+       retval = usb_ep_enable(&ci->ep0in->ep);
        if (retval)
                return retval;
-       spin_lock_irqsave(&udc->lock, flags);
-
-       udc->driver = driver;
-       pm_runtime_get_sync(&udc->gadget.dev);
-       if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
-               if (udc->vbus_active) {
-                       if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
-                               hw_device_reset(udc, USBMODE_CM_DC);
+       spin_lock_irqsave(&ci->lock, flags);
+
+       ci->driver = driver;
+       pm_runtime_get_sync(&ci->gadget.dev);
+       if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) {
+               if (ci->vbus_active) {
+                       if (ci->platdata->flags & CI13XXX_REGS_SHARED)
+                               hw_device_reset(ci, USBMODE_CM_DC);
                } else {
-                       pm_runtime_put_sync(&udc->gadget.dev);
+                       pm_runtime_put_sync(&ci->gadget.dev);
                        goto done;
                }
        }
 
-       retval = hw_device_state(udc, udc->ep0out->qh.dma);
+       retval = hw_device_state(ci, ci->ep0out->qh.dma);
        if (retval)
-               pm_runtime_put_sync(&udc->gadget.dev);
+               pm_runtime_put_sync(&ci->gadget.dev);
 
  done:
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_unlock_irqrestore(&ci->lock, flags);
        return retval;
 }
 
@@ -1539,25 +1540,25 @@ static int ci13xxx_start(struct usb_gadget *gadget,
 static int ci13xxx_stop(struct usb_gadget *gadget,
                        struct usb_gadget_driver *driver)
 {
-       struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
+       struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget);
        unsigned long flags;
 
-       spin_lock_irqsave(&udc->lock, flags);
+       spin_lock_irqsave(&ci->lock, flags);
 
-       if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
-                       udc->vbus_active) {
-               hw_device_state(udc, 0);
-               if (udc->udc_driver->notify_event)
-                       udc->udc_driver->notify_event(udc,
+       if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) ||
+                       ci->vbus_active) {
+               hw_device_state(ci, 0);
+               if (ci->platdata->notify_event)
+                       ci->platdata->notify_event(ci,
                        CI13XXX_CONTROLLER_STOPPED_EVENT);
-               udc->driver = NULL;
-               spin_unlock_irqrestore(&udc->lock, flags);
-               _gadget_stop_activity(&udc->gadget);
-               spin_lock_irqsave(&udc->lock, flags);
-               pm_runtime_put(&udc->gadget.dev);
+               ci->driver = NULL;
+               spin_unlock_irqrestore(&ci->lock, flags);
+               _gadget_stop_activity(&ci->gadget);
+               spin_lock_irqsave(&ci->lock, flags);
+               pm_runtime_put(&ci->gadget.dev);
        }
 
-       spin_unlock_irqrestore(&udc->lock, flags);
+       spin_unlock_irqrestore(&ci->lock, flags);
 
        return 0;
 }
@@ -1566,64 +1567,64 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
  * BUS block
  *****************************************************************************/
 /**
- * udc_irq: udc interrupt handler
+ * udc_irq: ci interrupt handler
  *
  * This function returns IRQ_HANDLED if the IRQ has been handled
  * It locks access to registers
  */
-static irqreturn_t udc_irq(struct ci13xxx *udc)
+static irqreturn_t udc_irq(struct ci13xxx *ci)
 {
        irqreturn_t retval;
        u32 intr;
 
-       if (udc == NULL)
+       if (ci == NULL)
                return IRQ_HANDLED;
 
-       spin_lock(&udc->lock);
+       spin_lock(&ci->lock);
 
-       if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) {
-               if (hw_read(udc, OP_USBMODE, USBMODE_CM) !=
+       if (ci->platdata->flags & CI13XXX_REGS_SHARED) {
+               if (hw_read(ci, OP_USBMODE, USBMODE_CM) !=
                                USBMODE_CM_DC) {
-                       spin_unlock(&udc->lock);
+                       spin_unlock(&ci->lock);
                        return IRQ_NONE;
                }
        }
-       intr = hw_test_and_clear_intr_active(udc);
+       intr = hw_test_and_clear_intr_active(ci);
        dbg_interrupt(intr);
 
        if (intr) {
                /* order defines priority - do NOT change it */
                if (USBi_URI & intr)
-                       isr_reset_handler(udc);
+                       isr_reset_handler(ci);
 
                if (USBi_PCI & intr) {
-                       udc->gadget.speed = hw_port_is_high_speed(udc) ?
+                       ci->gadget.speed = hw_port_is_high_speed(ci) ?
                                USB_SPEED_HIGH : USB_SPEED_FULL;
-                       if (udc->suspended && udc->driver->resume) {
-                               spin_unlock(&udc->lock);
-                               udc->driver->resume(&udc->gadget);
-                               spin_lock(&udc->lock);
-                               udc->suspended = 0;
+                       if (ci->suspended && ci->driver->resume) {
+                               spin_unlock(&ci->lock);
+                               ci->driver->resume(&ci->gadget);
+                               spin_lock(&ci->lock);
+                               ci->suspended = 0;
                        }
                }
 
                if (USBi_UI  & intr)
-                       isr_tr_complete_handler(udc);
+                       isr_tr_complete_handler(ci);
 
                if (USBi_SLI & intr) {
-                       if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
-                           udc->driver->suspend) {
-                               udc->suspended = 1;
-                               spin_unlock(&udc->lock);
-                               udc->driver->suspend(&udc->gadget);
-                               spin_lock(&udc->lock);
+                       if (ci->gadget.speed != USB_SPEED_UNKNOWN &&
+                           ci->driver->suspend) {
+                               ci->suspended = 1;
+                               spin_unlock(&ci->lock);
+                               ci->driver->suspend(&ci->gadget);
+                               spin_lock(&ci->lock);
                        }
                }
                retval = IRQ_HANDLED;
        } else {
                retval = IRQ_NONE;
        }
-       spin_unlock(&udc->lock);
+       spin_unlock(&ci->lock);
 
        return retval;
 }
@@ -1640,112 +1641,111 @@ static void udc_release(struct device *dev)
 
 /**
  * udc_start: initialize gadget role
- * @udc: chipidea controller
+ * @ci: chipidea controller
  */
-static int udc_start(struct ci13xxx *udc)
+static int udc_start(struct ci13xxx *ci)
 {
-       struct device *dev = udc->dev;
+       struct device *dev = ci->dev;
        int retval = 0;
 
-       if (!udc)
-               return -EINVAL;
-
-       spin_lock_init(&udc->lock);
+       spin_lock_init(&ci->lock);
 
-       udc->gadget.ops          = &usb_gadget_ops;
-       udc->gadget.speed        = USB_SPEED_UNKNOWN;
-       udc->gadget.max_speed    = USB_SPEED_HIGH;
-       udc->gadget.is_otg       = 0;
-       udc->gadget.name         = udc->udc_driver->name;
+       ci->gadget.ops          = &usb_gadget_ops;
+       ci->gadget.speed        = USB_SPEED_UNKNOWN;
+       ci->gadget.max_speed    = USB_SPEED_HIGH;
+       ci->gadget.is_otg       = 0;
+       ci->gadget.name         = ci->platdata->name;
 
-       INIT_LIST_HEAD(&udc->gadget.ep_list);
+       INIT_LIST_HEAD(&ci->gadget.ep_list);
 
-       dev_set_name(&udc->gadget.dev, "gadget");
-       udc->gadget.dev.dma_mask = dev->dma_mask;
-       udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask;
-       udc->gadget.dev.parent   = dev;
-       udc->gadget.dev.release  = udc_release;
+       dev_set_name(&ci->gadget.dev, "gadget");
+       ci->gadget.dev.dma_mask = dev->dma_mask;
+       ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask;
+       ci->gadget.dev.parent   = dev;
+       ci->gadget.dev.release  = udc_release;
 
        /* alloc resources */
-       udc->qh_pool = dma_pool_create("ci13xxx_qh", dev,
+       ci->qh_pool = dma_pool_create("ci13xxx_qh", dev,
                                       sizeof(struct ci13xxx_qh),
                                       64, CI13XXX_PAGE_SIZE);
-       if (udc->qh_pool == NULL)
+       if (ci->qh_pool == NULL)
                return -ENOMEM;
 
-       udc->td_pool = dma_pool_create("ci13xxx_td", dev,
+       ci->td_pool = dma_pool_create("ci13xxx_td", dev,
                                       sizeof(struct ci13xxx_td),
                                       64, CI13XXX_PAGE_SIZE);
-       if (udc->td_pool == NULL) {
+       if (ci->td_pool == NULL) {
                retval = -ENOMEM;
                goto free_qh_pool;
        }
 
-       retval = init_eps(udc);
+       retval = init_eps(ci);
        if (retval)
                goto free_pools;
 
-       udc->gadget.ep0 = &udc->ep0in->ep;
+       ci->gadget.ep0 = &ci->ep0in->ep;
 
-       udc->transceiver = usb_get_transceiver();
+       if (ci->global_phy)
+               ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
 
-       if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
-               if (udc->transceiver == NULL) {
+       if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
+               if (ci->transceiver == NULL) {
                        retval = -ENODEV;
                        goto free_pools;
                }
        }
 
-       if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
-               retval = hw_device_reset(udc, USBMODE_CM_DC);
+       if (!(ci->platdata->flags & CI13XXX_REGS_SHARED)) {
+               retval = hw_device_reset(ci, USBMODE_CM_DC);
                if (retval)
                        goto put_transceiver;
        }
 
-       retval = device_register(&udc->gadget.dev);
+       retval = device_register(&ci->gadget.dev);
        if (retval) {
-               put_device(&udc->gadget.dev);
+               put_device(&ci->gadget.dev);
                goto put_transceiver;
        }
 
-       retval = dbg_create_files(&udc->gadget.dev);
+       retval = dbg_create_files(&ci->gadget.dev);
        if (retval)
                goto unreg_device;
 
-       if (udc->transceiver) {
-               retval = otg_set_peripheral(udc->transceiver->otg,
-                                               &udc->gadget);
+       if (!IS_ERR_OR_NULL(ci->transceiver)) {
+               retval = otg_set_peripheral(ci->transceiver->otg,
+                                               &ci->gadget);
                if (retval)
                        goto remove_dbg;
        }
 
-       retval = usb_add_gadget_udc(dev, &udc->gadget);
+       retval = usb_add_gadget_udc(dev, &ci->gadget);
        if (retval)
                goto remove_trans;
 
-       pm_runtime_no_callbacks(&udc->gadget.dev);
-       pm_runtime_enable(&udc->gadget.dev);
+       pm_runtime_no_callbacks(&ci->gadget.dev);
+       pm_runtime_enable(&ci->gadget.dev);
 
        return retval;
 
 remove_trans:
-       if (udc->transceiver) {
-               otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
-               usb_put_transceiver(udc->transceiver);
+       if (!IS_ERR_OR_NULL(ci->transceiver)) {
+               otg_set_peripheral(ci->transceiver->otg, &ci->gadget);
+               if (ci->global_phy)
+                       usb_put_phy(ci->transceiver);
        }
 
        dev_err(dev, "error = %i\n", retval);
 remove_dbg:
-       dbg_remove_files(&udc->gadget.dev);
+       dbg_remove_files(&ci->gadget.dev);
 unreg_device:
-       device_unregister(&udc->gadget.dev);
+       device_unregister(&ci->gadget.dev);
 put_transceiver:
-       if (udc->transceiver)
-               usb_put_transceiver(udc->transceiver);
+       if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy)
+               usb_put_phy(ci->transceiver);
 free_pools:
-       dma_pool_destroy(udc->td_pool);
+       dma_pool_destroy(ci->td_pool);
 free_qh_pool:
-       dma_pool_destroy(udc->qh_pool);
+       dma_pool_destroy(ci->qh_pool);
        return retval;
 }
 
@@ -1754,32 +1754,33 @@ free_qh_pool:
  *
  * No interrupts active, the IRQ has been released
  */
-static void udc_stop(struct ci13xxx *udc)
+static void udc_stop(struct ci13xxx *ci)
 {
        int i;
 
-       if (udc == NULL)
+       if (ci == NULL)
                return;
 
-       usb_del_gadget_udc(&udc->gadget);
+       usb_del_gadget_udc(&ci->gadget);
 
-       for (i = 0; i < udc->hw_ep_max; i++) {
-               struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
+       for (i = 0; i < ci->hw_ep_max; i++) {
+               struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i];
 
-               dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma);
+               dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma);
        }
 
-       dma_pool_destroy(udc->td_pool);
-       dma_pool_destroy(udc->qh_pool);
+       dma_pool_destroy(ci->td_pool);
+       dma_pool_destroy(ci->qh_pool);
 
-       if (udc->transceiver) {
-               otg_set_peripheral(udc->transceiver->otg, NULL);
-               usb_put_transceiver(udc->transceiver);
+       if (!IS_ERR_OR_NULL(ci->transceiver)) {
+               otg_set_peripheral(ci->transceiver->otg, NULL);
+               if (ci->global_phy)
+                       usb_put_phy(ci->transceiver);
        }
-       dbg_remove_files(&udc->gadget.dev);
-       device_unregister(&udc->gadget.dev);
+       dbg_remove_files(&ci->gadget.dev);
+       device_unregister(&ci->gadget.dev);
        /* my kobject is dynamic, I swear! */
-       memset(&udc->gadget, 0, sizeof(udc->gadget));
+       memset(&ci->gadget, 0, sizeof(ci->gadget));
 }
 
 /**
index 36a2a0b..56d6bf6 100644 (file)
@@ -996,7 +996,7 @@ static int acm_probe(struct usb_interface *intf,
                case USB_CDC_CALL_MANAGEMENT_TYPE:
                        call_management_function = buffer[3];
                        call_interface_num = buffer[4];
-                       if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
+                       if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
                                dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
                        break;
                default:
index ee46927..65a55ab 100644 (file)
@@ -32,8 +32,6 @@
 #define DRIVER_AUTHOR "Oliver Neukum"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
 
-#define HUAWEI_VENDOR_ID       0x12D1
-
 static const struct usb_device_id wdm_ids[] = {
        {
                .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
@@ -41,29 +39,6 @@ static const struct usb_device_id wdm_ids[] = {
                .bInterfaceClass = USB_CLASS_COMM,
                .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
        },
-       {
-               /* 
-                * Huawei E392, E398 and possibly other Qualcomm based modems
-                * embed the Qualcomm QMI protocol inside CDC on CDC ECM like
-                * control interfaces.  Userspace access to this is required
-                * to configure the accompanying data interface
-                */
-               .match_flags        = USB_DEVICE_ID_MATCH_VENDOR |
-                                       USB_DEVICE_ID_MATCH_INT_INFO,
-               .idVendor           = HUAWEI_VENDOR_ID,
-               .bInterfaceClass    = USB_CLASS_VENDOR_SPEC,
-               .bInterfaceSubClass = 1,
-               .bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
-       },
-       {
-                /* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */
-               .match_flags        = USB_DEVICE_ID_MATCH_VENDOR |
-                                     USB_DEVICE_ID_MATCH_INT_INFO,
-               .idVendor           = HUAWEI_VENDOR_ID,
-               .bInterfaceClass    = USB_CLASS_VENDOR_SPEC,
-               .bInterfaceSubClass = 1,
-               .bInterfaceProtocol = 57, /* NOTE: CDC ECM control interface! */
-       },
        { }
 };
 
index e0f1079..ebb8a9d 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/notifier.h>
 #include <linux/security.h>
 #include <linux/user_namespace.h>
+#include <linux/scatterlist.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 #include <linux/moduleparam.h>
@@ -55,6 +56,7 @@
 
 #define USB_MAXBUS                     64
 #define USB_DEVICE_MAX                 USB_MAXBUS * 128
+#define USB_SG_SIZE                    16384 /* split-size for large txs */
 
 /* Mutual exclusion for removal, open, and release */
 DEFINE_MUTEX(usbfs_mutex);
@@ -285,9 +287,16 @@ static struct async *alloc_async(unsigned int numisoframes)
 
 static void free_async(struct async *as)
 {
+       int i;
+
        put_pid(as->pid);
        if (as->cred)
                put_cred(as->cred);
+       for (i = 0; i < as->urb->num_sgs; i++) {
+               if (sg_page(&as->urb->sg[i]))
+                       kfree(sg_virt(&as->urb->sg[i]));
+       }
+       kfree(as->urb->sg);
        kfree(as->urb->transfer_buffer);
        kfree(as->urb->setup_packet);
        usb_free_urb(as->urb);
@@ -388,6 +397,53 @@ static void snoop_urb(struct usb_device *udev,
        }
 }
 
+static void snoop_urb_data(struct urb *urb, unsigned len)
+{
+       int i, size;
+
+       if (!usbfs_snoop)
+               return;
+
+       if (urb->num_sgs == 0) {
+               print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
+                       urb->transfer_buffer, len, 1);
+               return;
+       }
+
+       for (i = 0; i < urb->num_sgs && len; i++) {
+               size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
+               print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
+                       sg_virt(&urb->sg[i]), size, 1);
+               len -= size;
+       }
+}
+
+static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb)
+{
+       unsigned i, len, size;
+
+       if (urb->number_of_packets > 0)         /* Isochronous */
+               len = urb->transfer_buffer_length;
+       else                                    /* Non-Isoc */
+               len = urb->actual_length;
+
+       if (urb->num_sgs == 0) {
+               if (copy_to_user(userbuffer, urb->transfer_buffer, len))
+                       return -EFAULT;
+               return 0;
+       }
+
+       for (i = 0; i < urb->num_sgs && len; i++) {
+               size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
+               if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size))
+                       return -EFAULT;
+               userbuffer += size;
+               len -= size;
+       }
+
+       return 0;
+}
+
 #define AS_CONTINUATION        1
 #define AS_UNLINK      2
 
@@ -454,9 +510,10 @@ static void async_completed(struct urb *urb)
        }
        snoop(&urb->dev->dev, "urb complete\n");
        snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
-                       as->status, COMPLETE,
-                       ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ?
-                               NULL : urb->transfer_buffer, urb->actual_length);
+                       as->status, COMPLETE, NULL, 0);
+       if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN)
+               snoop_urb_data(urb, urb->actual_length);
+
        if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
                        as->status != -ENOENT)
                cancel_bulk_urbs(ps, as->bulk_addr);
@@ -1114,8 +1171,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        struct async *as = NULL;
        struct usb_ctrlrequest *dr = NULL;
        unsigned int u, totlen, isofrmlen;
-       int ret, ifnum = -1;
-       int is_in;
+       int i, ret, is_in, num_sgs = 0, ifnum = -1;
+       void *buf;
 
        if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
                                USBDEVFS_URB_SHORT_NOT_OK |
@@ -1199,6 +1256,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        goto interrupt_urb;
                }
                uurb->number_of_packets = 0;
+               num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE);
+               if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize)
+                       num_sgs = 0;
                break;
 
        case USBDEVFS_URB_TYPE_INTERRUPT:
@@ -1255,26 +1315,67 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                ret = -ENOMEM;
                goto error;
        }
-       u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length;
+
+       u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length +
+            num_sgs * sizeof(struct scatterlist);
        ret = usbfs_increase_memory_usage(u);
        if (ret)
                goto error;
        as->mem_usage = u;
 
-       if (uurb->buffer_length > 0) {
+       if (num_sgs) {
+               as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist),
+                                     GFP_KERNEL);
+               if (!as->urb->sg) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+               as->urb->num_sgs = num_sgs;
+               sg_init_table(as->urb->sg, as->urb->num_sgs);
+
+               totlen = uurb->buffer_length;
+               for (i = 0; i < as->urb->num_sgs; i++) {
+                       u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen;
+                       buf = kmalloc(u, GFP_KERNEL);
+                       if (!buf) {
+                               ret = -ENOMEM;
+                               goto error;
+                       }
+                       sg_set_buf(&as->urb->sg[i], buf, u);
+
+                       if (!is_in) {
+                               if (copy_from_user(buf, uurb->buffer, u)) {
+                                       ret = -EFAULT;
+                                       goto error;
+                               }
+                       }
+                       totlen -= u;
+               }
+       } else if (uurb->buffer_length > 0) {
                as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
                                GFP_KERNEL);
                if (!as->urb->transfer_buffer) {
                        ret = -ENOMEM;
                        goto error;
                }
-               /* Isochronous input data may end up being discontiguous
-                * if some of the packets are short.  Clear the buffer so
-                * that the gaps don't leak kernel data to userspace.
-                */
-               if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO)
+
+               if (!is_in) {
+                       if (copy_from_user(as->urb->transfer_buffer,
+                                          uurb->buffer,
+                                          uurb->buffer_length)) {
+                               ret = -EFAULT;
+                               goto error;
+                       }
+               } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
+                       /*
+                        * Isochronous input data may end up being
+                        * discontiguous if some of the packets are short.
+                        * Clear the buffer so that the gaps don't leak
+                        * kernel data to userspace.
+                        */
                        memset(as->urb->transfer_buffer, 0,
                                        uurb->buffer_length);
+               }
        }
        as->urb->dev = ps->dev;
        as->urb->pipe = (uurb->type << 30) |
@@ -1328,17 +1429,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        as->pid = get_pid(task_pid(current));
        as->cred = get_current_cred();
        security_task_getsecid(current, &as->secid);
-       if (!is_in && uurb->buffer_length > 0) {
-               if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
-                               uurb->buffer_length)) {
-                       ret = -EFAULT;
-                       goto error;
-               }
-       }
        snoop_urb(ps->dev, as->userurb, as->urb->pipe,
                        as->urb->transfer_buffer_length, 0, SUBMIT,
-                       is_in ? NULL : as->urb->transfer_buffer,
-                               uurb->buffer_length);
+                       NULL, 0);
+       if (!is_in)
+               snoop_urb_data(as->urb, as->urb->transfer_buffer_length);
+
        async_newpending(as);
 
        if (usb_endpoint_xfer_bulk(&ep->desc)) {
@@ -1433,11 +1529,7 @@ static int processcompl(struct async *as, void __user * __user *arg)
        unsigned int i;
 
        if (as->userbuffer && urb->actual_length) {
-               if (urb->number_of_packets > 0)         /* Isochronous */
-                       i = urb->transfer_buffer_length;
-               else                                    /* Non-Isoc */
-                       i = urb->actual_length;
-               if (copy_to_user(as->userbuffer, urb->transfer_buffer, i))
+               if (copy_urb_data_to_user(as->userbuffer, urb))
                        goto err_out;
        }
        if (put_user(as->status, &userurb->status))
@@ -1604,10 +1696,10 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
        void __user *addr = as->userurb;
        unsigned int i;
 
-       if (as->userbuffer && urb->actual_length)
-               if (copy_to_user(as->userbuffer, urb->transfer_buffer,
-                                urb->actual_length))
+       if (as->userbuffer && urb->actual_length) {
+               if (copy_urb_data_to_user(as->userbuffer, urb))
                        return -EFAULT;
+       }
        if (put_user(as->status, &userurb->status))
                return -EFAULT;
        if (put_user(urb->actual_length, &userurb->actual_length))
@@ -1820,6 +1912,22 @@ static int proc_release_port(struct dev_state *ps, void __user *arg)
        return usb_hub_release_port(ps->dev, portnum, ps);
 }
 
+static int proc_get_capabilities(struct dev_state *ps, void __user *arg)
+{
+       __u32 caps;
+
+       caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM;
+       if (!ps->dev->bus->no_stop_on_short)
+               caps |= USBDEVFS_CAP_BULK_CONTINUATION;
+       if (ps->dev->bus->sg_tablesize)
+               caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER;
+
+       if (put_user(caps, (__u32 __user *)arg))
+               return -EFAULT;
+
+       return 0;
+}
+
 /*
  * NOTE:  All requests here that have interface numbers as parameters
  * are assuming that somehow the configuration has been prevented from
@@ -1990,6 +2098,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
                snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
                ret = proc_release_port(ps, p);
                break;
+       case USBDEVFS_GET_CAPABILITIES:
+               ret = proc_get_capabilities(ps, p);
+               break;
        }
        usb_unlock_device(dev);
        if (ret >= 0)
index f536aeb..445455a 100644 (file)
@@ -367,6 +367,7 @@ static int usb_probe_interface(struct device *dev)
        return error;
 
  err:
+       usb_set_intfdata(intf, NULL);
        intf->needs_remote_wakeup = 0;
        intf->condition = USB_INTERFACE_UNBOUND;
        usb_cancel_queued_reset(intf);
@@ -606,30 +607,19 @@ int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
 }
 
 /* returns 0 if no match, 1 if match */
-int usb_match_one_id(struct usb_interface *interface,
-                    const struct usb_device_id *id)
+int usb_match_one_id_intf(struct usb_device *dev,
+                         struct usb_host_interface *intf,
+                         const struct usb_device_id *id)
 {
-       struct usb_host_interface *intf;
-       struct usb_device *dev;
-
-       /* proc_connectinfo in devio.c may call us with id == NULL. */
-       if (id == NULL)
-               return 0;
-
-       intf = interface->cur_altsetting;
-       dev = interface_to_usbdev(interface);
-
-       if (!usb_match_device(dev, id))
-               return 0;
-
-       /* The interface class, subclass, and protocol should never be
+       /* The interface class, subclass, protocol and number should never be
         * checked for a match if the device class is Vendor Specific,
         * unless the match record specifies the Vendor ID. */
        if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
                        !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
                        (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
                                USB_DEVICE_ID_MATCH_INT_SUBCLASS |
-                               USB_DEVICE_ID_MATCH_INT_PROTOCOL)))
+                               USB_DEVICE_ID_MATCH_INT_PROTOCOL |
+                               USB_DEVICE_ID_MATCH_INT_NUMBER)))
                return 0;
 
        if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
@@ -644,8 +634,32 @@ int usb_match_one_id(struct usb_interface *interface,
            (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
                return 0;
 
+       if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
+           (id->bInterfaceNumber != intf->desc.bInterfaceNumber))
+               return 0;
+
        return 1;
 }
+
+/* returns 0 if no match, 1 if match */
+int usb_match_one_id(struct usb_interface *interface,
+                    const struct usb_device_id *id)
+{
+       struct usb_host_interface *intf;
+       struct usb_device *dev;
+
+       /* proc_connectinfo in devio.c may call us with id == NULL. */
+       if (id == NULL)
+               return 0;
+
+       intf = interface->cur_altsetting;
+       dev = interface_to_usbdev(interface);
+
+       if (!usb_match_device(dev, id))
+               return 0;
+
+       return usb_match_one_id_intf(dev, intf, id);
+}
 EXPORT_SYMBOL_GPL(usb_match_one_id);
 
 /**
index e673b26..e5387a4 100644 (file)
@@ -92,7 +92,7 @@ static int init_usb_class(void)
        }
 
        kref_init(&usb_class->kref);
-       usb_class->class = class_create(THIS_MODULE, "usb");
+       usb_class->class = class_create(THIS_MODULE, "usbmisc");
        if (IS_ERR(usb_class->class)) {
                result = IS_ERR(usb_class->class);
                printk(KERN_ERR "class_create failed for usb devices\n");
index 190b1ec..bc84106 100644 (file)
@@ -1398,7 +1398,15 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
            && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
                if (hcd->self.uses_dma) {
                        if (urb->num_sgs) {
-                               int n = dma_map_sg(
+                               int n;
+
+                               /* We don't support sg for isoc transfers ! */
+                               if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
+                                       WARN_ON(1);
+                                       return -EINVAL;
+                               }
+
+                               n = dma_map_sg(
                                                hcd->self.controller,
                                                urb->sg,
                                                urb->num_sgs,
index 8fb4849..821126e 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
 #include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
 #include <linux/usb/quirks.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
@@ -81,7 +82,7 @@ struct usb_hub {
        u8                      indicator[USB_MAXCHILDREN];
        struct delayed_work     leds;
        struct delayed_work     init_work;
-       void                    **port_owners;
+       struct dev_state        **port_owners;
 };
 
 static inline int hub_is_superspeed(struct usb_device *hdev)
@@ -1271,7 +1272,8 @@ static int hub_configure(struct usb_hub *hub,
 
        hdev->children = kzalloc(hdev->maxchild *
                                sizeof(struct usb_device *), GFP_KERNEL);
-       hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
+       hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *),
+                               GFP_KERNEL);
        if (!hdev->children || !hub->port_owners) {
                ret = -ENOMEM;
                goto fail;
@@ -1649,7 +1651,7 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
  * to one of these "claimed" ports, the program will "own" the device.
  */
 static int find_port_owner(struct usb_device *hdev, unsigned port1,
-               void ***ppowner)
+               struct dev_state ***ppowner)
 {
        if (hdev->state == USB_STATE_NOTATTACHED)
                return -ENODEV;
@@ -1664,10 +1666,11 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1,
 }
 
 /* In the following three functions, the caller must hold hdev's lock */
-int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
+int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
+                      struct dev_state *owner)
 {
        int rc;
-       void **powner;
+       struct dev_state **powner;
 
        rc = find_port_owner(hdev, port1, &powner);
        if (rc)
@@ -1678,10 +1681,11 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
        return rc;
 }
 
-int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
+int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
+                        struct dev_state *owner)
 {
        int rc;
-       void **powner;
+       struct dev_state **powner;
 
        rc = find_port_owner(hdev, port1, &powner);
        if (rc)
@@ -1692,10 +1696,10 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
        return rc;
 }
 
-void usb_hub_release_all_ports(struct usb_device *hdev, void *owner)
+void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
 {
        int n;
-       void **powner;
+       struct dev_state **powner;
 
        n = find_port_owner(hdev, 1, &powner);
        if (n == 0) {
@@ -2065,7 +2069,7 @@ static int usb_enumerate_device(struct usb_device *udev)
                if (err < 0) {
                        dev_err(&udev->dev, "can't read configurations, error %d\n",
                                err);
-                       goto fail;
+                       return err;
                }
        }
        if (udev->wusb == 1 && udev->authorized == 0) {
@@ -2081,8 +2085,12 @@ static int usb_enumerate_device(struct usb_device *udev)
                udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
        }
        err = usb_enumerate_device_otg(udev);
-fail:
-       return err;
+       if (err < 0)
+               return err;
+
+       usb_detect_interface_quirks(udev);
+
+       return 0;
 }
 
 static void set_usb_port_removable(struct usb_device *udev)
@@ -2611,6 +2619,50 @@ static int check_port_resume_type(struct usb_device *udev,
        return status;
 }
 
+int usb_disable_ltm(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       /* Check if the roothub and device supports LTM. */
+       if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+                       !usb_device_supports_ltm(udev))
+               return 0;
+
+       /* Clear Feature LTM Enable can only be sent if the device is
+        * configured.
+        */
+       if (!udev->actconfig)
+               return 0;
+
+       return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                       USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
+                       USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+                       USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       /* Check if the roothub and device supports LTM. */
+       if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+                       !usb_device_supports_ltm(udev))
+               return;
+
+       /* Set Feature LTM Enable can only be sent if the device is
+        * configured.
+        */
+       if (!udev->actconfig)
+               return;
+
+       usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                       USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+                       USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+                       USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
+
 #ifdef CONFIG_USB_SUSPEND
 
 /*
@@ -2706,6 +2758,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
        if (udev->usb2_hw_lpm_enabled == 1)
                usb_set_usb2_hardware_lpm(udev, 0);
 
+       if (usb_disable_ltm(udev)) {
+               dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.",
+                               __func__);
+               return -ENOMEM;
+       }
        if (usb_unlocked_disable_lpm(udev)) {
                dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
                                __func__);
@@ -2735,7 +2792,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                if (udev->usb2_hw_lpm_capable == 1)
                        usb_set_usb2_hardware_lpm(udev, 1);
 
-               /* Try to enable USB3 LPM again */
+               /* Try to enable USB3 LTM and LPM again */
+               usb_enable_ltm(udev);
                usb_unlocked_enable_lpm(udev);
 
                /* System sleep transitions should never fail */
@@ -2936,7 +2994,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
                if (udev->usb2_hw_lpm_capable == 1)
                        usb_set_usb2_hardware_lpm(udev, 1);
 
-               /* Try to enable USB3 LPM */
+               /* Try to enable USB3 LTM and LPM */
+               usb_enable_ltm(udev);
                usb_unlocked_enable_lpm(udev);
        }
 
@@ -3489,6 +3548,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
 
 void usb_unlocked_enable_lpm(struct usb_device *udev) { }
 EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+
+int usb_disable_ltm(struct usb_device *udev)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev) { }
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
 #endif
 
 
@@ -4038,6 +4106,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
        }
 
+       if (hcd->phy && !hdev->parent) {
+               if (portstatus & USB_PORT_STAT_CONNECTION)
+                       usb_phy_notify_connect(hcd->phy, port1);
+               else
+                       usb_phy_notify_disconnect(hcd->phy, port1);
+       }
+
        /* Return now if debouncing failed or nothing is connected or
         * the device was "removed".
         */
@@ -4672,6 +4747,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        }
        parent_hub = hdev_to_hub(parent_hdev);
 
+       /* Disable LPM and LTM while we reset the device and reinstall the alt
+        * settings.  Device-initiated LPM settings, and system exit latency
+        * settings are cleared when the device is reset, so we have to set
+        * them up again.
+        */
+       ret = usb_unlocked_disable_lpm(udev);
+       if (ret) {
+               dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
+               goto re_enumerate;
+       }
+       ret = usb_disable_ltm(udev);
+       if (ret) {
+               dev_err(&udev->dev, "%s Failed to disable LTM\n.",
+                               __func__);
+               goto re_enumerate;
+       }
+
        set_bit(port1, parent_hub->busy_bits);
        for (i = 0; i < SET_CONFIG_TRIES; ++i) {
 
@@ -4699,22 +4791,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                goto done;
 
        mutex_lock(hcd->bandwidth_mutex);
-       /* Disable LPM while we reset the device and reinstall the alt settings.
-        * Device-initiated LPM settings, and system exit latency settings are
-        * cleared when the device is reset, so we have to set them up again.
-        */
-       ret = usb_disable_lpm(udev);
-       if (ret) {
-               dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
-               mutex_unlock(hcd->bandwidth_mutex);
-               goto done;
-       }
        ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
        if (ret < 0) {
                dev_warn(&udev->dev,
                                "Busted HC?  Not enough HCD resources for "
                                "old configuration.\n");
-               usb_enable_lpm(udev);
                mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
        }
@@ -4726,7 +4807,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                dev_err(&udev->dev,
                        "can't restore configuration #%d (error=%d)\n",
                        udev->actconfig->desc.bConfigurationValue, ret);
-               usb_enable_lpm(udev);
                mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
        }
@@ -4765,17 +4845,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                                desc->bInterfaceNumber,
                                desc->bAlternateSetting,
                                ret);
-                       usb_unlocked_enable_lpm(udev);
                        goto re_enumerate;
                }
        }
 
-       /* Now that the alt settings are re-installed, enable LPM. */
-       usb_unlocked_enable_lpm(udev);
 done:
+       /* Now that the alt settings are re-installed, enable LTM and LPM. */
+       usb_unlocked_enable_lpm(udev);
+       usb_enable_ltm(udev);
        return 0;
  
 re_enumerate:
+       /* LPM state doesn't matter when we're about to destroy the device. */
        hub_port_logical_disconnect(parent_hub, port1);
        return -ENODEV;
 }
index bdd1c67..0ab7da2 100644 (file)
@@ -1174,6 +1174,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
                        put_device(&dev->actconfig->interface[i]->dev);
                        dev->actconfig->interface[i] = NULL;
                }
+               usb_unlocked_disable_lpm(dev);
+               usb_disable_ltm(dev);
                dev->actconfig = NULL;
                if (dev->state == USB_STATE_CONFIGURED)
                        usb_set_device_state(dev, USB_STATE_ADDRESS);
@@ -1559,7 +1561,7 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
 
        if (add_uevent_var(env,
                   "MODALIAS=usb:"
-                  "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+                  "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02Xin%02X",
                   le16_to_cpu(usb_dev->descriptor.idVendor),
                   le16_to_cpu(usb_dev->descriptor.idProduct),
                   le16_to_cpu(usb_dev->descriptor.bcdDevice),
@@ -1568,7 +1570,8 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
                   usb_dev->descriptor.bDeviceProtocol,
                   alt->desc.bInterfaceClass,
                   alt->desc.bInterfaceSubClass,
-                  alt->desc.bInterfaceProtocol))
+                  alt->desc.bInterfaceProtocol,
+                  alt->desc.bInterfaceNumber))
                return -ENOMEM;
 
        return 0;
@@ -1791,14 +1794,15 @@ free_interfaces:
         * installed, so that the xHCI driver can recalculate the U1/U2
         * timeouts.
         */
-       if (usb_disable_lpm(dev)) {
+       if (dev->actconfig && usb_disable_lpm(dev)) {
                dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
                mutex_unlock(hcd->bandwidth_mutex);
                return -ENOMEM;
        }
        ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
        if (ret < 0) {
-               usb_enable_lpm(dev);
+               if (dev->actconfig)
+                       usb_enable_lpm(dev);
                mutex_unlock(hcd->bandwidth_mutex);
                usb_autosuspend_device(dev);
                goto free_interfaces;
@@ -1818,7 +1822,7 @@ free_interfaces:
        if (!cp) {
                usb_set_device_state(dev, USB_STATE_ADDRESS);
                usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
-               usb_enable_lpm(dev);
+               /* Leave LPM disabled while the device is unconfigured. */
                mutex_unlock(hcd->bandwidth_mutex);
                usb_autosuspend_device(dev);
                goto free_interfaces;
@@ -1876,6 +1880,8 @@ free_interfaces:
 
        /* Now that the interfaces are installed, re-enable LPM. */
        usb_unlocked_enable_lpm(dev);
+       /* Enable LTM if it was turned off by usb_disable_device. */
+       usb_enable_ltm(dev);
 
        /* Now that all the interfaces are set up, register them
         * to trigger binding of drivers to interfaces.  probe()
index 32d3adc..f15501f 100644 (file)
 #include <linux/usb/quirks.h>
 #include "usb.h"
 
-/* List of quirky USB devices.  Please keep this list ordered by:
+/* Lists of quirky USB devices, split in device quirks and interface quirks.
+ * Device quirks are applied at the very beginning of the enumeration process,
+ * right after reading the device descriptor. They can thus only match on device
+ * information.
+ *
+ * Interface quirks are applied after reading all the configuration descriptors.
+ * They can match on both device and interface information.
+ *
+ * Note that the DELAY_INIT and HONOR_BNUMINTERFACES quirks do not make sense as
+ * interface quirks, as they only influence the enumeration process which is run
+ * before processing the interface quirks.
+ *
+ * Please keep the lists ordered by:
  *     1) Vendor ID
  *     2) Product ID
  *     3) Class ID
- *
- * as we want specific devices to be overridden first, and only after that, any
- * class specific quirks.
- *
- * Right now the logic aborts if it finds a valid device in the table, we might
- * want to change that in the future if it turns out that a whole class of
- * devices is broken...
  */
 static const struct usb_device_id usb_quirk_list[] = {
        /* CBM - Flash disk */
@@ -38,53 +43,23 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* Creative SB Audigy 2 NX */
        { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
 
-       /* Logitech Webcam C200 */
-       { USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Webcam C250 */
-       { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Webcam C300 */
-       { USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Webcam B/C500 */
-       { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Webcam C600 */
-       { USB_DEVICE(0x046d, 0x0808), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Webcam Pro 9000 */
-       { USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME },
+       /* Logitech Quickcam Fusion */
+       { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME },
 
-       /* Logitech Webcam C905 */
-       { USB_DEVICE(0x046d, 0x080a), .driver_info = USB_QUIRK_RESET_RESUME },
+       /* Logitech Quickcam Orbit MP */
+       { USB_DEVICE(0x046d, 0x08c2), .driver_info = USB_QUIRK_RESET_RESUME },
 
-       /* Logitech Webcam C210 */
-       { USB_DEVICE(0x046d, 0x0819), .driver_info = USB_QUIRK_RESET_RESUME },
+       /* Logitech Quickcam Pro for Notebook */
+       { USB_DEVICE(0x046d, 0x08c3), .driver_info = USB_QUIRK_RESET_RESUME },
 
-       /* Logitech Webcam C260 */
-       { USB_DEVICE(0x046d, 0x081a), .driver_info = USB_QUIRK_RESET_RESUME },
+       /* Logitech Quickcam Pro 5000 */
+       { USB_DEVICE(0x046d, 0x08c5), .driver_info = USB_QUIRK_RESET_RESUME },
 
-       /* Logitech Webcam C310 */
-       { USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME },
+       /* Logitech Quickcam OEM Dell Notebook */
+       { USB_DEVICE(0x046d, 0x08c6), .driver_info = USB_QUIRK_RESET_RESUME },
 
-       /* Logitech Webcam C910 */
-       { USB_DEVICE(0x046d, 0x0821), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Webcam C160 */
-       { USB_DEVICE(0x046d, 0x0824), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Webcam C270 */
-       { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Quickcam Pro 9000 */
-       { USB_DEVICE(0x046d, 0x0990), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Quickcam E3500 */
-       { USB_DEVICE(0x046d, 0x09a4), .driver_info = USB_QUIRK_RESET_RESUME },
-
-       /* Logitech Quickcam Vision Pro */
-       { USB_DEVICE(0x046d, 0x09a6), .driver_info = USB_QUIRK_RESET_RESUME },
+       /* Logitech Quickcam OEM Cisco VT Camera II */
+       { USB_DEVICE(0x046d, 0x08c7), .driver_info = USB_QUIRK_RESET_RESUME },
 
        /* Logitech Harmony 700-series */
        { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -156,16 +131,57 @@ static const struct usb_device_id usb_quirk_list[] = {
        { }  /* terminating entry must be last */
 };
 
-static const struct usb_device_id *find_id(struct usb_device *udev)
+static const struct usb_device_id usb_interface_quirk_list[] = {
+       /* Logitech UVC Cameras */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x046d, USB_CLASS_VIDEO, 1, 0),
+         .driver_info = USB_QUIRK_RESET_RESUME },
+
+       { }  /* terminating entry must be last */
+};
+
+static bool usb_match_any_interface(struct usb_device *udev,
+                                   const struct usb_device_id *id)
 {
-       const struct usb_device_id *id = usb_quirk_list;
+       unsigned int i;
 
-       for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
-                       id->driver_info; id++) {
-               if (usb_match_device(udev, id))
-                       return id;
+       for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) {
+               struct usb_host_config *cfg = &udev->config[i];
+               unsigned int j;
+
+               for (j = 0; j < cfg->desc.bNumInterfaces; ++j) {
+                       struct usb_interface_cache *cache;
+                       struct usb_host_interface *intf;
+
+                       cache = cfg->intf_cache[j];
+                       if (cache->num_altsetting == 0)
+                               continue;
+
+                       intf = &cache->altsetting[0];
+                       if (usb_match_one_id_intf(udev, intf, id))
+                               return true;
+               }
+       }
+
+       return false;
+}
+
+static u32 __usb_detect_quirks(struct usb_device *udev,
+                              const struct usb_device_id *id)
+{
+       u32 quirks = 0;
+
+       for (; id->match_flags; id++) {
+               if (!usb_match_device(udev, id))
+                       continue;
+
+               if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) &&
+                   !usb_match_any_interface(udev, id))
+                       continue;
+
+               quirks |= (u32)(id->driver_info);
        }
-       return NULL;
+
+       return quirks;
 }
 
 /*
@@ -173,14 +189,10 @@ static const struct usb_device_id *find_id(struct usb_device *udev)
  */
 void usb_detect_quirks(struct usb_device *udev)
 {
-       const struct usb_device_id *id = usb_quirk_list;
-
-       id = find_id(udev);
-       if (id)
-               udev->quirks = (u32)(id->driver_info);
+       udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
        if (udev->quirks)
                dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
-                               udev->quirks);
+                       udev->quirks);
 
        /* For the present, all devices default to USB-PERSIST enabled */
 #if 0          /* was: #ifdef CONFIG_PM */
@@ -197,3 +209,16 @@ void usb_detect_quirks(struct usb_device *udev)
                udev->persist_enabled = 1;
 #endif /* CONFIG_PM */
 }
+
+void usb_detect_interface_quirks(struct usb_device *udev)
+{
+       u32 quirks;
+
+       quirks = __usb_detect_quirks(udev, usb_interface_quirk_list);
+       if (quirks == 0)
+               return;
+
+       dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n",
+               quirks);
+       udev->quirks |= quirks;
+}
index 9a56e3a..682e825 100644 (file)
@@ -253,6 +253,15 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf)
 }
 static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL);
 
+static ssize_t
+show_ltm_capable(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       if (usb_device_supports_ltm(to_usb_device(dev)))
+               return sprintf(buf, "%s\n", "yes");
+       return sprintf(buf, "%s\n", "no");
+}
+static DEVICE_ATTR(ltm_capable, S_IRUGO, show_ltm_capable, NULL);
+
 #ifdef CONFIG_PM
 
 static ssize_t
@@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = {
        &dev_attr_authorized.attr,
        &dev_attr_remove.attr,
        &dev_attr_removable.attr,
+       &dev_attr_ltm_capable.attr,
        NULL,
 };
 static struct attribute_group dev_attr_grp = {
@@ -840,7 +850,7 @@ static ssize_t show_modalias(struct device *dev,
        alt = intf->cur_altsetting;
 
        return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
-                       "ic%02Xisc%02Xip%02X\n",
+                       "ic%02Xisc%02Xip%02Xin%02X\n",
                        le16_to_cpu(udev->descriptor.idVendor),
                        le16_to_cpu(udev->descriptor.idProduct),
                        le16_to_cpu(udev->descriptor.bcdDevice),
@@ -849,7 +859,8 @@ static ssize_t show_modalias(struct device *dev,
                        udev->descriptor.bDeviceProtocol,
                        alt->desc.bInterfaceClass,
                        alt->desc.bInterfaceSubClass,
-                       alt->desc.bInterfaceProtocol);
+                       alt->desc.bInterfaceProtocol,
+                       alt->desc.bInterfaceNumber);
 }
 static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
 
index 25d0c61..cd8fb44 100644 (file)
@@ -396,6 +396,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
        dev->dev.dma_mask = bus->controller->dma_mask;
        set_dev_node(&dev->dev, dev_to_node(bus->controller));
        dev->state = USB_STATE_ATTACHED;
+       dev->lpm_disable_count = 1;
        atomic_set(&dev->urbnum, 0);
 
        INIT_LIST_HEAD(&dev->ep0.urb_list);
index 5c5c538..acb103c 100644 (file)
@@ -1,5 +1,7 @@
 #include <linux/pm.h>
 
+struct dev_state;
+
 /* Functions local to drivers/usb/core/ */
 
 extern int usb_create_sysfs_dev_files(struct usb_device *dev);
@@ -24,6 +26,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
 extern int usb_deauthorize_device(struct usb_device *);
 extern int usb_authorize_device(struct usb_device *);
 extern void usb_detect_quirks(struct usb_device *udev);
+extern void usb_detect_interface_quirks(struct usb_device *udev);
 extern int usb_remove_device(struct usb_device *udev);
 
 extern int usb_get_device_descriptor(struct usb_device *dev,
@@ -35,16 +38,20 @@ extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_choose_configuration(struct usb_device *udev);
 
 extern void usb_kick_khubd(struct usb_device *dev);
+extern int usb_match_one_id_intf(struct usb_device *dev,
+                                struct usb_host_interface *intf,
+                                const struct usb_device_id *id);
 extern int usb_match_device(struct usb_device *dev,
                            const struct usb_device_id *id);
 extern void usb_forced_unbind_intf(struct usb_interface *intf);
 extern void usb_rebind_intf(struct usb_interface *intf);
 
 extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
-               void *owner);
+               struct dev_state *owner);
 extern int usb_hub_release_port(struct usb_device *hdev, unsigned port,
-               void *owner);
-extern void usb_hub_release_all_ports(struct usb_device *hdev, void *owner);
+               struct dev_state *owner);
+extern void usb_hub_release_all_ports(struct usb_device *hdev,
+               struct dev_state *owner);
 extern bool usb_device_is_owned(struct usb_device *udev);
 
 extern int  usb_hub_init(void);
index 1040bdb..c34452a 100644 (file)
@@ -148,6 +148,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
        reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
        dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 
+       mdelay(100);
+
        /* After PHYs are stable we can take Core out of reset state */
        reg = dwc3_readl(dwc->regs, DWC3_GCTL);
        reg &= ~DWC3_GCTL_CORESOFTRESET;
@@ -255,7 +257,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
  *
  * Returns 0 on success otherwise negative errno.
  */
-static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
+static int dwc3_event_buffers_setup(struct dwc3 *dwc)
 {
        struct dwc3_event_buffer        *evt;
        int                             n;
@@ -266,6 +268,8 @@ static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
                                evt->buf, (unsigned long long) evt->dma,
                                evt->length);
 
+               evt->lpos = 0;
+
                dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
                                lower_32_bits(evt->dma));
                dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
@@ -285,6 +289,9 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
 
        for (n = 0; n < dwc->num_event_buffers; n++) {
                evt = dwc->ev_buffs[n];
+
+               evt->lpos = 0;
+
                dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
                dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
                dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0);
@@ -328,8 +335,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
        }
        dwc->revision = reg;
 
-       dwc3_core_soft_reset(dwc);
-
        /* issue device SoftReset too */
        timeout = jiffies + msecs_to_jiffies(500);
        dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
@@ -347,6 +352,8 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
                cpu_relax();
        } while (true);
 
+       dwc3_core_soft_reset(dwc);
+
        dwc3_cache_hwparams(dwc);
 
        reg = dwc3_readl(dwc->regs, DWC3_GCTL);
index f69c877..151eca8 100644 (file)
@@ -67,6 +67,7 @@
 #define DWC3_DEVICE_EVENT_CONNECT_DONE         2
 #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE   3
 #define DWC3_DEVICE_EVENT_WAKEUP               4
+#define DWC3_DEVICE_EVENT_HIBER_REQ            5
 #define DWC3_DEVICE_EVENT_EOPF                 6
 #define DWC3_DEVICE_EVENT_SOF                  7
 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR                9
 #define DWC3_GCTL_PRTCAP_DEVICE        2
 #define DWC3_GCTL_PRTCAP_OTG   3
 
-#define DWC3_GCTL_CORESOFTRESET        (1 << 11)
-#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
-#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
-#define DWC3_GCTL_DISSCRAMBLE  (1 << 3)
-#define DWC3_GCTL_DSBLCLKGTNG  (1 << 0)
+#define DWC3_GCTL_CORESOFTRESET                (1 << 11)
+#define DWC3_GCTL_SCALEDOWN(n)         ((n) << 4)
+#define DWC3_GCTL_SCALEDOWN_MASK       DWC3_GCTL_SCALEDOWN(3)
+#define DWC3_GCTL_DISSCRAMBLE          (1 << 3)
+#define DWC3_GCTL_GBLHIBERNATIONEN     (1 << 1)
+#define DWC3_GCTL_DSBLCLKGTNG          (1 << 0)
 
 /* Global USB2 PHY Configuration Register */
-#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB2PHYCFG_SUSPHY        (1 << 6)
+#define DWC3_GUSB2PHYCFG_PHYSOFTRST    (1 << 31)
+#define DWC3_GUSB2PHYCFG_SUSPHY                (1 << 6)
 
 /* Global USB3 PIPE Control Register */
-#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
+#define DWC3_GUSB3PIPECTL_PHYSOFTRST   (1 << 31)
+#define DWC3_GUSB3PIPECTL_SUSPHY       (1 << 17)
 
 /* Global TX Fifo Size Register */
-#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
-#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
+#define DWC3_GTXFIFOSIZ_TXFDEF(n)      ((n) & 0xffff)
+#define DWC3_GTXFIFOSIZ_TXFSTADDR(n)   ((n) & 0xffff0000)
 
 /* Global HWPARAMS1 Register */
 #define DWC3_GHWPARAMS1_EN_PWROPT(n)   (((n) & (3 << 24)) >> 24)
 #define DWC3_GHWPARAMS1_EN_PWROPT_NO   0
 #define DWC3_GHWPARAMS1_EN_PWROPT_CLK  1
+#define DWC3_GHWPARAMS1_EN_PWROPT_HIB  2
+#define DWC3_GHWPARAMS1_PWROPT(n)      ((n) << 24)
+#define DWC3_GHWPARAMS1_PWROPT_MASK    DWC3_GHWPARAMS1_PWROPT(3)
+
+/* Global HWPARAMS4 Register */
+#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n)   (((n) & (0x0f << 13)) >> 13)
+#define DWC3_MAX_HIBER_SCRATCHBUFS             15
 
 /* Device Configuration Register */
 #define DWC3_DCFG_LPM_CAP      (1 << 22)
 #define DWC3_DCFG_LOWSPEED     (2 << 0)
 #define DWC3_DCFG_FULLSPEED1   (3 << 0)
 
+#define DWC3_DCFG_LPM_CAP      (1 << 22)
+
 /* Device Control Register */
 #define DWC3_DCTL_RUN_STOP     (1 << 31)
 #define DWC3_DCTL_CSFTRST      (1 << 30)
 #define DWC3_DCTL_LSFTRST      (1 << 29)
 
 #define DWC3_DCTL_HIRD_THRES_MASK      (0x1f << 24)
-#define DWC3_DCTL_HIRD_THRES(n)        (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24)
+#define DWC3_DCTL_HIRD_THRES(n)        ((n) << 24)
 
 #define DWC3_DCTL_APPL1RES     (1 << 23)
 
-#define DWC3_DCTL_TRGTULST_MASK        (0x0f << 17)
-#define DWC3_DCTL_TRGTULST(n)  ((n) << 17)
-
-#define DWC3_DCTL_TRGTULST_U2  (DWC3_DCTL_TRGTULST(2))
-#define DWC3_DCTL_TRGTULST_U3  (DWC3_DCTL_TRGTULST(3))
-#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
-#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
-#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
+/* These apply for core versions 1.87a and earlier */
+#define DWC3_DCTL_TRGTULST_MASK                (0x0f << 17)
+#define DWC3_DCTL_TRGTULST(n)          ((n) << 17)
+#define DWC3_DCTL_TRGTULST_U2          (DWC3_DCTL_TRGTULST(2))
+#define DWC3_DCTL_TRGTULST_U3          (DWC3_DCTL_TRGTULST(3))
+#define DWC3_DCTL_TRGTULST_SS_DIS      (DWC3_DCTL_TRGTULST(4))
+#define DWC3_DCTL_TRGTULST_RX_DET      (DWC3_DCTL_TRGTULST(5))
+#define DWC3_DCTL_TRGTULST_SS_INACT    (DWC3_DCTL_TRGTULST(6))
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
+#define DWC3_DCTL_L1_HIBER_EN  (1 << 18)
+#define DWC3_DCTL_CRS          (1 << 17)
+#define DWC3_DCTL_CSS          (1 << 16)
 
 #define DWC3_DCTL_INITU2ENA    (1 << 12)
 #define DWC3_DCTL_ACCEPTU2ENA  (1 << 11)
 #define DWC3_DEVTEN_ERRTICERREN                (1 << 9)
 #define DWC3_DEVTEN_SOFEN              (1 << 7)
 #define DWC3_DEVTEN_EOPFEN             (1 << 6)
+#define DWC3_DEVTEN_HIBERNATIONREQEVTEN        (1 << 5)
 #define DWC3_DEVTEN_WKUPEVTEN          (1 << 4)
 #define DWC3_DEVTEN_ULSTCNGEN          (1 << 3)
 #define DWC3_DEVTEN_CONNECTDONEEN      (1 << 2)
 #define DWC3_DEVTEN_DISCONNEVTEN       (1 << 0)
 
 /* Device Status Register */
+#define DWC3_DSTS_DCNRD                        (1 << 29)
+
+/* This applies for core versions 1.87a and earlier */
 #define DWC3_DSTS_PWRUPREQ             (1 << 24)
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DSTS_RSS                  (1 << 25)
+#define DWC3_DSTS_SSS                  (1 << 24)
+
 #define DWC3_DSTS_COREIDLE             (1 << 23)
 #define DWC3_DSTS_DEVCTRLHLT           (1 << 22)
 
 
 #define DWC3_DSTS_RXFIFOEMPTY          (1 << 17)
 
-#define DWC3_DSTS_SOFFN_MASK           (0x3ff << 3)
+#define DWC3_DSTS_SOFFN_MASK           (0x3fff << 3)
 #define DWC3_DSTS_SOFFN(n)             (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
 
 #define DWC3_DSTS_CONNECTSPD           (7 << 0)
 #define DWC3_DGCMD_SET_LMP             0x01
 #define DWC3_DGCMD_SET_PERIODIC_PAR    0x02
 #define DWC3_DGCMD_XMIT_FUNCTION       0x03
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO      0x04
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI      0x05
+
 #define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
 #define DWC3_DGCMD_ALL_FIFO_FLUSH      0x0a
 #define DWC3_DGCMD_SET_ENDPOINT_NRDY   0x0c
 
 #define DWC3_DGCMD_STATUS(n)           (((n) >> 15) & 1)
 #define DWC3_DGCMD_CMDACT              (1 << 10)
+#define DWC3_DGCMD_CMDIOC              (1 << 8)
+
+/* Device Generic Command Parameter Register */
+#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT      (1 << 0)
+#define DWC3_DGCMDPAR_FIFO_NUM(n)              ((n) << 0)
+#define DWC3_DGCMDPAR_RX_FIFO                  (0 << 5)
+#define DWC3_DGCMDPAR_TX_FIFO                  (1 << 5)
+#define DWC3_DGCMDPAR_LOOPBACK_DIS             (0 << 0)
+#define DWC3_DGCMDPAR_LOOPBACK_ENA             (1 << 0)
 
 /* Device Endpoint Command Register */
 #define DWC3_DEPCMD_PARAM_SHIFT                16
 #define DWC3_DEPCMD_STARTTRANSFER      (0x06 << 0)
 #define DWC3_DEPCMD_CLEARSTALL         (0x05 << 0)
 #define DWC3_DEPCMD_SETSTALL           (0x04 << 0)
+/* This applies for core versions 1.90a and earlier */
 #define DWC3_DEPCMD_GETSEQNUMBER       (0x03 << 0)
+/* This applies for core versions 1.94a and later */
+#define DWC3_DEPCMD_GETEPSTATE         (0x03 << 0)
 #define DWC3_DEPCMD_SETTRANSFRESOURCE  (0x02 << 0)
 #define DWC3_DEPCMD_SETEPCONFIG                (0x01 << 0)
 
@@ -361,7 +404,8 @@ struct dwc3_event_buffer {
  * @current_trb: index of current used trb
  * @number: endpoint number (1 - 15)
  * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
- * @res_trans_idx: Resource transfer index
+ * @resource_index: Resource transfer index
+ * @current_uf: Current uf received through last event parameter
  * @interval: the intervall on which the ISOC transfer is started
  * @name: a human readable name e.g. ep1out-bulk
  * @direction: true for TX, false for RX
@@ -385,6 +429,7 @@ struct dwc3_ep {
 #define DWC3_EP_WEDGE          (1 << 2)
 #define DWC3_EP_BUSY           (1 << 4)
 #define DWC3_EP_PENDING_REQUEST        (1 << 5)
+#define DWC3_EP_MISSED_ISOC    (1 << 6)
 
        /* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN                (1 << 31)
@@ -393,7 +438,8 @@ struct dwc3_ep {
 
        u8                      number;
        u8                      type;
-       u8                      res_trans_idx;
+       u8                      resource_index;
+       u16                     current_uf;
        u32                     interval;
 
        char                    name[20];
@@ -437,6 +483,8 @@ enum dwc3_link_state {
        DWC3_LINK_STATE_HRESET          = 0x09,
        DWC3_LINK_STATE_CMPLY           = 0x0a,
        DWC3_LINK_STATE_LPBK            = 0x0b,
+       DWC3_LINK_STATE_RESET           = 0x0e,
+       DWC3_LINK_STATE_RESUME          = 0x0f,
        DWC3_LINK_STATE_MASK            = 0x0f,
 };
 
@@ -450,11 +498,12 @@ enum dwc3_device_state {
 #define DWC3_TRB_SIZE_MASK     (0x00ffffff)
 #define DWC3_TRB_SIZE_LENGTH(n)        ((n) & DWC3_TRB_SIZE_MASK)
 #define DWC3_TRB_SIZE_PCM1(n)  (((n) & 0x03) << 24)
-#define DWC3_TRB_SIZE_TRBSTS(n)        (((n) & (0x0f << 28) >> 28))
+#define DWC3_TRB_SIZE_TRBSTS(n)        (((n) & (0x0f << 28)) >> 28)
 
 #define DWC3_TRBSTS_OK                 0
 #define DWC3_TRBSTS_MISSED_ISOC                1
 #define DWC3_TRBSTS_SETUP_PENDING      2
+#define DWC3_TRB_STS_XFER_IN_PROG      4
 
 /* TRB Control */
 #define DWC3_TRB_CTRL_HWO              (1 << 0)
@@ -543,6 +592,14 @@ struct dwc3_request {
        unsigned                queued:1;
 };
 
+/*
+ * struct dwc3_scratchpad_array - hibernation scratchpad array
+ * (format defined by hw)
+ */
+struct dwc3_scratchpad_array {
+       __le64  dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
+};
+
 /**
  * struct dwc3 - representation of our controller
  * @ctrl_req: usb control request which is used for ep0
@@ -624,8 +681,10 @@ struct dwc3 {
 #define DWC3_REVISION_180A     0x5533180a
 #define DWC3_REVISION_183A     0x5533183a
 #define DWC3_REVISION_185A     0x5533185a
+#define DWC3_REVISION_187A     0x5533187a
 #define DWC3_REVISION_188A     0x5533188a
 #define DWC3_REVISION_190A     0x5533190a
+#define DWC3_REVISION_194A     0x5533194a
 #define DWC3_REVISION_200A     0x5533200a
 #define DWC3_REVISION_202A     0x5533202a
 #define DWC3_REVISION_210A     0x5533210a
index d190301..b8f0038 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/platform_device.h>
 #include <linux/platform_data/dwc3-exynos.h>
 #include <linux/dma-mapping.h>
-#include <linux/module.h>
 #include <linux/clk.h>
 
 #include "core.h"
index 9e8a3dc..9b94886 100644 (file)
@@ -54,7 +54,9 @@
 #include "gadget.h"
 #include "io.h"
 
-static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum);
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+               struct dwc3_ep *dep, struct dwc3_request *req);
 
 static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
 {
@@ -111,7 +113,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
        }
 
        dep->flags |= DWC3_EP_BUSY;
-       dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+       dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
                        dep->number);
 
        dwc->ep0_next_event = DWC3_EP0_COMPLETE;
@@ -150,16 +152,15 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
                        return 0;
                }
 
-               ret = dwc3_ep0_start_trans(dwc, direction,
-                               req->request.dma, req->request.length,
-                               DWC3_TRBCTL_CONTROL_DATA);
+               __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
                dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
                                DWC3_EP0_DIR_IN);
        } else if (dwc->delayed_status) {
                dwc->delayed_status = false;
 
                if (dwc->ep0state == EP0_STATUS_PHASE)
-                       dwc3_ep0_do_control_status(dwc, 1);
+                       __dwc3_ep0_do_control_status(dwc, dwc->eps[1]);
                else
                        dev_dbg(dwc->dev, "too early for delayed status\n");
        }
@@ -224,6 +225,16 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
        dwc3_ep0_out_start(dwc);
 }
 
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+       struct dwc3_ep                  *dep = to_dwc3_ep(ep);
+       struct dwc3                     *dwc = dep->dwc;
+
+       dwc3_ep0_stall_and_restart(dwc);
+
+       return 0;
+}
+
 void dwc3_ep0_out_start(struct dwc3 *dwc)
 {
        int                             ret;
@@ -463,6 +474,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 {
        u32 cfg;
        int ret;
+       u32 reg;
 
        dwc->start_config_issued = false;
        cfg = le16_to_cpu(ctrl->wValue);
@@ -477,6 +489,14 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
                /* if the cfg matches and the cfg is non zero */
                if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
                        dwc->dev_state = DWC3_CONFIGURED_STATE;
+                       /*
+                        * Enable transition to U1/U2 state when
+                        * nothing is pending from application.
+                        */
+                       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                       reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+                       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
                        dwc->resize_fifos = true;
                        dev_dbg(dwc->dev, "resize fifos flag SET\n");
                }
@@ -514,8 +534,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
 
        dwc->u1sel = timing.u1sel;
        dwc->u1pel = timing.u1pel;
-       dwc->u2sel = timing.u2sel;
-       dwc->u2pel = timing.u2pel;
+       dwc->u2sel = le16_to_cpu(timing.u2sel);
+       dwc->u2pel = le16_to_cpu(timing.u2pel);
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        if (reg & DWC3_DCTL_INITU2ENA)
@@ -640,11 +660,11 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
 {
        struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
-       int ret;
+       int ret = -EINVAL;
        u32 len;
 
        if (!dwc->gadget_driver)
-               goto err;
+               goto out;
 
        len = le16_to_cpu(ctrl->wLength);
        if (!len) {
@@ -665,11 +685,9 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
        if (ret == USB_GADGET_DELAYED_STATUS)
                dwc->delayed_status = true;
 
-       if (ret >= 0)
-               return;
-
-err:
-       dwc3_ep0_stall_and_restart(dwc);
+out:
+       if (ret < 0)
+               dwc3_ep0_stall_and_restart(dwc);
 }
 
 static void dwc3_ep0_complete_data(struct dwc3 *dwc,
@@ -723,7 +741,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
        }
 }
 
-static void dwc3_ep0_complete_req(struct dwc3 *dwc,
+static void dwc3_ep0_complete_status(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
 {
        struct dwc3_request     *r;
@@ -745,6 +763,7 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc,
                        dev_dbg(dwc->dev, "Invalid Test #%d\n",
                                        dwc->test_mode_nr);
                        dwc3_ep0_stall_and_restart(dwc);
+                       return;
                }
        }
 
@@ -758,7 +777,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
        struct dwc3_ep          *dep = dwc->eps[event->endpoint_number];
 
        dep->flags &= ~DWC3_EP_BUSY;
-       dep->res_trans_idx = 0;
+       dep->resource_index = 0;
        dwc->setup_packet_pending = false;
 
        switch (dwc->ep0state) {
@@ -774,7 +793,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
 
        case EP0_STATUS_PHASE:
                dev_vdbg(dwc->dev, "Status Phase\n");
-               dwc3_ep0_complete_req(dwc, event);
+               dwc3_ep0_complete_status(dwc, event);
                break;
        default:
                WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state);
@@ -787,68 +806,81 @@ static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
        dwc3_ep0_out_start(dwc);
 }
 
-static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
-               const struct dwc3_event_depevt *event)
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+               struct dwc3_ep *dep, struct dwc3_request *req)
 {
-       struct dwc3_ep          *dep;
-       struct dwc3_request     *req;
        int                     ret;
 
-       dep = dwc->eps[0];
-
-       if (list_empty(&dep->request_list)) {
-               dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
-               dep->flags |= DWC3_EP_PENDING_REQUEST;
-
-               if (event->endpoint_number)
-                       dep->flags |= DWC3_EP0_DIR_IN;
-               return;
-       }
-
-       req = next_request(&dep->request_list);
-       req->direction = !!event->endpoint_number;
+       req->direction = !!dep->number;
 
        if (req->request.length == 0) {
-               ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
+               ret = dwc3_ep0_start_trans(dwc, dep->number,
                                dwc->ctrl_req_addr, 0,
                                DWC3_TRBCTL_CONTROL_DATA);
-       } else if ((req->request.length % dep->endpoint.maxpacket)
-                       && (event->endpoint_number == 0)) {
+       } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
+                       && (dep->number == 0)) {
+               u32             transfer_size;
+
                ret = usb_gadget_map_request(&dwc->gadget, &req->request,
-                               event->endpoint_number);
+                               dep->number);
                if (ret) {
                        dev_dbg(dwc->dev, "failed to map request\n");
                        return;
                }
 
-               WARN_ON(req->request.length > dep->endpoint.maxpacket);
+               WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE);
+
+               transfer_size = roundup(req->request.length,
+                               (u32) dep->endpoint.maxpacket);
 
                dwc->ep0_bounced = true;
 
                /*
-                * REVISIT in case request length is bigger than EP0
-                * wMaxPacketSize, we will need two chained TRBs to handle
-                * the transfer.
+                * REVISIT in case request length is bigger than
+                * DWC3_EP0_BOUNCE_SIZE we will need two chained
+                * TRBs to handle the transfer.
                 */
-               ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
-                               dwc->ep0_bounce_addr, dep->endpoint.maxpacket,
+               ret = dwc3_ep0_start_trans(dwc, dep->number,
+                               dwc->ep0_bounce_addr, transfer_size,
                                DWC3_TRBCTL_CONTROL_DATA);
        } else {
                ret = usb_gadget_map_request(&dwc->gadget, &req->request,
-                               event->endpoint_number);
+                               dep->number);
                if (ret) {
                        dev_dbg(dwc->dev, "failed to map request\n");
                        return;
                }
 
-               ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
-                               req->request.dma, req->request.length,
-                               DWC3_TRBCTL_CONTROL_DATA);
+               ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+                               req->request.length, DWC3_TRBCTL_CONTROL_DATA);
        }
 
        WARN_ON(ret < 0);
 }
 
+static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
+               const struct dwc3_event_depevt *event)
+{
+       struct dwc3_ep          *dep;
+       struct dwc3_request     *req;
+
+       dep = dwc->eps[0];
+
+       if (list_empty(&dep->request_list)) {
+               dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
+               dep->flags |= DWC3_EP_PENDING_REQUEST;
+
+               if (event->endpoint_number)
+                       dep->flags |= DWC3_EP0_DIR_IN;
+               return;
+       }
+
+       req = next_request(&dep->request_list);
+       dep = dwc->eps[event->endpoint_number];
+
+       __dwc3_ep0_do_control_data(dwc, dep, req);
+}
+
 static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
 {
        struct dwc3             *dwc = dep->dwc;
@@ -861,10 +893,8 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
                        dwc->ctrl_req_addr, 0, type);
 }
 
-static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
-       struct dwc3_ep          *dep = dwc->eps[epnum];
-
        if (dwc->resize_fifos) {
                dev_dbg(dwc->dev, "starting to resize fifos\n");
                dwc3_gadget_resize_tx_fifos(dwc);
@@ -874,13 +904,21 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
        WARN_ON(dwc3_ep0_start_control_status(dep));
 }
 
+static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
+               const struct dwc3_event_depevt *event)
+{
+       struct dwc3_ep          *dep = dwc->eps[event->endpoint_number];
+
+       __dwc3_ep0_do_control_status(dwc, dep);
+}
+
 static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
 {
        dwc->setup_packet_pending = true;
 
        /*
-        * This part is very tricky: If we has just handled
+        * This part is very tricky: If we have just handled
         * XferNotReady(Setup) and we're now expecting a
         * XferComplete but, instead, we receive another
         * XferNotReady(Setup), we should STALL and restart
@@ -974,7 +1012,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
                        return;
                }
 
-               dwc3_ep0_do_control_status(dwc, event->endpoint_number);
+               dwc3_ep0_do_control_status(dwc, event);
        }
 }
 
index ec70df7..58fdfad 100644 (file)
@@ -100,6 +100,23 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
        int             retries = 10000;
        u32             reg;
 
+       /*
+        * Wait until device controller is ready. Only applies to 1.94a and
+        * later RTL.
+        */
+       if (dwc->revision >= DWC3_REVISION_194A) {
+               while (--retries) {
+                       reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+                       if (reg & DWC3_DSTS_DCNRD)
+                               udelay(5);
+                       else
+                               break;
+               }
+
+               if (retries <= 0)
+                       return -ETIMEDOUT;
+       }
+
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
 
@@ -107,7 +124,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
        reg |= DWC3_DCTL_ULSTCHNGREQ(state);
        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
 
+       /*
+        * The following code is racy when called from dwc3_gadget_wakeup,
+        * and is not needed, at least on newer versions
+        */
+       if (dwc->revision >= DWC3_REVISION_194A)
+               return 0;
+
        /* wait for a change in DSTS */
+       retries = 10000;
        while (--retries) {
                reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 
@@ -265,8 +290,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
                return "Clear Stall";
        case DWC3_DEPCMD_SETSTALL:
                return "Set Stall";
-       case DWC3_DEPCMD_GETSEQNUMBER:
-               return "Get Data Sequence Number";
+       case DWC3_DEPCMD_GETEPSTATE:
+               return "Get Endpoint State";
        case DWC3_DEPCMD_SETTRANSFRESOURCE:
                return "Set Endpoint Transfer Resource";
        case DWC3_DEPCMD_SETEPCONFIG:
@@ -414,7 +439,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
 
        params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
                | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
-               | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst);
+               | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1);
 
        params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
                | DWC3_DEPCFG_XFER_NOT_READY_EN;
@@ -530,9 +555,37 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
        struct dwc3_request             *req;
 
-       if (!list_empty(&dep->req_queued))
+       if (!list_empty(&dep->req_queued)) {
                dwc3_stop_active_transfer(dwc, dep->number);
 
+               /*
+                * NOTICE: We are violating what the Databook says about the
+                * EndTransfer command. Ideally we would _always_ wait for the
+                * EndTransfer Command Completion IRQ, but that's causing too
+                * much trouble synchronizing between us and gadget driver.
+                *
+                * We have discussed this with the IP Provider and it was
+                * suggested to giveback all requests here, but give HW some
+                * extra time to synchronize with the interconnect. We're using
+                * an arbitraty 100us delay for that.
+                *
+                * Note also that a similar handling was tested by Synopsys
+                * (thanks a lot Paul) and nothing bad has come out of it.
+                * In short, what we're doing is:
+                *
+                * - Issue EndTransfer WITH CMDIOC bit set
+                * - Wait 100us
+                * - giveback all requests to gadget driver
+                */
+               udelay(100);
+
+               while (!list_empty(&dep->req_queued)) {
+                       req = next_request(&dep->req_queued);
+
+                       dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+               }
+       }
+
        while (!list_empty(&dep->request_list)) {
                req = next_request(&dep->request_list);
 
@@ -741,8 +794,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
        case USB_ENDPOINT_XFER_ISOC:
                trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
 
-               /* IOC every DWC3_TRB_NUM / 4 so we can refill */
-               if (!(cur_slot % (DWC3_TRB_NUM / 4)))
+               if (!req->request.no_interrupt)
                        trb->ctrl |= DWC3_TRB_CTRL_IOC;
                break;
 
@@ -958,14 +1010,42 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
        dep->flags |= DWC3_EP_BUSY;
 
        if (start_new) {
-               dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+               dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
                                dep->number);
-               WARN_ON_ONCE(!dep->res_trans_idx);
+               WARN_ON_ONCE(!dep->resource_index);
        }
 
        return 0;
 }
 
+static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
+               struct dwc3_ep *dep, u32 cur_uf)
+{
+       u32 uf;
+
+       if (list_empty(&dep->request_list)) {
+               dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
+                       dep->name);
+               return;
+       }
+
+       /* 4 micro frames in the future */
+       uf = cur_uf + dep->interval * 4;
+
+       __dwc3_gadget_kick_transfer(dep, uf, 1);
+}
+
+static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
+               struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
+{
+       u32 cur_uf, mask;
+
+       mask = ~(dep->interval - 1);
+       cur_uf = event->parameters & mask;
+
+       __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+}
+
 static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 {
        struct dwc3             *dwc = dep->dwc;
@@ -995,11 +1075,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 
        list_add_tail(&req->list, &dep->request_list);
 
-       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY))
-               dep->flags |= DWC3_EP_PENDING_REQUEST;
-
        /*
-        * There are two special cases:
+        * There are a few special cases:
         *
         * 1. XferNotReady with empty list of requests. We need to kick the
         *    transfer here in that situation, otherwise we will be NAKing
@@ -1008,31 +1085,46 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
         *    able to receive the data until the next request is queued.
         *    The following code is handling exactly that.
         *
-        * 2. XferInProgress on Isoc EP with an active transfer. We need to
-        *    kick the transfer here after queuing a request, otherwise the
-        *    core may not see the modified TRB(s).
         */
        if (dep->flags & DWC3_EP_PENDING_REQUEST) {
                int     ret;
-               int     start_trans = 1;
-               u8      trans_idx = dep->res_trans_idx;
 
-               if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
-                               (dep->flags & DWC3_EP_BUSY)) {
-                       start_trans = 0;
-                       WARN_ON_ONCE(!trans_idx);
-               } else {
-                       trans_idx = 0;
+               ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+               if (ret && ret != -EBUSY) {
+                       struct dwc3     *dwc = dep->dwc;
+
+                       dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+                                       dep->name);
                }
+       }
 
-               ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans);
+       /*
+        * 2. XferInProgress on Isoc EP with an active transfer. We need to
+        *    kick the transfer here after queuing a request, otherwise the
+        *    core may not see the modified TRB(s).
+        */
+       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+                       (dep->flags & DWC3_EP_BUSY)) {
+               WARN_ON_ONCE(!dep->resource_index);
+               ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
+                               false);
                if (ret && ret != -EBUSY) {
                        struct dwc3     *dwc = dep->dwc;
 
                        dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
                                        dep->name);
                }
-       };
+       }
+
+       /*
+        * 3. Missed ISOC Handling. We need to start isoc transfer on the saved
+        * uframe number.
+        */
+       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+               (dep->flags & DWC3_EP_MISSED_ISOC)) {
+                       __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
+                       dep->flags &= ~DWC3_EP_MISSED_ISOC;
+       }
 
        return 0;
 }
@@ -1118,15 +1210,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
        memset(&params, 0x00, sizeof(params));
 
        if (value) {
-               if (dep->number == 0 || dep->number == 1) {
-                       /*
-                        * Whenever EP0 is stalled, we will restart
-                        * the state machine, thus moving back to
-                        * Setup Phase
-                        */
-                       dwc->ep0state = EP0_SETUP_PHASE;
-               }
-
                ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
                        DWC3_DEPCMD_SETSTALL, &params);
                if (ret)
@@ -1186,7 +1269,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
        dep->flags |= DWC3_EP_WEDGE;
        spin_unlock_irqrestore(&dwc->lock, flags);
 
-       return dwc3_gadget_ep_set_halt(ep, 1);
+       if (dep->number == 0 || dep->number == 1)
+               return dwc3_gadget_ep0_set_halt(ep, 1);
+       else
+               return dwc3_gadget_ep_set_halt(ep, 1);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -1204,7 +1290,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
        .free_request   = dwc3_gadget_ep_free_request,
        .queue          = dwc3_gadget_ep0_queue,
        .dequeue        = dwc3_gadget_ep_dequeue,
-       .set_halt       = dwc3_gadget_ep_set_halt,
+       .set_halt       = dwc3_gadget_ep0_set_halt,
        .set_wedge      = dwc3_gadget_ep_set_wedge,
 };
 
@@ -1280,9 +1366,13 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
                goto out;
        }
 
-       /* write zeroes to Link Change Request */
-       reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
-       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+       /* Recent versions do this automatically */
+       if (dwc->revision < DWC3_REVISION_194A) {
+               /* write zeroes to Link Change Request */
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+               reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+       }
 
        /* poll until Link State changes to ON */
        timeout = jiffies + msecs_to_jiffies(100);
@@ -1319,16 +1409,21 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
        return 0;
 }
 
-static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
 {
        u32                     reg;
        u32                     timeout = 500;
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        if (is_on) {
-               reg &= ~DWC3_DCTL_TRGTULST_MASK;
-               reg |= (DWC3_DCTL_RUN_STOP
-                               | DWC3_DCTL_TRGTULST_RX_DET);
+               if (dwc->revision <= DWC3_REVISION_187A) {
+                       reg &= ~DWC3_DCTL_TRGTULST_MASK;
+                       reg |= DWC3_DCTL_TRGTULST_RX_DET;
+               }
+
+               if (dwc->revision >= DWC3_REVISION_194A)
+                       reg &= ~DWC3_DCTL_KEEP_CONNECT;
+               reg |= DWC3_DCTL_RUN_STOP;
        } else {
                reg &= ~DWC3_DCTL_RUN_STOP;
        }
@@ -1346,7 +1441,7 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
                }
                timeout--;
                if (!timeout)
-                       break;
+                       return -ETIMEDOUT;
                udelay(1);
        } while (1);
 
@@ -1354,20 +1449,23 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
                        dwc->gadget_driver
                        ? dwc->gadget_driver->function : "no-function",
                        is_on ? "connect" : "disconnect");
+
+       return 0;
 }
 
 static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
 {
        struct dwc3             *dwc = gadget_to_dwc(g);
        unsigned long           flags;
+       int                     ret;
 
        is_on = !!is_on;
 
        spin_lock_irqsave(&dwc->lock, flags);
-       dwc3_gadget_run_stop(dwc, is_on);
+       ret = dwc3_gadget_run_stop(dwc, is_on);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
-       return 0;
+       return ret;
 }
 
 static int dwc3_gadget_start(struct usb_gadget *g,
@@ -1468,6 +1566,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
 
        return 0;
 }
+
 static const struct usb_gadget_ops dwc3_gadget_ops = {
        .get_frame              = dwc3_gadget_get_frame,
        .wakeup                 = dwc3_gadget_wakeup,
@@ -1558,6 +1657,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
        struct dwc3_trb         *trb;
        unsigned int            count;
        unsigned int            s_pkt = 0;
+       unsigned int            trb_status;
 
        do {
                req = next_request(&dep->req_queued);
@@ -1583,9 +1683,18 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
 
                if (dep->direction) {
                        if (count) {
-                               dev_err(dwc->dev, "incomplete IN transfer %s\n",
-                                               dep->name);
-                               status = -ECONNRESET;
+                               trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+                               if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
+                                       dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+                                                       dep->name);
+                                       dep->current_uf = event->parameters &
+                                               ~(dep->interval - 1);
+                                       dep->flags |= DWC3_EP_MISSED_ISOC;
+                               } else {
+                                       dev_err(dwc->dev, "incomplete IN transfer %s\n",
+                                                       dep->name);
+                                       status = -ECONNRESET;
+                               }
                        }
                } else {
                        if (count && (event->status & DEPEVT_STATUS_SHORT))
@@ -1604,7 +1713,8 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
                if (s_pkt)
                        break;
                if ((event->status & DEPEVT_STATUS_LST) &&
-                               (trb->ctrl & DWC3_TRB_CTRL_LST))
+                               (trb->ctrl & (DWC3_TRB_CTRL_LST |
+                                               DWC3_TRB_CTRL_HWO)))
                        break;
                if ((event->status & DEPEVT_STATUS_IOC) &&
                                (trb->ctrl & DWC3_TRB_CTRL_IOC))
@@ -1657,65 +1767,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
        }
 }
 
-static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
-               struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
-{
-       u32 uf, mask;
-
-       if (list_empty(&dep->request_list)) {
-               dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
-                       dep->name);
-               return;
-       }
-
-       mask = ~(dep->interval - 1);
-       uf = event->parameters & mask;
-       /* 4 micro frames in the future */
-       uf += dep->interval * 4;
-
-       __dwc3_gadget_kick_transfer(dep, uf, 1);
-}
-
-static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
-               const struct dwc3_event_depevt *event)
-{
-       struct dwc3 *dwc = dep->dwc;
-       struct dwc3_event_depevt mod_ev = *event;
-
-       /*
-        * We were asked to remove one request. It is possible that this
-        * request and a few others were started together and have the same
-        * transfer index. Since we stopped the complete endpoint we don't
-        * know how many requests were already completed (and not yet)
-        * reported and how could be done (later). We purge them all until
-        * the end of the list.
-        */
-       mod_ev.status = DEPEVT_STATUS_LST;
-       dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN);
-       dep->flags &= ~DWC3_EP_BUSY;
-       /* pending requests are ignored and are queued on XferNotReady */
-}
-
-static void dwc3_ep_cmd_compl(struct dwc3_ep *dep,
-               const struct dwc3_event_depevt *event)
-{
-       u32 param = event->parameters;
-       u32 cmd_type = (param >> 8) & ((1 << 5) - 1);
-
-       switch (cmd_type) {
-       case DWC3_DEPCMD_ENDTRANSFER:
-               dwc3_process_ep_cmd_complete(dep, event);
-               break;
-       case DWC3_DEPCMD_STARTTRANSFER:
-               dep->res_trans_idx = param & 0x7f;
-               break;
-       default:
-               printk(KERN_ERR "%s() unknown /unexpected type: %d\n",
-                               __func__, cmd_type);
-               break;
-       };
-}
-
 static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
 {
@@ -1724,6 +1775,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
        dep = dwc->eps[epnum];
 
+       if (!(dep->flags & DWC3_EP_ENABLED))
+               return;
+
        dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
                        dwc3_ep_event_string(event->endpoint_event));
 
@@ -1734,7 +1788,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
        switch (event->endpoint_event) {
        case DWC3_DEPEVT_XFERCOMPLETE:
-               dep->res_trans_idx = 0;
+               dep->resource_index = 0;
 
                if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
                        dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
@@ -1797,7 +1851,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
                break;
        case DWC3_DEPEVT_EPCMDCMPLT:
-               dwc3_ep_cmd_compl(dep, event);
+               dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
                break;
        }
 }
@@ -1820,16 +1874,16 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
 
        dep = dwc->eps[epnum];
 
-       WARN_ON(!dep->res_trans_idx);
-       if (dep->res_trans_idx) {
-               cmd = DWC3_DEPCMD_ENDTRANSFER;
-               cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
-               cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx);
-               memset(&params, 0, sizeof(params));
-               ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
-               WARN_ON_ONCE(ret);
-               dep->res_trans_idx = 0;
-       }
+       if (!dep->resource_index)
+               return;
+
+       cmd = DWC3_DEPCMD_ENDTRANSFER;
+       cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
+       cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+       memset(&params, 0, sizeof(params));
+       ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
+       WARN_ON_ONCE(ret);
+       dep->resource_index = 0;
 }
 
 static void dwc3_stop_active_transfers(struct dwc3 *dwc)
@@ -1872,11 +1926,9 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
 
 static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
 {
+       int                     reg;
+
        dev_vdbg(dwc->dev, "%s\n", __func__);
-#if 0
-       XXX
-       U1/U2 is powersave optimization. Skip it for now. Anyway we need to
-       enable it before we can disable it.
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_INITU1ENA;
@@ -1884,9 +1936,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
 
        reg &= ~DWC3_DCTL_INITU2ENA;
        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-#endif
 
-       dwc3_stop_active_transfers(dwc);
        dwc3_disconnect_gadget(dwc);
        dwc->start_config_issued = false;
 
@@ -1894,30 +1944,30 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
        dwc->setup_packet_pending = false;
 }
 
-static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
 {
        u32                     reg;
 
        reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
 
-       if (on)
-               reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
-       else
+       if (suspend)
                reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+       else
+               reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
 
        dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
 }
 
-static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend)
 {
        u32                     reg;
 
        reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
 
-       if (on)
-               reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
-       else
+       if (suspend)
                reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+       else
+               reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
 
        dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 }
@@ -1962,16 +2012,18 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
        /* after reset -> Default State */
        dwc->dev_state = DWC3_DEFAULT_STATE;
 
-       /* Enable PHYs */
-       dwc3_gadget_usb2_phy_power(dwc, true);
-       dwc3_gadget_usb3_phy_power(dwc, true);
+       /* Recent versions support automatic phy suspend and don't need this */
+       if (dwc->revision < DWC3_REVISION_194A) {
+               /* Resume PHYs */
+               dwc3_gadget_usb2_phy_suspend(dwc, false);
+               dwc3_gadget_usb3_phy_suspend(dwc, false);
+       }
 
        if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
                dwc3_disconnect_gadget(dwc);
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_TSTCTRL_MASK;
-       reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);
        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
        dwc->test_mode = false;
 
@@ -2010,16 +2062,16 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 }
 
-static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed)
+static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
 {
        switch (speed) {
        case USB_SPEED_SUPER:
-               dwc3_gadget_usb2_phy_power(dwc, false);
+               dwc3_gadget_usb2_phy_suspend(dwc, true);
                break;
        case USB_SPEED_HIGH:
        case USB_SPEED_FULL:
        case USB_SPEED_LOW:
-               dwc3_gadget_usb3_phy_power(dwc, false);
+               dwc3_gadget_usb3_phy_suspend(dwc, true);
                break;
        }
 }
@@ -2082,8 +2134,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
                break;
        }
 
-       /* Disable unneded PHY */
-       dwc3_gadget_disable_phy(dwc, dwc->gadget.speed);
+       /* Recent versions support automatic phy suspend and don't need this */
+       if (dwc->revision < DWC3_REVISION_194A) {
+               /* Suspend unneeded PHY */
+               dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
+       }
 
        dep = dwc->eps[0];
        ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
@@ -2373,10 +2428,6 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
        reg |= DWC3_DCFG_LPM_CAP;
        dwc3_writel(dwc->regs, DWC3_DCFG, reg);
 
-       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
-       reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA;
-       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
        /* Enable all but Start and End of Frame IRQs */
        reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
                        DWC3_DEVTEN_EVNTOVERFLOWEN |
@@ -2389,6 +2440,24 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
                        DWC3_DEVTEN_DISCONNEVTEN);
        dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
 
+       /* Enable USB2 LPM and automatic phy suspend only on recent versions */
+       if (dwc->revision >= DWC3_REVISION_194A) {
+               reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+               reg |= DWC3_DCFG_LPM_CAP;
+               dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+               reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+
+               /* TODO: This should be configurable */
+               reg |= DWC3_DCTL_HIRD_THRES(28);
+
+               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+               dwc3_gadget_usb2_phy_suspend(dwc, false);
+               dwc3_gadget_usb3_phy_suspend(dwc, false);
+       }
+
        ret = device_register(&dwc->gadget.dev);
        if (ret) {
                dev_err(dwc->dev, "failed to register gadget device\n");
index 95ef6a2..99e6d72 100644 (file)
@@ -66,7 +66,12 @@ struct dwc3;
 #define DWC3_DEPCFG_FIFO_NUMBER(n)     ((n) << 17)
 #define DWC3_DEPCFG_BURST_SIZE(n)      ((n) << 22)
 #define DWC3_DEPCFG_DATA_SEQ_NUM(n)    ((n) << 26)
+/* This applies for core versions earlier than 1.94a */
 #define DWC3_DEPCFG_IGN_SEQ_NUM                (1 << 31)
+/* These apply for core versions 1.94a and later */
+#define DWC3_DEPCFG_ACTION_INIT                (0 << 30)
+#define DWC3_DEPCFG_ACTION_RESTORE     (1 << 30)
+#define DWC3_DEPCFG_ACTION_MODIFY      (2 << 30)
 
 /* DEPXFERCFG parameter 0 */
 #define DWC3_DEPXFERCFG_NUM_XFER_RES(n)        ((n) & 0xffff)
@@ -106,6 +111,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
 void dwc3_ep0_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event);
 void dwc3_ep0_out_start(struct dwc3 *dwc);
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
 int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
                gfp_t gfp_flags);
 int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
index 1fc8f12..ee0ebac 100644 (file)
@@ -334,7 +334,7 @@ static int dbgp_control_msg(unsigned devnum, int requesttype,
        int ret;
 
        read = (requesttype & USB_DIR_IN) != 0;
-       if (size > (read ? DBGP_MAX_PACKET:0))
+       if (size > (read ? DBGP_MAX_PACKET : 0))
                return -1;
 
        /* Compute the control message */
index 271ca16..51ab5fd 100644 (file)
@@ -321,6 +321,15 @@ config USB_MV_UDC
          USB2.0 OTG controller, which can be configured as high speed or
          full speed USB peripheral.
 
+config USB_MV_U3D
+       tristate "MARVELL PXA2128 USB 3.0 controller"
+       depends on CPU_MMP3
+       select USB_GADGET_DUALSPEED
+       select USB_GADGET_SUPERSPEED
+       help
+         MARVELL PXA2128 Processor series include a super speed USB3.0 device
+         controller, which support super speed USB peripheral.
+
 #
 # Controllers available in both integrated and discrete versions
 #
index 1811513..3fd8cd0 100644 (file)
@@ -29,6 +29,7 @@ obj-$(CONFIG_USB_EG20T)               += pch_udc.o
 obj-$(CONFIG_USB_MV_UDC)       += mv_udc.o
 mv_udc-y                       := mv_udc_core.o
 obj-$(CONFIG_USB_FUSB300)      += fusb300_udc.o
+obj-$(CONFIG_USB_MV_U3D)       += mv_u3d_core.o
 
 #
 # USB gadget drivers
index fdb7aec..75b8a69 100644 (file)
@@ -235,6 +235,7 @@ static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
 static struct usb_composite_driver acm_ms_driver = {
        .name           = "g_acm_ms",
        .dev            = &device_desc,
+       .max_speed      = USB_SPEED_SUPER,
        .strings        = dev_strings,
        .unbind         = __exit_p(acm_ms_unbind),
 };
index 1a4430f..c9e66df 100644 (file)
@@ -1634,6 +1634,7 @@ static int at91_start(struct usb_gadget *gadget,
        udc = container_of(gadget, struct at91_udc, gadget);
        udc->driver = driver;
        udc->gadget.dev.driver = &driver->driver;
+       udc->gadget.dev.of_node = udc->pdev->dev.of_node;
        dev_set_drvdata(&udc->gadget.dev, &driver->driver);
        udc->enabled = 1;
        udc->selfpowered = 1;
index 390749b..3f72110 100644 (file)
@@ -117,6 +117,7 @@ int config_ep_by_speed(struct usb_gadget *g,
                        struct usb_function *f,
                        struct usb_ep *_ep)
 {
+       struct usb_composite_dev        *cdev = get_gadget_data(g);
        struct usb_endpoint_descriptor *chosen_desc = NULL;
        struct usb_descriptor_header **speed_desc = NULL;
 
@@ -180,10 +181,12 @@ ep_found:
                        _ep->mult = comp_desc->bmAttributes & 0x3;
                case USB_ENDPOINT_XFER_BULK:
                case USB_ENDPOINT_XFER_INT:
-                       _ep->maxburst = comp_desc->bMaxBurst;
+                       _ep->maxburst = comp_desc->bMaxBurst + 1;
                        break;
                default:
-                       /* Do nothing for control endpoints */
+                       if (comp_desc->bMaxBurst != 0)
+                               ERROR(cdev, "ep0 bMaxBurst must be 0\n");
+                       _ep->maxburst = 1;
                        break;
                }
        }
index dcd1c7f..8adc79d 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/blkdev.h>
 #include <linux/pagemap.h>
 #include <linux/export.h>
+#include <linux/hid.h>
 #include <asm/unaligned.h>
 
 #include <linux/usb/composite.h>
@@ -1671,6 +1672,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
        }
                break;
 
+       case HID_DT_HID:
+               pr_vdebug("hid descriptor\n");
+               if (length != sizeof(struct hid_descriptor))
+                       goto inv_length;
+               break;
+
        case USB_DT_OTG:
                if (length != sizeof(struct usb_otg_descriptor))
                        goto inv_length;
index 3b3932c..16a8b1c 100644 (file)
@@ -26,6 +26,12 @@ static struct class *hidg_class;
 /*-------------------------------------------------------------------------*/
 /*                            HID gadget struct                            */
 
+struct f_hidg_req_list {
+       struct usb_request      *req;
+       unsigned int            pos;
+       struct list_head        list;
+};
+
 struct f_hidg {
        /* configuration */
        unsigned char                   bInterfaceSubClass;
@@ -35,10 +41,10 @@ struct f_hidg {
        unsigned short                  report_length;
 
        /* recv report */
-       char                            *set_report_buff;
-       unsigned short                  set_report_length;
+       struct list_head                completed_out_req;
        spinlock_t                      spinlock;
        wait_queue_head_t               read_queue;
+       unsigned int                    qlen;
 
        /* send report */
        struct mutex                    lock;
@@ -49,7 +55,9 @@ struct f_hidg {
        int                             minor;
        struct cdev                     cdev;
        struct usb_function             func;
+
        struct usb_ep                   *in_ep;
+       struct usb_ep                   *out_ep;
 };
 
 static inline struct f_hidg *func_to_hidg(struct usb_function *f)
@@ -65,7 +73,7 @@ static struct usb_interface_descriptor hidg_interface_desc = {
        .bDescriptorType        = USB_DT_INTERFACE,
        /* .bInterfaceNumber    = DYNAMIC */
        .bAlternateSetting      = 0,
-       .bNumEndpoints          = 1,
+       .bNumEndpoints          = 2,
        .bInterfaceClass        = USB_CLASS_HID,
        /* .bInterfaceSubClass  = DYNAMIC */
        /* .bInterfaceProtocol  = DYNAMIC */
@@ -96,10 +104,23 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
                                      */
 };
 
+static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_OUT,
+       .bmAttributes           = USB_ENDPOINT_XFER_INT,
+       /*.wMaxPacketSize       = DYNAMIC */
+       .bInterval              = 4, /* FIXME: Add this field in the
+                                     * HID gadget configuration?
+                                     * (struct hidg_func_descriptor)
+                                     */
+};
+
 static struct usb_descriptor_header *hidg_hs_descriptors[] = {
        (struct usb_descriptor_header *)&hidg_interface_desc,
        (struct usb_descriptor_header *)&hidg_desc,
        (struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
+       (struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
        NULL,
 };
 
@@ -117,10 +138,23 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
                                       */
 };
 
+static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_OUT,
+       .bmAttributes           = USB_ENDPOINT_XFER_INT,
+       /*.wMaxPacketSize       = DYNAMIC */
+       .bInterval              = 10, /* FIXME: Add this field in the
+                                      * HID gadget configuration?
+                                      * (struct hidg_func_descriptor)
+                                      */
+};
+
 static struct usb_descriptor_header *hidg_fs_descriptors[] = {
        (struct usb_descriptor_header *)&hidg_interface_desc,
        (struct usb_descriptor_header *)&hidg_desc,
        (struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
+       (struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
        NULL,
 };
 
@@ -130,9 +164,11 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
 static ssize_t f_hidg_read(struct file *file, char __user *buffer,
                        size_t count, loff_t *ptr)
 {
-       struct f_hidg   *hidg     = file->private_data;
-       char            *tmp_buff = NULL;
-       unsigned long   flags;
+       struct f_hidg *hidg = file->private_data;
+       struct f_hidg_req_list *list;
+       struct usb_request *req;
+       unsigned long flags;
+       int ret;
 
        if (!count)
                return 0;
@@ -142,8 +178,9 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
 
        spin_lock_irqsave(&hidg->spinlock, flags);
 
-#define READ_COND (hidg->set_report_buff != NULL)
+#define READ_COND (!list_empty(&hidg->completed_out_req))
 
+       /* wait for at least one buffer to complete */
        while (!READ_COND) {
                spin_unlock_irqrestore(&hidg->spinlock, flags);
                if (file->f_flags & O_NONBLOCK)
@@ -155,19 +192,34 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
                spin_lock_irqsave(&hidg->spinlock, flags);
        }
 
-
-       count = min_t(unsigned, count, hidg->set_report_length);
-       tmp_buff = hidg->set_report_buff;
-       hidg->set_report_buff = NULL;
-
+       /* pick the first one */
+       list = list_first_entry(&hidg->completed_out_req,
+                               struct f_hidg_req_list, list);
+       req = list->req;
+       count = min_t(unsigned int, count, req->actual - list->pos);
        spin_unlock_irqrestore(&hidg->spinlock, flags);
 
-       if (tmp_buff != NULL) {
-               /* copy to user outside spinlock */
-               count -= copy_to_user(buffer, tmp_buff, count);
-               kfree(tmp_buff);
-       } else
-               count = -ENOMEM;
+       /* copy to user outside spinlock */
+       count -= copy_to_user(buffer, req->buf + list->pos, count);
+       list->pos += count;
+
+       /*
+        * if this request is completely handled and transfered to
+        * userspace, remove its entry from the list and requeue it
+        * again. Otherwise, we will revisit it again upon the next
+        * call, taking into account its current read position.
+        */
+       if (list->pos == req->actual) {
+               spin_lock_irqsave(&hidg->spinlock, flags);
+               list_del(&list->list);
+               kfree(list);
+               spin_unlock_irqrestore(&hidg->spinlock, flags);
+
+               req->length = hidg->report_length;
+               ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
+               if (ret < 0)
+                       return ret;
+       }
 
        return count;
 }
@@ -282,28 +334,37 @@ static int f_hidg_open(struct inode *inode, struct file *fd)
 /*-------------------------------------------------------------------------*/
 /*                                usb_function                             */
 
-static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
+static struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, unsigned length)
 {
-       struct f_hidg *hidg = (struct f_hidg *)req->context;
-
-       if (req->status != 0 || req->buf == NULL || req->actual == 0) {
-               ERROR(hidg->func.config->cdev, "%s FAILED\n", __func__);
-               return;
+       struct usb_request *req;
+
+       req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+       if (req) {
+               req->length = length;
+               req->buf = kmalloc(length, GFP_ATOMIC);
+               if (!req->buf) {
+                       usb_ep_free_request(ep, req);
+                       req = NULL;
+               }
        }
+       return req;
+}
 
-       spin_lock(&hidg->spinlock);
-
-       hidg->set_report_buff = krealloc(hidg->set_report_buff,
-                                        req->actual, GFP_ATOMIC);
+static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_hidg *hidg = (struct f_hidg *) req->context;
+       struct f_hidg_req_list *req_list;
+       unsigned long flags;
 
-       if (hidg->set_report_buff == NULL) {
-               spin_unlock(&hidg->spinlock);
+       req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
+       if (!req_list)
                return;
-       }
-       hidg->set_report_length = req->actual;
-       memcpy(hidg->set_report_buff, req->buf, req->actual);
 
-       spin_unlock(&hidg->spinlock);
+       req_list->req = req;
+
+       spin_lock_irqsave(&hidg->spinlock, flags);
+       list_add_tail(&req_list->list, &hidg->completed_out_req);
+       spin_unlock_irqrestore(&hidg->spinlock, flags);
 
        wake_up(&hidg->read_queue);
 }
@@ -344,9 +405,7 @@ static int hidg_setup(struct usb_function *f,
        case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
                  | HID_REQ_SET_REPORT):
                VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
-               req->context  = hidg;
-               req->complete = hidg_set_report_complete;
-               goto respond;
+               goto stall;
                break;
 
        case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
@@ -403,16 +462,25 @@ respond:
 static void hidg_disable(struct usb_function *f)
 {
        struct f_hidg *hidg = func_to_hidg(f);
+       struct f_hidg_req_list *list, *next;
 
        usb_ep_disable(hidg->in_ep);
        hidg->in_ep->driver_data = NULL;
+
+       usb_ep_disable(hidg->out_ep);
+       hidg->out_ep->driver_data = NULL;
+
+       list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
+               list_del(&list->list);
+               kfree(list);
+       }
 }
 
 static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
        struct usb_composite_dev                *cdev = f->config->cdev;
        struct f_hidg                           *hidg = func_to_hidg(f);
-       int status = 0;
+       int i, status = 0;
 
        VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
 
@@ -429,11 +497,55 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
                }
                status = usb_ep_enable(hidg->in_ep);
                if (status < 0) {
-                       ERROR(cdev, "Enable endpoint FAILED!\n");
+                       ERROR(cdev, "Enable IN endpoint FAILED!\n");
                        goto fail;
                }
                hidg->in_ep->driver_data = hidg;
        }
+
+
+       if (hidg->out_ep != NULL) {
+               /* restart endpoint */
+               if (hidg->out_ep->driver_data != NULL)
+                       usb_ep_disable(hidg->out_ep);
+
+               status = config_ep_by_speed(f->config->cdev->gadget, f,
+                                           hidg->out_ep);
+               if (status) {
+                       ERROR(cdev, "config_ep_by_speed FAILED!\n");
+                       goto fail;
+               }
+               status = usb_ep_enable(hidg->out_ep);
+               if (status < 0) {
+                       ERROR(cdev, "Enable IN endpoint FAILED!\n");
+                       goto fail;
+               }
+               hidg->out_ep->driver_data = hidg;
+
+               /*
+                * allocate a bunch of read buffers and queue them all at once.
+                */
+               for (i = 0; i < hidg->qlen && status == 0; i++) {
+                       struct usb_request *req =
+                                       hidg_alloc_ep_req(hidg->out_ep,
+                                                         hidg->report_length);
+                       if (req) {
+                               req->complete = hidg_set_report_complete;
+                               req->context  = hidg;
+                               status = usb_ep_queue(hidg->out_ep, req,
+                                                     GFP_ATOMIC);
+                               if (status)
+                                       ERROR(cdev, "%s queue req --> %d\n",
+                                               hidg->out_ep->name, status);
+                       } else {
+                               usb_ep_disable(hidg->out_ep);
+                               hidg->out_ep->driver_data = NULL;
+                               status = -ENOMEM;
+                               goto fail;
+                       }
+               }
+       }
+
 fail:
        return status;
 }
@@ -470,13 +582,18 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
        ep->driver_data = c->cdev;      /* claim */
        hidg->in_ep = ep;
 
+       ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
+       if (!ep)
+               goto fail;
+       ep->driver_data = c->cdev;      /* claim */
+       hidg->out_ep = ep;
+
        /* preallocate request and buffer */
        status = -ENOMEM;
        hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
        if (!hidg->req)
                goto fail;
 
-
        hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
        if (!hidg->req->buf)
                goto fail;
@@ -486,12 +603,12 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
        hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
        hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
        hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+       hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+       hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
        hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
        hidg_desc.desc[0].wDescriptorLength =
                cpu_to_le16(hidg->report_desc_length);
 
-       hidg->set_report_buff = NULL;
-
        /* copy descriptors */
        f->descriptors = usb_copy_descriptors(hidg_fs_descriptors);
        if (!f->descriptors)
@@ -500,6 +617,8 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
        if (gadget_is_dualspeed(c->cdev->gadget)) {
                hidg_hs_in_ep_desc.bEndpointAddress =
                        hidg_fs_in_ep_desc.bEndpointAddress;
+               hidg_hs_out_ep_desc.bEndpointAddress =
+                       hidg_fs_out_ep_desc.bEndpointAddress;
                f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors);
                if (!f->hs_descriptors)
                        goto fail;
@@ -509,6 +628,7 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
        spin_lock_init(&hidg->spinlock);
        init_waitqueue_head(&hidg->write_queue);
        init_waitqueue_head(&hidg->read_queue);
+       INIT_LIST_HEAD(&hidg->completed_out_req);
 
        /* create char device */
        cdev_init(&hidg->cdev, &f_hidg_fops);
@@ -553,7 +673,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
        usb_free_descriptors(f->descriptors);
 
        kfree(hidg->report_desc);
-       kfree(hidg->set_report_buff);
        kfree(hidg);
 }
 
@@ -624,6 +743,9 @@ int __init hidg_bind_config(struct usb_configuration *c,
        hidg->func.disable = hidg_disable;
        hidg->func.setup   = hidg_setup;
 
+       /* this could me made configurable at some point */
+       hidg->qlen         = 4;
+
        status = usb_add_function(c, &hidg->func);
        if (status)
                kfree(hidg);
index f67b453..4f1142e 100644 (file)
  * function for a USB device, it also illustrates a technique of
  * double-buffering for increased throughput.
  *
- * Function supports multiple logical units (LUNs).  Backing storage
- * for each LUN is provided by a regular file or a block device.
- * Access for each LUN can be limited to read-only.  Moreover, the
- * function can indicate that LUN is removable and/or CD-ROM.  (The
- * later implies read-only access.)
- *
+ * For more information about MSF and in particular its module
+ * parameters and sysfs interface read the
+ * <Documentation/usb/mass-storage.txt> file.
+ */
+
+/*
  * MSF is configured by specifying a fsg_config structure.  It has the
  * following fields:
  *
  *     ->nofua         Flag specifying that FUA flag in SCSI WRITE(10,12)
  *                             commands for this LUN shall be ignored.
  *
- *     lun_name_format A printf-like format for names of the LUN
- *                             devices.  This determines how the
- *                             directory in sysfs will be named.
- *                             Unless you are using several MSFs in
- *                             a single gadget (as opposed to single
- *                             MSF in many configurations) you may
- *                             leave it as NULL (in which case
- *                             "lun%d" will be used).  In the format
- *                             you can use "%d" to index LUNs for
- *                             MSF's with more than one LUN.  (Beware
- *                             that there is only one integer given
- *                             as an argument for the format and
- *                             specifying invalid format may cause
- *                             unspecified behaviour.)
- *     thread_name     Name of the kernel thread process used by the
- *                             MSF.  You can safely set it to NULL
- *                             (in which case default "file-storage"
- *                             will be used).
- *
  *     vendor_name
  *     product_name
  *     release         Information used as a reply to INQUIRY
  * data track and no audio tracks; hence there need be only one
  * backing file per LUN.
  *
- *
- * MSF includes support for module parameters.  If gadget using it
- * decides to use it, the following module parameters will be
- * available:
- *
- *     file=filename[,filename...]
- *                     Names of the files or block devices used for
- *                             backing storage.
- *     ro=b[,b...]     Default false, boolean for read-only access.
- *     removable=b[,b...]
- *                     Default true, boolean for removable media.
- *     cdrom=b[,b...]  Default false, boolean for whether to emulate
- *                             a CD-ROM drive.
- *     nofua=b[,b...]  Default false, booleans for ignore FUA flag
- *                             in SCSI WRITE(10,12) commands
- *     luns=N          Default N = number of filenames, number of
- *                             LUNs to support.
- *     stall           Default determined according to the type of
- *                             USB device controller (usually true),
- *                             boolean to permit the driver to halt
- *                             bulk endpoints.
- *
- * The module parameters may be prefixed with some string.  You need
- * to consult gadget's documentation or source to verify whether it is
- * using those module parameters and if it does what are the prefixes
- * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is
- * the prefix).
- *
- *
- * Requirements are modest; only a bulk-in and a bulk-out endpoint are
- * needed.  The memory requirement amounts to two 16K buffers, size
- * configurable by a parameter.  Support is included for both
- * full-speed and high-speed operation.
- *
- * Note that the driver is slightly non-portable in that it assumes a
- * single memory/DMA buffer will be useable for bulk-in, bulk-out, and
- * interrupt-in endpoints.  With most device controllers this isn't an
- * issue, but there may be some with hardware restrictions that prevent
- * a buffer from being used by more than one endpoint.
- *
- *
- * The pathnames of the backing files and the ro settings are
- * available in the attribute files "file" and "ro" in the lun<n> (or
- * to be more precise in a directory which name comes from
- * "lun_name_format" option!) subdirectory of the gadget's sysfs
- * directory.  If the "removable" option is set, writing to these
- * files will simulate ejecting/loading the medium (writing an empty
- * line means eject) and adjusting a write-enable tab.  Changes to the
- * ro setting are not allowed when the medium is loaded or if CD-ROM
- * emulation is being used.
- *
- * When a LUN receive an "eject" SCSI request (Start/Stop Unit),
- * if the LUN is removable, the backing file is released to simulate
- * ejection.
- *
- *
  * This function is heavily based on "File-backed Storage Gadget" by
  * Alan Stern which in turn is heavily based on "Gadget Zero" by David
  * Brownell.  The driver's SCSI command interface was based on the
  * In normal operation the main thread is started during the gadget's
  * fsg_bind() callback and stopped during fsg_unbind().  But it can
  * also exit when it receives a signal, and there's no point leaving
- * the gadget running when the thread is dead.  At of this moment, MSF
+ * the gadget running when the thread is dead.  As of this moment, MSF
  * provides no way to deregister the gadget when thread dies -- maybe
  * a callback functions is needed.
  *
@@ -417,9 +342,6 @@ struct fsg_config {
                char nofua;
        } luns[FSG_MAX_LUNS];
 
-       const char              *lun_name_format;
-       const char              *thread_name;
-
        /* Callback functions. */
        const struct fsg_operations     *ops;
        /* Gadget's private data. */
@@ -2687,11 +2609,15 @@ static int fsg_main_thread(void *common_)
 
 /*************************** DEVICE ATTRIBUTES ***************************/
 
-/* Write permission is checked per LUN in store_*() functions. */
 static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
 static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
 static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
 
+static struct device_attribute dev_attr_ro_cdrom =
+       __ATTR(ro, 0444, fsg_show_ro, NULL);
+static struct device_attribute dev_attr_file_nonremovable =
+       __ATTR(file, 0444, fsg_show_file, NULL);
+
 
 /****************************** FSG COMMON ******************************/
 
@@ -2792,11 +2718,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
                curlun->dev.parent = &gadget->dev;
                /* curlun->dev.driver = &fsg_driver.driver; XXX */
                dev_set_drvdata(&curlun->dev, &common->filesem);
-               dev_set_name(&curlun->dev,
-                            cfg->lun_name_format
-                          ? cfg->lun_name_format
-                          : "lun%d",
-                            i);
+               dev_set_name(&curlun->dev, "lun%d", i);
 
                rc = device_register(&curlun->dev);
                if (rc) {
@@ -2806,10 +2728,16 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
                        goto error_release;
                }
 
-               rc = device_create_file(&curlun->dev, &dev_attr_ro);
+               rc = device_create_file(&curlun->dev,
+                                       curlun->cdrom
+                                     ? &dev_attr_ro_cdrom
+                                     : &dev_attr_ro);
                if (rc)
                        goto error_luns;
-               rc = device_create_file(&curlun->dev, &dev_attr_file);
+               rc = device_create_file(&curlun->dev,
+                                       curlun->removable
+                                     ? &dev_attr_file
+                                     : &dev_attr_file_nonremovable);
                if (rc)
                        goto error_luns;
                rc = device_create_file(&curlun->dev, &dev_attr_nofua);
@@ -2878,8 +2806,7 @@ buffhds_first_it:
 
        /* Tell the thread to start working */
        common->thread_task =
-               kthread_create(fsg_main_thread, common,
-                              cfg->thread_name ?: "file-storage");
+               kthread_create(fsg_main_thread, common, "file-storage");
        if (IS_ERR(common->thread_task)) {
                rc = PTR_ERR(common->thread_task);
                goto error_release;
@@ -2945,8 +2872,14 @@ static void fsg_common_release(struct kref *ref)
                /* In error recovery common->nluns may be zero. */
                for (; i; --i, ++lun) {
                        device_remove_file(&lun->dev, &dev_attr_nofua);
-                       device_remove_file(&lun->dev, &dev_attr_ro);
-                       device_remove_file(&lun->dev, &dev_attr_file);
+                       device_remove_file(&lun->dev,
+                                          lun->cdrom
+                                        ? &dev_attr_ro_cdrom
+                                        : &dev_attr_ro);
+                       device_remove_file(&lun->dev,
+                                          lun->removable
+                                        ? &dev_attr_file
+                                        : &dev_attr_file_nonremovable);
                        fsg_lun_close(lun);
                        device_unregister(&lun->dev);
                }
@@ -3167,8 +3100,7 @@ fsg_config_from_params(struct fsg_config *cfg,
        for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
                lun->ro = !!params->ro[i];
                lun->cdrom = !!params->cdrom[i];
-               lun->removable = /* Removable by default */
-                       params->removable_count <= i || params->removable[i];
+               lun->removable = !!params->removable[i];
                lun->filename =
                        params->file_count > i && params->file[i][0]
                        ? params->file[i]
@@ -3176,8 +3108,6 @@ fsg_config_from_params(struct fsg_config *cfg,
        }
 
        /* Let MSF use defaults */
-       cfg->lun_name_format = 0;
-       cfg->thread_name = 0;
        cfg->vendor_name = 0;
        cfg->product_name = 0;
        cfg->release = 0xffff;
@@ -3203,4 +3133,3 @@ fsg_common_from_params(struct fsg_common *common,
        fsg_config_from_params(&cfg, params);
        return fsg_common_init(common, cdev, &cfg);
 }
-
index 2022fe4..2a8bf06 100644 (file)
 
 unsigned int uvc_gadget_trace_param;
 
+/*-------------------------------------------------------------------------*/
+
+/* module parameters specific to the Video streaming endpoint */
+static unsigned streaming_interval = 1;
+module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_interval, "1 - 16");
+
+static unsigned streaming_maxpacket = 1024;
+module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
+
+static unsigned streaming_mult;
+module_param(streaming_mult, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_mult, "0 - 2 (hs/ss only)");
+
+static unsigned streaming_maxburst;
+module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
+
 /* --------------------------------------------------------------------------
  * Function descriptors
  */
@@ -59,6 +78,8 @@ static struct usb_gadget_strings *uvc_function_strings[] = {
 #define UVC_INTF_VIDEO_CONTROL                 0
 #define UVC_INTF_VIDEO_STREAMING               1
 
+#define STATUS_BYTECOUNT                       16      /* 16 bytes status */
+
 static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
        .bLength                = sizeof(uvc_iad),
        .bDescriptorType        = USB_DT_INTERFACE_ASSOCIATION,
@@ -82,12 +103,12 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = {
        .iInterface             = 0,
 };
 
-static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_fs_control_ep __initdata = {
        .bLength                = USB_DT_ENDPOINT_SIZE,
        .bDescriptorType        = USB_DT_ENDPOINT,
        .bEndpointAddress       = USB_DIR_IN,
        .bmAttributes           = USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize         = cpu_to_le16(16),
+       .wMaxPacketSize         = cpu_to_le16(STATUS_BYTECOUNT),
        .bInterval              = 8,
 };
 
@@ -95,7 +116,7 @@ static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
        .bLength                = UVC_DT_CONTROL_ENDPOINT_SIZE,
        .bDescriptorType        = USB_DT_CS_ENDPOINT,
        .bDescriptorSubType     = UVC_EP_INTERRUPT,
-       .wMaxTransferSize       = cpu_to_le16(16),
+       .wMaxTransferSize       = cpu_to_le16(STATUS_BYTECOUNT),
 };
 
 static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
@@ -122,7 +143,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
        .iInterface             = 0,
 };
 
-static struct usb_endpoint_descriptor uvc_streaming_ep = {
+static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
        .bLength                = USB_DT_ENDPOINT_SIZE,
        .bDescriptorType        = USB_DT_ENDPOINT,
        .bEndpointAddress       = USB_DIR_IN,
@@ -131,15 +152,72 @@ static struct usb_endpoint_descriptor uvc_streaming_ep = {
        .bInterval              = 1,
 };
 
+static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize         = cpu_to_le16(1024),
+       .bInterval              = 1,
+};
+
+/* super speed support */
+static struct usb_endpoint_descriptor uvc_ss_control_ep __initdata = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
+       .bInterval =            8,
+};
+
+static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
+       .bLength =              sizeof uvc_ss_control_comp,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 3 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+       .wBytesPerInterval =    cpu_to_le16(STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
+static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
+       .bLength =              sizeof uvc_ss_streaming_comp,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 3 values can be tweaked if necessary */
+       .bMaxBurst =            0,
+       .bmAttributes = 0,
+       .wBytesPerInterval =    cpu_to_le16(1024),
+};
+
 static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
        (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
-       (struct usb_descriptor_header *) &uvc_streaming_ep,
+       (struct usb_descriptor_header *) &uvc_fs_streaming_ep,
        NULL,
 };
 
 static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
        (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
-       (struct usb_descriptor_header *) &uvc_streaming_ep,
+       (struct usb_descriptor_header *) &uvc_hs_streaming_ep,
+       NULL,
+};
+
+static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
+       (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
+       (struct usb_descriptor_header *) &uvc_ss_streaming_ep,
+       (struct usb_descriptor_header *) &uvc_ss_streaming_comp,
        NULL,
 };
 
@@ -215,6 +293,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
        struct uvc_device *uvc = to_uvc(f);
        struct v4l2_event v4l2_event;
        struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+       int ret;
 
        INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
 
@@ -262,7 +341,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
                        return 0;
 
                if (uvc->video.ep) {
-                       uvc->video.ep->desc = &uvc_streaming_ep;
+                       ret = config_ep_by_speed(f->config->cdev->gadget,
+                                       &(uvc->func), uvc->video.ep);
+                       if (ret)
+                               return ret;
                        usb_ep_enable(uvc->video.ep);
                }
 
@@ -368,9 +450,11 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
 {
        struct uvc_input_header_descriptor *uvc_streaming_header;
        struct uvc_header_descriptor *uvc_control_header;
+       const struct uvc_descriptor_header * const *uvc_control_desc;
        const struct uvc_descriptor_header * const *uvc_streaming_cls;
        const struct usb_descriptor_header * const *uvc_streaming_std;
        const struct usb_descriptor_header * const *src;
+       static struct usb_endpoint_descriptor *uvc_control_ep;
        struct usb_descriptor_header **dst;
        struct usb_descriptor_header **hdr;
        unsigned int control_size;
@@ -379,10 +463,29 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
        unsigned int bytes;
        void *mem;
 
-       uvc_streaming_cls = (speed == USB_SPEED_FULL)
-                         ? uvc->desc.fs_streaming : uvc->desc.hs_streaming;
-       uvc_streaming_std = (speed == USB_SPEED_FULL)
-                         ? uvc_fs_streaming : uvc_hs_streaming;
+       switch (speed) {
+       case USB_SPEED_SUPER:
+               uvc_control_desc = uvc->desc.ss_control;
+               uvc_streaming_cls = uvc->desc.ss_streaming;
+               uvc_streaming_std = uvc_ss_streaming;
+               uvc_control_ep = &uvc_ss_control_ep;
+               break;
+
+       case USB_SPEED_HIGH:
+               uvc_control_desc = uvc->desc.fs_control;
+               uvc_streaming_cls = uvc->desc.hs_streaming;
+               uvc_streaming_std = uvc_hs_streaming;
+               uvc_control_ep = &uvc_fs_control_ep;
+               break;
+
+       case USB_SPEED_FULL:
+       default:
+               uvc_control_desc = uvc->desc.fs_control;
+               uvc_streaming_cls = uvc->desc.fs_streaming;
+               uvc_streaming_std = uvc_fs_streaming;
+               uvc_control_ep = &uvc_fs_control_ep;
+               break;
+       }
 
        /* Descriptors layout
         *
@@ -400,16 +503,24 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
        control_size = 0;
        streaming_size = 0;
        bytes = uvc_iad.bLength + uvc_control_intf.bLength
-             + uvc_control_ep.bLength + uvc_control_cs_ep.bLength
+             + uvc_control_ep->bLength + uvc_control_cs_ep.bLength
              + uvc_streaming_intf_alt0.bLength;
-       n_desc = 5;
 
-       for (src = (const struct usb_descriptor_header**)uvc->desc.control; *src; ++src) {
+       if (speed == USB_SPEED_SUPER) {
+               bytes += uvc_ss_control_comp.bLength;
+               n_desc = 6;
+       } else {
+               n_desc = 5;
+       }
+
+       for (src = (const struct usb_descriptor_header **)uvc_control_desc;
+                       *src; ++src) {
                control_size += (*src)->bLength;
                bytes += (*src)->bLength;
                n_desc++;
        }
-       for (src = (const struct usb_descriptor_header**)uvc_streaming_cls; *src; ++src) {
+       for (src = (const struct usb_descriptor_header **)uvc_streaming_cls;
+                       *src; ++src) {
                streaming_size += (*src)->bLength;
                bytes += (*src)->bLength;
                n_desc++;
@@ -433,12 +544,15 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
 
        uvc_control_header = mem;
        UVC_COPY_DESCRIPTORS(mem, dst,
-               (const struct usb_descriptor_header**)uvc->desc.control);
+               (const struct usb_descriptor_header **)uvc_control_desc);
        uvc_control_header->wTotalLength = cpu_to_le16(control_size);
        uvc_control_header->bInCollection = 1;
        uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
 
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep);
+       UVC_COPY_DESCRIPTOR(mem, dst, uvc_control_ep);
+       if (speed == USB_SPEED_SUPER)
+               UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp);
+
        UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
        UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
 
@@ -446,7 +560,8 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
        UVC_COPY_DESCRIPTORS(mem, dst,
                (const struct usb_descriptor_header**)uvc_streaming_cls);
        uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
-       uvc_streaming_header->bEndpointAddress = uvc_streaming_ep.bEndpointAddress;
+       uvc_streaming_header->bEndpointAddress =
+               uvc_fs_streaming_ep.bEndpointAddress;
 
        UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
 
@@ -482,6 +597,7 @@ uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
 
        kfree(f->descriptors);
        kfree(f->hs_descriptors);
+       kfree(f->ss_descriptors);
 
        kfree(uvc);
 }
@@ -496,8 +612,26 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 
        INFO(cdev, "uvc_function_bind\n");
 
+       /* sanity check the streaming endpoint module parameters */
+       if (streaming_interval < 1)
+               streaming_interval = 1;
+       if (streaming_interval > 16)
+               streaming_interval = 16;
+       if (streaming_mult > 2)
+               streaming_mult = 2;
+       if (streaming_maxburst > 15)
+               streaming_maxburst = 15;
+
+       /*
+        * fill in the FS video streaming specific descriptors from the
+        * module parameters
+        */
+       uvc_fs_streaming_ep.wMaxPacketSize = streaming_maxpacket > 1023 ?
+                                               1023 : streaming_maxpacket;
+       uvc_fs_streaming_ep.bInterval = streaming_interval;
+
        /* Allocate endpoints. */
-       ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
+       ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_control_ep);
        if (!ep) {
                INFO(cdev, "Unable to allocate control EP\n");
                goto error;
@@ -505,7 +639,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
        uvc->control_ep = ep;
        ep->driver_data = uvc;
 
-       ep = usb_ep_autoconfig(cdev->gadget, &uvc_streaming_ep);
+       ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
        if (!ep) {
                INFO(cdev, "Unable to allocate streaming EP\n");
                goto error;
@@ -526,9 +660,52 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
        uvc_streaming_intf_alt1.bInterfaceNumber = ret;
        uvc->streaming_intf = ret;
 
-       /* Copy descriptors. */
+       /* sanity check the streaming endpoint module parameters */
+       if (streaming_maxpacket > 1024)
+               streaming_maxpacket = 1024;
+
+       /* Copy descriptors for FS. */
        f->descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
-       f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
+
+       /* support high speed hardware */
+       if (gadget_is_dualspeed(cdev->gadget)) {
+               /*
+                * Fill in the HS descriptors from the module parameters for the
+                * Video Streaming endpoint.
+                * NOTE: We assume that the user knows what they are doing and
+                * won't give parameters that their UDC doesn't support.
+                */
+               uvc_hs_streaming_ep.wMaxPacketSize = streaming_maxpacket;
+               uvc_hs_streaming_ep.wMaxPacketSize |= streaming_mult << 11;
+               uvc_hs_streaming_ep.bInterval = streaming_interval;
+               uvc_hs_streaming_ep.bEndpointAddress =
+                               uvc_fs_streaming_ep.bEndpointAddress;
+
+               /* Copy descriptors. */
+               f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
+       }
+
+       /* support super speed hardware */
+       if (gadget_is_superspeed(c->cdev->gadget)) {
+               /*
+                * Fill in the SS descriptors from the module parameters for the
+                * Video Streaming endpoint.
+                * NOTE: We assume that the user knows what they are doing and
+                * won't give parameters that their UDC doesn't support.
+                */
+               uvc_ss_streaming_ep.wMaxPacketSize = streaming_maxpacket;
+               uvc_ss_streaming_ep.bInterval = streaming_interval;
+               uvc_ss_streaming_comp.bmAttributes = streaming_mult;
+               uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
+               uvc_ss_streaming_comp.wBytesPerInterval =
+                       streaming_maxpacket * (streaming_mult + 1) *
+                       (streaming_maxburst + 1);
+               uvc_ss_streaming_ep.bEndpointAddress =
+                               uvc_fs_streaming_ep.bEndpointAddress;
+
+               /* Copy descriptors. */
+               f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
+       }
 
        /* Preallocate control endpoint request. */
        uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
@@ -583,9 +760,11 @@ error:
  */
 int __init
 uvc_bind_config(struct usb_configuration *c,
-               const struct uvc_descriptor_header * const *control,
+               const struct uvc_descriptor_header * const *fs_control,
+               const struct uvc_descriptor_header * const *ss_control,
                const struct uvc_descriptor_header * const *fs_streaming,
-               const struct uvc_descriptor_header * const *hs_streaming)
+               const struct uvc_descriptor_header * const *hs_streaming,
+               const struct uvc_descriptor_header * const *ss_streaming)
 {
        struct uvc_device *uvc;
        int ret = 0;
@@ -603,38 +782,54 @@ uvc_bind_config(struct usb_configuration *c,
        uvc->state = UVC_STATE_DISCONNECTED;
 
        /* Validate the descriptors. */
-       if (control == NULL || control[0] == NULL ||
-           control[0]->bDescriptorSubType != UVC_VC_HEADER)
+       if (fs_control == NULL || fs_control[0] == NULL ||
+               fs_control[0]->bDescriptorSubType != UVC_VC_HEADER)
                goto error;
 
-       if (fs_streaming == NULL || fs_streaming[0] == NULL ||
-           fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
+       if (ss_control == NULL || ss_control[0] == NULL ||
+               ss_control[0]->bDescriptorSubType != UVC_VC_HEADER)
                goto error;
 
-       if (hs_streaming == NULL || hs_streaming[0] == NULL ||
-           hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
+       if (fs_streaming == NULL || fs_streaming[0] == NULL ||
+               fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
                goto error;
 
-       uvc->desc.control = control;
-       uvc->desc.fs_streaming = fs_streaming;
-       uvc->desc.hs_streaming = hs_streaming;
-
-       /* Allocate string descriptor numbers. */
-       if ((ret = usb_string_id(c->cdev)) < 0)
+       if (hs_streaming == NULL || hs_streaming[0] == NULL ||
+               hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
                goto error;
-       uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = ret;
-       uvc_iad.iFunction = ret;
 
-       if ((ret = usb_string_id(c->cdev)) < 0)
+       if (ss_streaming == NULL || ss_streaming[0] == NULL ||
+               ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
                goto error;
-       uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = ret;
-       uvc_control_intf.iInterface = ret;
 
-       if ((ret = usb_string_id(c->cdev)) < 0)
-               goto error;
-       uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id = ret;
-       uvc_streaming_intf_alt0.iInterface = ret;
-       uvc_streaming_intf_alt1.iInterface = ret;
+       uvc->desc.fs_control = fs_control;
+       uvc->desc.ss_control = ss_control;
+       uvc->desc.fs_streaming = fs_streaming;
+       uvc->desc.hs_streaming = hs_streaming;
+       uvc->desc.ss_streaming = ss_streaming;
+
+       /* maybe allocate device-global string IDs, and patch descriptors */
+       if (uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id == 0) {
+               /* Allocate string descriptor numbers. */
+               ret = usb_string_id(c->cdev);
+               if (ret < 0)
+                       goto error;
+               uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = ret;
+               uvc_iad.iFunction = ret;
+
+               ret = usb_string_id(c->cdev);
+               if (ret < 0)
+                       goto error;
+               uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = ret;
+               uvc_control_intf.iInterface = ret;
+
+               ret = usb_string_id(c->cdev);
+               if (ret < 0)
+                       goto error;
+               uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id = ret;
+               uvc_streaming_intf_alt0.iInterface = ret;
+               uvc_streaming_intf_alt1.iInterface = ret;
+       }
 
        /* Register the function. */
        uvc->func.name = "uvc";
index abf8329..c3d258d 100644 (file)
 #include <linux/usb/video.h>
 
 extern int uvc_bind_config(struct usb_configuration *c,
-                          const struct uvc_descriptor_header * const *control,
-                          const struct uvc_descriptor_header * const *fs_streaming,
-                          const struct uvc_descriptor_header * const *hs_streaming);
+                  const struct uvc_descriptor_header * const *fs_control,
+                  const struct uvc_descriptor_header * const *hs_control,
+                  const struct uvc_descriptor_header * const *fs_streaming,
+                  const struct uvc_descriptor_header * const *hs_streaming,
+                  const struct uvc_descriptor_header * const *ss_streaming);
 
 #endif /* _F_UVC_H_ */
 
index dcbc0a2..1b0f086 100644 (file)
@@ -21,7 +21,8 @@
 #include <mach/hardware.h>
 
 static struct clk *mxc_ahb_clk;
-static struct clk *mxc_usb_clk;
+static struct clk *mxc_per_clk;
+static struct clk *mxc_ipg_clk;
 
 /* workaround ENGcm09152 for i.MX35 */
 #define USBPHYCTRL_OTGBASE_OFFSET      0x608
@@ -35,28 +36,31 @@ int fsl_udc_clk_init(struct platform_device *pdev)
 
        pdata = pdev->dev.platform_data;
 
-       if (!cpu_is_mx35() && !cpu_is_mx25()) {
-               mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb");
-               if (IS_ERR(mxc_ahb_clk))
-                       return PTR_ERR(mxc_ahb_clk);
+       mxc_ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+       if (IS_ERR(mxc_ipg_clk)) {
+               dev_err(&pdev->dev, "clk_get(\"ipg\") failed\n");
+               return PTR_ERR(mxc_ipg_clk);
+       }
 
-               ret = clk_enable(mxc_ahb_clk);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n");
-                       goto eenahb;
-               }
+       mxc_ahb_clk = devm_clk_get(&pdev->dev, "ahb");
+       if (IS_ERR(mxc_ahb_clk)) {
+               dev_err(&pdev->dev, "clk_get(\"ahb\") failed\n");
+               return PTR_ERR(mxc_ahb_clk);
        }
 
-       /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
-       mxc_usb_clk = clk_get(&pdev->dev, "usb");
-       if (IS_ERR(mxc_usb_clk)) {
-               dev_err(&pdev->dev, "clk_get(\"usb\") failed\n");
-               ret = PTR_ERR(mxc_usb_clk);
-               goto egusb;
+       mxc_per_clk = devm_clk_get(&pdev->dev, "per");
+       if (IS_ERR(mxc_per_clk)) {
+               dev_err(&pdev->dev, "clk_get(\"per\") failed\n");
+               return PTR_ERR(mxc_per_clk);
        }
 
+       clk_prepare_enable(mxc_ipg_clk);
+       clk_prepare_enable(mxc_ahb_clk);
+       clk_prepare_enable(mxc_per_clk);
+
+       /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
        if (!cpu_is_mx51()) {
-               freq = clk_get_rate(mxc_usb_clk);
+               freq = clk_get_rate(mxc_per_clk);
                if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
                    (freq < 59999000 || freq > 60001000)) {
                        dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
@@ -65,24 +69,13 @@ int fsl_udc_clk_init(struct platform_device *pdev)
                }
        }
 
-       ret = clk_enable(mxc_usb_clk);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n");
-               goto eenusb;
-       }
-
        return 0;
 
-eenusb:
 eclkrate:
-       clk_put(mxc_usb_clk);
-       mxc_usb_clk = NULL;
-egusb:
-       if (!cpu_is_mx35())
-               clk_disable(mxc_ahb_clk);
-eenahb:
-       if (!cpu_is_mx35())
-               clk_put(mxc_ahb_clk);
+       clk_disable_unprepare(mxc_ipg_clk);
+       clk_disable_unprepare(mxc_ahb_clk);
+       clk_disable_unprepare(mxc_per_clk);
+       mxc_per_clk = NULL;
        return ret;
 }
 
@@ -104,20 +97,15 @@ void fsl_udc_clk_finalize(struct platform_device *pdev)
 
        /* ULPI transceivers don't need usbpll */
        if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
-               clk_disable(mxc_usb_clk);
-               clk_put(mxc_usb_clk);
-               mxc_usb_clk = NULL;
+               clk_disable_unprepare(mxc_per_clk);
+               mxc_per_clk = NULL;
        }
 }
 
 void fsl_udc_clk_release(void)
 {
-       if (mxc_usb_clk) {
-               clk_disable(mxc_usb_clk);
-               clk_put(mxc_usb_clk);
-       }
-       if (!cpu_is_mx35()) {
-               clk_disable(mxc_ahb_clk);
-               clk_put(mxc_ahb_clk);
-       }
+       if (mxc_per_clk)
+               clk_disable_unprepare(mxc_per_clk);
+       clk_disable_unprepare(mxc_ahb_clk);
+       clk_disable_unprepare(mxc_ipg_clk);
 }
index bc6f9bb..3def828 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/ioport.h>
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/list.h>
@@ -1229,7 +1230,7 @@ static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
        struct fsl_udc *udc;
 
        udc = container_of(gadget, struct fsl_udc, gadget);
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                return usb_phy_set_power(udc->transceiver, mA);
        return -ENOTSUPP;
 }
@@ -1983,13 +1984,13 @@ static int fsl_start(struct usb_gadget_driver *driver,
                goto out;
        }
 
-       if (udc_controller->transceiver) {
+       if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
                /* Suspend the controller until OTG enable it */
                udc_controller->stopped = 1;
                printk(KERN_INFO "Suspend udc for OTG auto detect\n");
 
                /* connect to bus through transceiver */
-               if (udc_controller->transceiver) {
+               if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
                        retval = otg_set_peripheral(
                                        udc_controller->transceiver->otg,
                                                    &udc_controller->gadget);
@@ -2030,7 +2031,7 @@ static int fsl_stop(struct usb_gadget_driver *driver)
        if (!driver || driver != udc_controller->driver || !driver->unbind)
                return -EINVAL;
 
-       if (udc_controller->transceiver)
+       if (!IS_ERR_OR_NULL(udc_controller->transceiver))
                otg_set_peripheral(udc_controller->transceiver->otg, NULL);
 
        /* stop DR, disable intr */
@@ -2455,8 +2456,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
 
 #ifdef CONFIG_USB_OTG
        if (pdata->operating_mode == FSL_USB2_DR_OTG) {
-               udc_controller->transceiver = usb_get_transceiver();
-               if (!udc_controller->transceiver) {
+               udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+               if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
                        ERR("Can't find OTG driver!\n");
                        ret = -ENODEV;
                        goto err_kfree;
@@ -2540,7 +2541,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
                goto err_free_irq;
        }
 
-       if (!udc_controller->transceiver) {
+       if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
                /* initialize usb hw reg except for regs for EP,
                 * leave usbintr reg untouched */
                dr_controller_setup(udc_controller);
@@ -2560,11 +2561,12 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
        dev_set_name(&udc_controller->gadget.dev, "gadget");
        udc_controller->gadget.dev.release = fsl_udc_release;
        udc_controller->gadget.dev.parent = &pdev->dev;
+       udc_controller->gadget.dev.of_node = pdev->dev.of_node;
        ret = device_register(&udc_controller->gadget.dev);
        if (ret < 0)
                goto err_free_irq;
 
-       if (udc_controller->transceiver)
+       if (!IS_ERR_OR_NULL(udc_controller->transceiver))
                udc_controller->gadget.is_otg = 1;
 
        /* setup QH and epctrl for ep0 */
index 54034f8..dc53348 100644 (file)
@@ -1432,7 +1432,7 @@ static int __init imx_udc_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "can't get USB clock\n");
                goto fail2;
        }
-       clk_enable(clk);
+       clk_prepare_enable(clk);
 
        if (clk_get_rate(clk) != 48000000) {
                D_INI(&pdev->dev,
@@ -1496,7 +1496,7 @@ fail4:
                free_irq(imx_usb->usbd_int[i], imx_usb);
 fail3:
        clk_put(clk);
-       clk_disable(clk);
+       clk_disable_unprepare(clk);
 fail2:
        iounmap(base);
 fail1:
@@ -1521,7 +1521,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev)
                free_irq(imx_usb->usbd_int[i], imx_usb);
 
        clk_put(imx_usb->clk);
-       clk_disable(imx_usb->clk);
+       clk_disable_unprepare(imx_usb->clk);
        iounmap(imx_usb->base);
 
        release_mem_region(imx_usb->res->start, resource_size(imx_usb->res));
index 2ab0388..f1ec99e 100644 (file)
@@ -165,6 +165,7 @@ struct lpc32xx_udc {
        int                     udp_irq[4];
        struct clk              *usb_pll_clk;
        struct clk              *usb_slv_clk;
+       struct clk              *usb_otg_clk;
 
        /* DMA support */
        u32                     *udca_v_base;
@@ -227,33 +228,15 @@ static inline struct lpc32xx_udc *to_udc(struct usb_gadget *g)
 #define UDCA_BUFF_SIZE (128)
 
 /* TODO: When the clock framework is introduced in LPC32xx, IO_ADDRESS will
- * be replaced with an inremap()ed pointer, see USB_OTG_CLK_CTRL()
+ * be replaced with an inremap()ed pointer
  * */
 #define USB_CTRL               IO_ADDRESS(LPC32XX_CLK_PM_BASE + 0x64)
-#define USB_CLOCK_MASK         (AHB_M_CLOCK_ON | OTG_CLOCK_ON | \
-                                DEV_CLOCK_ON | I2C_CLOCK_ON)
 
 /* USB_CTRL bit defines */
 #define USB_SLAVE_HCLK_EN      (1 << 24)
 #define USB_HOST_NEED_CLK_EN   (1 << 21)
 #define USB_DEV_NEED_CLK_EN    (1 << 22)
 
-#define USB_OTG_CLK_CTRL(udc)  ((udc)->udp_baseaddr + 0xFF4)
-#define USB_OTG_CLK_STAT(udc)  ((udc)->udp_baseaddr + 0xFF8)
-
-/* USB_OTG_CLK_CTRL bit defines */
-#define AHB_M_CLOCK_ON         (1 << 4)
-#define OTG_CLOCK_ON           (1 << 3)
-#define I2C_CLOCK_ON           (1 << 2)
-#define DEV_CLOCK_ON           (1 << 1)
-#define HOST_CLOCK_ON          (1 << 0)
-
-#define USB_OTG_STAT_CONTROL(udc) (udc->udp_baseaddr + 0x110)
-
-/* USB_OTG_STAT_CONTROL bit defines */
-#define TRANSPARENT_I2C_EN     (1 << 7)
-#define HOST_EN                        (1 << 0)
-
 /**********************************************************************
  * USB device controller register offsets
  **********************************************************************/
@@ -677,7 +660,7 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc)
                ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD);
 
        /* Enable usb_need_clk clock after transceiver is initialized */
-       writel((readl(USB_CTRL) | (1 << 22)), USB_CTRL);
+       writel((readl(USB_CTRL) | USB_DEV_NEED_CLK_EN), USB_CTRL);
 
        dev_info(udc->dev, "ISP1301 Vendor ID  : 0x%04x\n",
                 i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00));
@@ -1010,11 +993,8 @@ static void udc_dd_free(struct lpc32xx_udc *udc, struct lpc32xx_usbd_dd_gad *dd)
 /* Enables or disables most of the USB system clocks when low power mode is
  * needed. Clocks are typically started on a connection event, and disabled
  * when a cable is disconnected */
-#define OTGOFF_CLK_MASK (AHB_M_CLOCK_ON | I2C_CLOCK_ON)
 static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
 {
-       int to = 1000;
-
        if (enable != 0) {
                if (udc->clocked)
                        return;
@@ -1028,14 +1008,7 @@ static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
                writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN,
                             USB_CTRL);
 
-               /* Set to enable all needed USB OTG clocks */
-               writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL(udc));
-
-               while (((readl(USB_OTG_CLK_STAT(udc)) & USB_CLOCK_MASK) !=
-                       USB_CLOCK_MASK) && (to > 0))
-                       to--;
-               if (!to)
-                       dev_dbg(udc->dev, "Cannot enable USB OTG clocking\n");
+               clk_enable(udc->usb_otg_clk);
        } else {
                if (!udc->clocked)
                        return;
@@ -1047,19 +1020,11 @@ static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
                /* 48MHz PLL dpwn */
                clk_disable(udc->usb_pll_clk);
 
-               /* Enable the USB device clock */
+               /* Disable the USB device clock */
                writel(readl(USB_CTRL) & ~USB_DEV_NEED_CLK_EN,
                             USB_CTRL);
 
-               /* Set to enable all needed USB OTG clocks */
-               writel(OTGOFF_CLK_MASK, USB_OTG_CLK_CTRL(udc));
-
-               while (((readl(USB_OTG_CLK_STAT(udc)) &
-                        OTGOFF_CLK_MASK) !=
-                       OTGOFF_CLK_MASK) && (to > 0))
-                       to--;
-               if (!to)
-                       dev_dbg(udc->dev, "Cannot disable USB OTG clocking\n");
+               clk_disable(udc->usb_otg_clk);
        }
 }
 
@@ -3041,6 +3006,7 @@ static int lpc32xx_start(struct usb_gadget_driver *driver,
 
        udc->driver = driver;
        udc->gadget.dev.driver = &driver->driver;
+       udc->gadget.dev.of_node = udc->dev->of_node;
        udc->enabled = 1;
        udc->selfpowered = 1;
        udc->vbus = 0;
@@ -3239,6 +3205,12 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
                retval = PTR_ERR(udc->usb_slv_clk);
                goto usb_clk_get_fail;
        }
+       udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
+       if (IS_ERR(udc->usb_otg_clk)) {
+               dev_err(udc->dev, "failed to acquire USB otg clock\n");
+               retval = PTR_ERR(udc->usb_slv_clk);
+               goto usb_otg_clk_get_fail;
+       }
 
        /* Setup PLL clock to 48MHz */
        retval = clk_enable(udc->usb_pll_clk);
@@ -3262,15 +3234,12 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
                goto usb_clk_enable_fail;
        }
 
-       /* Set to enable all needed USB OTG clocks */
-       writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL(udc));
-
-       i = 1000;
-       while (((readl(USB_OTG_CLK_STAT(udc)) & USB_CLOCK_MASK) !=
-               USB_CLOCK_MASK) && (i > 0))
-               i--;
-       if (!i)
-               dev_dbg(udc->dev, "USB OTG clocks not correctly enabled\n");
+       /* Enable USB OTG clock */
+       retval = clk_enable(udc->usb_otg_clk);
+       if (retval < 0) {
+               dev_err(udc->dev, "failed to start USB otg clock\n");
+               goto usb_otg_clk_enable_fail;
+       }
 
        /* Setup deferred workqueue data */
        udc->poweron = udc->pullup = 0;
@@ -3390,12 +3359,16 @@ dma_alloc_fail:
        dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
                          udc->udca_v_base, udc->udca_p_base);
 i2c_fail:
+       clk_disable(udc->usb_otg_clk);
+usb_otg_clk_enable_fail:
        clk_disable(udc->usb_slv_clk);
 usb_clk_enable_fail:
 pll_set_fail:
        clk_disable(udc->usb_pll_clk);
 pll_enable_fail:
        clk_put(udc->usb_slv_clk);
+usb_otg_clk_get_fail:
+       clk_put(udc->usb_otg_clk);
 usb_clk_get_fail:
        clk_put(udc->usb_pll_clk);
 pll_get_fail:
@@ -3433,6 +3406,8 @@ static int __devexit lpc32xx_udc_remove(struct platform_device *pdev)
 
        device_unregister(&udc->gadget.dev);
 
+       clk_disable(udc->usb_otg_clk);
+       clk_put(udc->usb_otg_clk);
        clk_disable(udc->usb_slv_clk);
        clk_put(udc->usb_slv_clk);
        clk_disable(udc->usb_pll_clk);
@@ -3446,7 +3421,6 @@ static int __devexit lpc32xx_udc_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
-       int to = 1000;
        struct lpc32xx_udc *udc = platform_get_drvdata(pdev);
 
        if (udc->clocked) {
@@ -3461,15 +3435,6 @@ static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg)
                   on resume */
                udc->clocked = 1;
 
-               /* Kill OTG and I2C clocks */
-               writel(0, USB_OTG_CLK_CTRL(udc));
-               while (((readl(USB_OTG_CLK_STAT(udc)) & OTGOFF_CLK_MASK) !=
-                       OTGOFF_CLK_MASK) && (to > 0))
-                       to--;
-               if (!to)
-                       dev_dbg(udc->dev,
-                               "USB OTG clocks not correctly enabled\n");
-
                /* Kill global USB clock */
                clk_disable(udc->usb_slv_clk);
        }
diff --git a/drivers/usb/gadget/mv_u3d.h b/drivers/usb/gadget/mv_u3d.h
new file mode 100644 (file)
index 0000000..e32a787
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef __MV_U3D_H
+#define __MV_U3D_H
+
+#define MV_U3D_EP_CONTEXT_ALIGNMENT    32
+#define MV_U3D_TRB_ALIGNMENT   16
+#define MV_U3D_DMA_BOUNDARY    4096
+#define MV_U3D_EP0_MAX_PKT_SIZE        512
+
+/* ep0 transfer state */
+#define MV_U3D_WAIT_FOR_SETUP          0
+#define MV_U3D_DATA_STATE_XMIT         1
+#define MV_U3D_DATA_STATE_NEED_ZLP     2
+#define MV_U3D_WAIT_FOR_OUT_STATUS     3
+#define MV_U3D_DATA_STATE_RECV         4
+#define MV_U3D_STATUS_STAGE            5
+
+#define MV_U3D_EP_MAX_LENGTH_TRANSFER  0x10000
+
+/* USB3 Interrupt Status */
+#define MV_U3D_USBINT_SETUP            0x00000001
+#define MV_U3D_USBINT_RX_COMPLETE      0x00000002
+#define MV_U3D_USBINT_TX_COMPLETE      0x00000004
+#define MV_U3D_USBINT_UNDER_RUN        0x00000008
+#define MV_U3D_USBINT_RXDESC_ERR       0x00000010
+#define MV_U3D_USBINT_TXDESC_ERR       0x00000020
+#define MV_U3D_USBINT_RX_TRB_COMPLETE  0x00000040
+#define MV_U3D_USBINT_TX_TRB_COMPLETE  0x00000080
+#define MV_U3D_USBINT_VBUS_VALID       0x00010000
+#define MV_U3D_USBINT_STORAGE_CMD_FULL 0x00020000
+#define MV_U3D_USBINT_LINK_CHG         0x01000000
+
+/* USB3 Interrupt Enable */
+#define MV_U3D_INTR_ENABLE_SETUP               0x00000001
+#define MV_U3D_INTR_ENABLE_RX_COMPLETE         0x00000002
+#define MV_U3D_INTR_ENABLE_TX_COMPLETE         0x00000004
+#define MV_U3D_INTR_ENABLE_UNDER_RUN           0x00000008
+#define MV_U3D_INTR_ENABLE_RXDESC_ERR          0x00000010
+#define MV_U3D_INTR_ENABLE_TXDESC_ERR          0x00000020
+#define MV_U3D_INTR_ENABLE_RX_TRB_COMPLETE     0x00000040
+#define MV_U3D_INTR_ENABLE_TX_TRB_COMPLETE     0x00000080
+#define MV_U3D_INTR_ENABLE_RX_BUFFER_ERR       0x00000100
+#define MV_U3D_INTR_ENABLE_VBUS_VALID          0x00010000
+#define MV_U3D_INTR_ENABLE_STORAGE_CMD_FULL    0x00020000
+#define MV_U3D_INTR_ENABLE_LINK_CHG            0x01000000
+#define MV_U3D_INTR_ENABLE_PRIME_STATUS        0x02000000
+
+/* USB3 Link Change */
+#define MV_U3D_LINK_CHANGE_LINK_UP             0x00000001
+#define MV_U3D_LINK_CHANGE_SUSPEND             0x00000002
+#define MV_U3D_LINK_CHANGE_RESUME              0x00000004
+#define MV_U3D_LINK_CHANGE_WRESET              0x00000008
+#define MV_U3D_LINK_CHANGE_HRESET              0x00000010
+#define MV_U3D_LINK_CHANGE_VBUS_INVALID        0x00000020
+#define MV_U3D_LINK_CHANGE_INACT               0x00000040
+#define MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0    0x00000080
+#define MV_U3D_LINK_CHANGE_U1                  0x00000100
+#define MV_U3D_LINK_CHANGE_U2                  0x00000200
+#define MV_U3D_LINK_CHANGE_U3                  0x00000400
+
+/* bridge setting */
+#define MV_U3D_BRIDGE_SETTING_VBUS_VALID       (1 << 16)
+
+/* Command Register Bit Masks */
+#define MV_U3D_CMD_RUN_STOP            0x00000001
+#define MV_U3D_CMD_CTRL_RESET          0x00000002
+
+/* ep control register */
+#define MV_U3D_EPXCR_EP_TYPE_CONTROL           0
+#define MV_U3D_EPXCR_EP_TYPE_ISOC              1
+#define MV_U3D_EPXCR_EP_TYPE_BULK              2
+#define MV_U3D_EPXCR_EP_TYPE_INT               3
+#define MV_U3D_EPXCR_EP_ENABLE_SHIFT           4
+#define MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT      12
+#define MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT     16
+#define MV_U3D_USB_BULK_BURST_OUT              6
+#define MV_U3D_USB_BULK_BURST_IN               14
+
+#define MV_U3D_EPXCR_EP_FLUSH          (1 << 7)
+#define MV_U3D_EPXCR_EP_HALT           (1 << 1)
+#define MV_U3D_EPXCR_EP_INIT           (1)
+
+/* TX/RX Status Register */
+#define MV_U3D_XFERSTATUS_COMPLETE_SHIFT       24
+#define MV_U3D_COMPLETE_INVALID        0
+#define MV_U3D_COMPLETE_SUCCESS        1
+#define MV_U3D_COMPLETE_BUFF_ERR       2
+#define MV_U3D_COMPLETE_SHORT_PACKET   3
+#define MV_U3D_COMPLETE_TRB_ERR        5
+#define MV_U3D_XFERSTATUS_TRB_LENGTH_MASK      (0xFFFFFF)
+
+#define MV_U3D_USB_LINK_BYPASS_VBUS    0x8
+
+#define MV_U3D_LTSSM_PHY_INIT_DONE             0x80000000
+#define MV_U3D_LTSSM_NEVER_GO_COMPLIANCE       0x40000000
+
+#define MV_U3D_USB3_OP_REGS_OFFSET     0x100
+#define MV_U3D_USB3_PHY_OFFSET         0xB800
+
+#define DCS_ENABLE     0x1
+
+/* timeout */
+#define MV_U3D_RESET_TIMEOUT           10000
+#define MV_U3D_FLUSH_TIMEOUT           100000
+#define MV_U3D_OWN_TIMEOUT             10000
+#define LOOPS_USEC_SHIFT       4
+#define LOOPS_USEC             (1 << LOOPS_USEC_SHIFT)
+#define LOOPS(timeout)         ((timeout) >> LOOPS_USEC_SHIFT)
+
+/* ep direction */
+#define MV_U3D_EP_DIR_IN               1
+#define MV_U3D_EP_DIR_OUT              0
+#define mv_u3d_ep_dir(ep)      (((ep)->ep_num == 0) ? \
+                               ((ep)->u3d->ep0_dir) : ((ep)->direction))
+
+/* usb capability registers */
+struct mv_u3d_cap_regs {
+       u32     rsvd[5];
+       u32     dboff;  /* doorbell register offset */
+       u32     rtsoff; /* runtime register offset */
+       u32     vuoff;  /* vendor unique register offset */
+};
+
+/* operation registers */
+struct mv_u3d_op_regs {
+       u32     usbcmd;         /* Command register */
+       u32     rsvd1[11];
+       u32     dcbaapl;        /* Device Context Base Address low register */
+       u32     dcbaaph;        /* Device Context Base Address high register */
+       u32     rsvd2[243];
+       u32     portsc;         /* port status and control register*/
+       u32     portlinkinfo;   /* port link info register*/
+       u32     rsvd3[9917];
+       u32     doorbell;       /* doorbell register */
+};
+
+/* control enpoint enable registers */
+struct epxcr {
+       u32     epxoutcr0;      /* ep out control 0 register */
+       u32     epxoutcr1;      /* ep out control 1 register */
+       u32     epxincr0;       /* ep in control 0 register */
+       u32     epxincr1;       /* ep in control 1 register */
+};
+
+/* transfer status registers */
+struct xferstatus {
+       u32     curdeqlo;       /* current TRB pointer low */
+       u32     curdeqhi;       /* current TRB pointer high */
+       u32     statuslo;       /* transfer status low */
+       u32     statushi;       /* transfer status high */
+};
+
+/* vendor unique control registers */
+struct mv_u3d_vuc_regs {
+       u32     ctrlepenable;   /* control endpoint enable register */
+       u32     setuplock;      /* setup lock register */
+       u32     endcomplete;    /* endpoint transfer complete register */
+       u32     intrcause;      /* interrupt cause register */
+       u32     intrenable;     /* interrupt enable register */
+       u32     trbcomplete;    /* TRB complete register */
+       u32     linkchange;     /* link change register */
+       u32     rsvd1[5];
+       u32     trbunderrun;    /* TRB underrun register */
+       u32     rsvd2[43];
+       u32     bridgesetting;  /* bridge setting register */
+       u32     rsvd3[7];
+       struct xferstatus       txst[16];       /* TX status register */
+       struct xferstatus       rxst[16];       /* RX status register */
+       u32     ltssm;          /* LTSSM control register */
+       u32     pipe;           /* PIPE control register */
+       u32     linkcr0;        /* link control 0 register */
+       u32     linkcr1;        /* link control 1 register */
+       u32     rsvd6[60];
+       u32     mib0;           /* MIB0 counter register */
+       u32     usblink;        /* usb link control register */
+       u32     ltssmstate;     /* LTSSM state register */
+       u32     linkerrorcause; /* link error cause register */
+       u32     rsvd7[60];
+       u32     devaddrtiebrkr; /* device address and tie breaker */
+       u32     itpinfo0;       /* ITP info 0 register */
+       u32     itpinfo1;       /* ITP info 1 register */
+       u32     rsvd8[61];
+       struct epxcr    epcr[16];       /* ep control register */
+       u32     rsvd9[64];
+       u32     phyaddr;        /* PHY address register */
+       u32     phydata;        /* PHY data register */
+};
+
+/* Endpoint context structure */
+struct mv_u3d_ep_context {
+       u32     rsvd0;
+       u32     rsvd1;
+       u32     trb_addr_lo;            /* TRB address low 32 bit */
+       u32     trb_addr_hi;            /* TRB address high 32 bit */
+       u32     rsvd2;
+       u32     rsvd3;
+       struct usb_ctrlrequest setup_buffer;    /* setup data buffer */
+};
+
+/* TRB control data structure */
+struct mv_u3d_trb_ctrl {
+       u32     own:1;          /* owner of TRB */
+       u32     rsvd1:3;
+       u32     chain:1;        /* associate this TRB with the
+                               next TRB on the Ring */
+       u32     ioc:1;          /* interrupt on complete */
+       u32     rsvd2:4;
+       u32     type:6;         /* TRB type */
+#define TYPE_NORMAL    1
+#define TYPE_DATA      3
+#define TYPE_LINK      6
+       u32     dir:1;          /* Working at data stage of control endpoint
+                               operation. 0 is OUT and 1 is IN. */
+       u32     rsvd3:15;
+};
+
+/* TRB data structure
+ * For multiple TRB, all the TRBs' physical address should be continuous.
+ */
+struct mv_u3d_trb_hw {
+       u32     buf_addr_lo;    /* data buffer address low 32 bit */
+       u32     buf_addr_hi;    /* data buffer address high 32 bit */
+       u32     trb_len;        /* transfer length */
+       struct mv_u3d_trb_ctrl  ctrl;   /* TRB control data */
+};
+
+/* TRB structure */
+struct mv_u3d_trb {
+       struct mv_u3d_trb_hw *trb_hw;   /* point to the trb_hw structure */
+       dma_addr_t trb_dma;             /* dma address for this trb_hw */
+       struct list_head trb_list;      /* trb list */
+};
+
+/* device data structure */
+struct mv_u3d {
+       struct usb_gadget               gadget;
+       struct usb_gadget_driver        *driver;
+       spinlock_t                      lock;   /* device lock */
+       struct completion               *done;
+       struct device                   *dev;
+       int                             irq;
+
+       /* usb controller registers */
+       struct mv_u3d_cap_regs __iomem  *cap_regs;
+       struct mv_u3d_op_regs __iomem   *op_regs;
+       struct mv_u3d_vuc_regs __iomem  *vuc_regs;
+       void __iomem                    *phy_regs;
+
+       unsigned int                    max_eps;
+       struct mv_u3d_ep_context        *ep_context;
+       size_t                          ep_context_size;
+       dma_addr_t                      ep_context_dma;
+
+       struct dma_pool                 *trb_pool; /* for TRB data structure */
+       struct mv_u3d_ep                *eps;
+
+       struct mv_u3d_req               *status_req; /* ep0 status request */
+       struct usb_ctrlrequest          local_setup_buff; /* store setup data*/
+
+       unsigned int            resume_state;   /* USB state to resume */
+       unsigned int            usb_state;      /* USB current state */
+       unsigned int            ep0_state;      /* Endpoint zero state */
+       unsigned int            ep0_dir;
+
+       unsigned int            dev_addr;       /* device address */
+
+       unsigned int            errors;
+
+       unsigned                softconnect:1;
+       unsigned                vbus_active:1;  /* vbus is active or not */
+       unsigned                remote_wakeup:1; /* support remote wakeup */
+       unsigned                clock_gating:1; /* clock gating or not */
+       unsigned                active:1;       /* udc is active or not */
+       unsigned                vbus_valid_detect:1; /* udc vbus detection */
+
+       struct mv_usb_addon_irq *vbus;
+       unsigned int            power;
+
+       struct clk              *clk;
+};
+
+/* endpoint data structure */
+struct mv_u3d_ep {
+       struct usb_ep           ep;
+       struct mv_u3d           *u3d;
+       struct list_head        queue;  /* ep request queued hardware */
+       struct list_head        req_list; /* list of ep request */
+       struct mv_u3d_ep_context        *ep_context; /* ep context */
+       u32                     direction;
+       char                    name[14];
+       u32                     processing; /* there is ep request
+                                               queued on haredware */
+       spinlock_t              req_lock; /* ep lock */
+       unsigned                wedge:1;
+       unsigned                enabled:1;
+       unsigned                ep_type:2;
+       unsigned                ep_num:8;
+};
+
+/* request data structure */
+struct mv_u3d_req {
+       struct usb_request      req;
+       struct mv_u3d_ep        *ep;
+       struct list_head        queue;  /* ep requst queued on hardware */
+       struct list_head        list;   /* ep request list */
+       struct list_head        trb_list; /* trb list of a request */
+
+       struct mv_u3d_trb       *trb_head; /* point to first trb of a request */
+       unsigned                trb_count; /* TRB number in the chain */
+       unsigned                chain;     /* TRB chain or not */
+};
+
+#endif
diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c
new file mode 100644 (file)
index 0000000..8cfd5b0
--- /dev/null
@@ -0,0 +1,2098 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mv_usb.h>
+#include <linux/clk.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#include "mv_u3d.h"
+
+#define DRIVER_DESC            "Marvell PXA USB3.0 Device Controller driver"
+
+static const char driver_name[] = "mv_u3d";
+static const char driver_desc[] = DRIVER_DESC;
+
+static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status);
+static void mv_u3d_stop_activity(struct mv_u3d *u3d,
+                       struct usb_gadget_driver *driver);
+
+/* for endpoint 0 operations */
+static const struct usb_endpoint_descriptor mv_u3d_ep0_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     0,
+       .bmAttributes =         USB_ENDPOINT_XFER_CONTROL,
+       .wMaxPacketSize =       MV_U3D_EP0_MAX_PKT_SIZE,
+};
+
+static void mv_u3d_ep0_reset(struct mv_u3d *u3d)
+{
+       struct mv_u3d_ep *ep;
+       u32 epxcr;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               ep = &u3d->eps[i];
+               ep->u3d = u3d;
+
+               /* ep0 ep context, ep0 in and out share the same ep context */
+               ep->ep_context = &u3d->ep_context[1];
+       }
+
+       /* reset ep state machine */
+       /* reset ep0 out */
+       epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
+       epxcr |= MV_U3D_EPXCR_EP_INIT;
+       iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0);
+       udelay(5);
+       epxcr &= ~MV_U3D_EPXCR_EP_INIT;
+       iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0);
+
+       epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE
+               << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
+               | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
+               | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+               | MV_U3D_EPXCR_EP_TYPE_CONTROL);
+       iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr1);
+
+       /* reset ep0 in */
+       epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
+       epxcr |= MV_U3D_EPXCR_EP_INIT;
+       iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0);
+       udelay(5);
+       epxcr &= ~MV_U3D_EPXCR_EP_INIT;
+       iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0);
+
+       epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE
+               << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
+               | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
+               | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+               | MV_U3D_EPXCR_EP_TYPE_CONTROL);
+       iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr1);
+}
+
+static void mv_u3d_ep0_stall(struct mv_u3d *u3d)
+{
+       u32 tmp;
+       dev_dbg(u3d->dev, "%s\n", __func__);
+
+       /* set TX and RX to stall */
+       tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
+       tmp |= MV_U3D_EPXCR_EP_HALT;
+       iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
+
+       tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
+       tmp |= MV_U3D_EPXCR_EP_HALT;
+       iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
+
+       /* update ep0 state */
+       u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
+       u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
+}
+
+static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index,
+       struct mv_u3d_req *curr_req)
+{
+       struct mv_u3d_trb       *curr_trb;
+       dma_addr_t cur_deq_lo;
+       struct mv_u3d_ep_context        *curr_ep_context;
+       int trb_complete, actual, remaining_length;
+       int direction, ep_num;
+       int retval = 0;
+       u32 tmp, status, length;
+
+       curr_ep_context = &u3d->ep_context[index];
+       direction = index % 2;
+       ep_num = index / 2;
+
+       trb_complete = 0;
+       actual = curr_req->req.length;
+
+       while (!list_empty(&curr_req->trb_list)) {
+               curr_trb = list_entry(curr_req->trb_list.next,
+                                       struct mv_u3d_trb, trb_list);
+               if (!curr_trb->trb_hw->ctrl.own) {
+                       dev_err(u3d->dev, "%s, TRB own error!\n",
+                               u3d->eps[index].name);
+                       return 1;
+               }
+
+               curr_trb->trb_hw->ctrl.own = 0;
+               if (direction == MV_U3D_EP_DIR_OUT) {
+                       tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo);
+                       cur_deq_lo =
+                               ioread32(&u3d->vuc_regs->rxst[ep_num].curdeqlo);
+               } else {
+                       tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo);
+                       cur_deq_lo =
+                               ioread32(&u3d->vuc_regs->txst[ep_num].curdeqlo);
+               }
+
+               status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT;
+               length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK;
+
+               if (status == MV_U3D_COMPLETE_SUCCESS ||
+                       (status == MV_U3D_COMPLETE_SHORT_PACKET &&
+                       direction == MV_U3D_EP_DIR_OUT)) {
+                       remaining_length += length;
+                       actual -= remaining_length;
+               } else {
+                       dev_err(u3d->dev,
+                               "complete_tr error: ep=%d %s: error = 0x%x\n",
+                               index >> 1, direction ? "SEND" : "RECV",
+                               status);
+                       retval = -EPROTO;
+               }
+
+               list_del_init(&curr_trb->trb_list);
+       }
+       if (retval)
+               return retval;
+
+       curr_req->req.actual = actual;
+       return 0;
+}
+
+/*
+ * mv_u3d_done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ * request is still in progress.
+ */
+static
+void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status)
+{
+       struct mv_u3d *u3d = (struct mv_u3d *)ep->u3d;
+
+       dev_dbg(u3d->dev, "mv_u3d_done: remove req->queue\n");
+       /* Removed the req from ep queue */
+       list_del_init(&req->queue);
+
+       /* req.status should be set as -EINPROGRESS in ep_queue() */
+       if (req->req.status == -EINPROGRESS)
+               req->req.status = status;
+       else
+               status = req->req.status;
+
+       /* Free trb for the request */
+       if (!req->chain)
+               dma_pool_free(u3d->trb_pool,
+                       req->trb_head->trb_hw, req->trb_head->trb_dma);
+       else {
+               dma_unmap_single(ep->u3d->gadget.dev.parent,
+                       (dma_addr_t)req->trb_head->trb_dma,
+                       req->trb_count * sizeof(struct mv_u3d_trb_hw),
+                       DMA_BIDIRECTIONAL);
+               kfree(req->trb_head->trb_hw);
+       }
+       kfree(req->trb_head);
+
+       usb_gadget_unmap_request(&u3d->gadget, &req->req, mv_u3d_ep_dir(ep));
+
+       if (status && (status != -ESHUTDOWN)) {
+               dev_dbg(u3d->dev, "complete %s req %p stat %d len %u/%u",
+                       ep->ep.name, &req->req, status,
+                       req->req.actual, req->req.length);
+       }
+
+       spin_unlock(&ep->u3d->lock);
+       /*
+        * complete() is from gadget layer,
+        * eg fsg->bulk_in_complete()
+        */
+       if (req->req.complete)
+               req->req.complete(&ep->ep, &req->req);
+
+       spin_lock(&ep->u3d->lock);
+}
+
+static int mv_u3d_queue_trb(struct mv_u3d_ep *ep, struct mv_u3d_req *req)
+{
+       u32 tmp, direction;
+       struct mv_u3d *u3d;
+       struct mv_u3d_ep_context *ep_context;
+       int retval = 0;
+
+       u3d = ep->u3d;
+       direction = mv_u3d_ep_dir(ep);
+
+       /* ep0 in and out share the same ep context slot 1*/
+       if (ep->ep_num == 0)
+               ep_context = &(u3d->ep_context[1]);
+       else
+               ep_context = &(u3d->ep_context[ep->ep_num * 2 + direction]);
+
+       /* check if the pipe is empty or not */
+       if (!list_empty(&ep->queue)) {
+               dev_err(u3d->dev, "add trb to non-empty queue!\n");
+               retval = -ENOMEM;
+               WARN_ON(1);
+       } else {
+               ep_context->rsvd0 = cpu_to_le32(1);
+               ep_context->rsvd1 = 0;
+
+               /* Configure the trb address and set the DCS bit.
+                * Both DCS bit and own bit in trb should be set.
+                */
+               ep_context->trb_addr_lo =
+                       cpu_to_le32(req->trb_head->trb_dma | DCS_ENABLE);
+               ep_context->trb_addr_hi = 0;
+
+               /* Ensure that updates to the EP Context will
+                * occure before Ring Bell.
+                */
+               wmb();
+
+               /* ring bell the ep */
+               if (ep->ep_num == 0)
+                       tmp = 0x1;
+               else
+                       tmp = ep->ep_num * 2
+                               + ((direction == MV_U3D_EP_DIR_OUT) ? 0 : 1);
+
+               iowrite32(tmp, &u3d->op_regs->doorbell);
+       }
+       return retval;
+}
+
+static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req,
+                               unsigned *length, dma_addr_t *dma)
+{
+       u32 temp;
+       unsigned int direction;
+       struct mv_u3d_trb *trb;
+       struct mv_u3d_trb_hw *trb_hw;
+       struct mv_u3d *u3d;
+
+       /* how big will this transfer be? */
+       *length = req->req.length - req->req.actual;
+       BUG_ON(*length > (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER);
+
+       u3d = req->ep->u3d;
+
+       trb = kzalloc(sizeof(*trb), GFP_ATOMIC);
+       if (!trb) {
+               dev_err(u3d->dev, "%s, trb alloc fail\n", __func__);
+               return NULL;
+       }
+
+       /*
+        * Be careful that no _GFP_HIGHMEM is set,
+        * or we can not use dma_to_virt
+        * cannot use GFP_KERNEL in spin lock
+        */
+       trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma);
+       if (!trb_hw) {
+               dev_err(u3d->dev,
+                       "%s, dma_pool_alloc fail\n", __func__);
+               return NULL;
+       }
+       trb->trb_dma = *dma;
+       trb->trb_hw = trb_hw;
+
+       /* initialize buffer page pointers */
+       temp = (u32)(req->req.dma + req->req.actual);
+
+       trb_hw->buf_addr_lo = cpu_to_le32(temp);
+       trb_hw->buf_addr_hi = 0;
+       trb_hw->trb_len = cpu_to_le32(*length);
+       trb_hw->ctrl.own = 1;
+
+       if (req->ep->ep_num == 0)
+               trb_hw->ctrl.type = TYPE_DATA;
+       else
+               trb_hw->ctrl.type = TYPE_NORMAL;
+
+       req->req.actual += *length;
+
+       direction = mv_u3d_ep_dir(req->ep);
+       if (direction == MV_U3D_EP_DIR_IN)
+               trb_hw->ctrl.dir = 1;
+       else
+               trb_hw->ctrl.dir = 0;
+
+       /* Enable interrupt for the last trb of a request */
+       if (!req->req.no_interrupt)
+               trb_hw->ctrl.ioc = 1;
+
+       trb_hw->ctrl.chain = 0;
+
+       wmb();
+       return trb;
+}
+
+static int mv_u3d_build_trb_chain(struct mv_u3d_req *req, unsigned *length,
+               struct mv_u3d_trb *trb, int *is_last)
+{
+       u32 temp;
+       unsigned int direction;
+       struct mv_u3d *u3d;
+
+       /* how big will this transfer be? */
+       *length = min(req->req.length - req->req.actual,
+                       (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER);
+
+       u3d = req->ep->u3d;
+
+       trb->trb_dma = 0;
+
+       /* initialize buffer page pointers */
+       temp = (u32)(req->req.dma + req->req.actual);
+
+       trb->trb_hw->buf_addr_lo = cpu_to_le32(temp);
+       trb->trb_hw->buf_addr_hi = 0;
+       trb->trb_hw->trb_len = cpu_to_le32(*length);
+       trb->trb_hw->ctrl.own = 1;
+
+       if (req->ep->ep_num == 0)
+               trb->trb_hw->ctrl.type = TYPE_DATA;
+       else
+               trb->trb_hw->ctrl.type = TYPE_NORMAL;
+
+       req->req.actual += *length;
+
+       direction = mv_u3d_ep_dir(req->ep);
+       if (direction == MV_U3D_EP_DIR_IN)
+               trb->trb_hw->ctrl.dir = 1;
+       else
+               trb->trb_hw->ctrl.dir = 0;
+
+       /* zlp is needed if req->req.zero is set */
+       if (req->req.zero) {
+               if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+                       *is_last = 1;
+               else
+                       *is_last = 0;
+       } else if (req->req.length == req->req.actual)
+               *is_last = 1;
+       else
+               *is_last = 0;
+
+       /* Enable interrupt for the last trb of a request */
+       if (*is_last && !req->req.no_interrupt)
+               trb->trb_hw->ctrl.ioc = 1;
+
+       if (*is_last)
+               trb->trb_hw->ctrl.chain = 0;
+       else {
+               trb->trb_hw->ctrl.chain = 1;
+               dev_dbg(u3d->dev, "chain trb\n");
+       }
+
+       wmb();
+
+       return 0;
+}
+
+/* generate TRB linked list for a request
+ * usb controller only supports continous trb chain,
+ * that trb structure physical address should be continous.
+ */
+static int mv_u3d_req_to_trb(struct mv_u3d_req *req)
+{
+       unsigned count;
+       int is_last;
+       struct mv_u3d_trb *trb;
+       struct mv_u3d_trb_hw *trb_hw;
+       struct mv_u3d *u3d;
+       dma_addr_t dma;
+       unsigned length;
+       unsigned trb_num;
+
+       u3d = req->ep->u3d;
+
+       INIT_LIST_HEAD(&req->trb_list);
+
+       length = req->req.length - req->req.actual;
+       /* normally the request transfer length is less than 16KB.
+        * we use buil_trb_one() to optimize it.
+        */
+       if (length <= (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER) {
+               trb = mv_u3d_build_trb_one(req, &count, &dma);
+               list_add_tail(&trb->trb_list, &req->trb_list);
+               req->trb_head = trb;
+               req->trb_count = 1;
+               req->chain = 0;
+       } else {
+               trb_num = length / MV_U3D_EP_MAX_LENGTH_TRANSFER;
+               if (length % MV_U3D_EP_MAX_LENGTH_TRANSFER)
+                       trb_num++;
+
+               trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC);
+               if (!trb) {
+                       dev_err(u3d->dev,
+                                       "%s, trb alloc fail\n", __func__);
+                       return -ENOMEM;
+               }
+
+               trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC);
+               if (!trb_hw) {
+                       dev_err(u3d->dev,
+                                       "%s, trb_hw alloc fail\n", __func__);
+                       return -ENOMEM;
+               }
+
+               do {
+                       trb->trb_hw = trb_hw;
+                       if (mv_u3d_build_trb_chain(req, &count,
+                                               trb, &is_last)) {
+                               dev_err(u3d->dev,
+                                       "%s, mv_u3d_build_trb_chain fail\n",
+                                       __func__);
+                               return -EIO;
+                       }
+
+                       list_add_tail(&trb->trb_list, &req->trb_list);
+                       req->trb_count++;
+                       trb++;
+                       trb_hw++;
+               } while (!is_last);
+
+               req->trb_head = list_entry(req->trb_list.next,
+                                       struct mv_u3d_trb, trb_list);
+               req->trb_head->trb_dma = dma_map_single(u3d->gadget.dev.parent,
+                                       req->trb_head->trb_hw,
+                                       trb_num * sizeof(*trb_hw),
+                                       DMA_BIDIRECTIONAL);
+
+               req->chain = 1;
+       }
+
+       return 0;
+}
+
+static int
+mv_u3d_start_queue(struct mv_u3d_ep *ep)
+{
+       struct mv_u3d *u3d = ep->u3d;
+       struct mv_u3d_req *req;
+       int ret;
+
+       if (!list_empty(&ep->req_list) && !ep->processing)
+               req = list_entry(ep->req_list.next, struct mv_u3d_req, list);
+       else
+               return 0;
+
+       ep->processing = 1;
+
+       /* set up dma mapping */
+       ret = usb_gadget_map_request(&u3d->gadget, &req->req,
+                                       mv_u3d_ep_dir(ep));
+       if (ret)
+               return ret;
+
+       req->req.status = -EINPROGRESS;
+       req->req.actual = 0;
+       req->trb_count = 0;
+
+       /* build trbs and push them to device queue */
+       if (!mv_u3d_req_to_trb(req)) {
+               ret = mv_u3d_queue_trb(ep, req);
+               if (ret) {
+                       ep->processing = 0;
+                       return ret;
+               }
+       } else {
+               ep->processing = 0;
+               dev_err(u3d->dev, "%s, mv_u3d_req_to_trb fail\n", __func__);
+               return -ENOMEM;
+       }
+
+       /* irq handler advances the queue */
+       if (req)
+               list_add_tail(&req->queue, &ep->queue);
+
+       return 0;
+}
+
+static int mv_u3d_ep_enable(struct usb_ep *_ep,
+               const struct usb_endpoint_descriptor *desc)
+{
+       struct mv_u3d *u3d;
+       struct mv_u3d_ep *ep;
+       struct mv_u3d_ep_context *ep_context;
+       u16 max = 0;
+       unsigned maxburst = 0;
+       u32 epxcr, direction;
+
+       if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+               return -EINVAL;
+
+       ep = container_of(_ep, struct mv_u3d_ep, ep);
+       u3d = ep->u3d;
+
+       if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       direction = mv_u3d_ep_dir(ep);
+       max = le16_to_cpu(desc->wMaxPacketSize);
+
+       if (!_ep->maxburst)
+               _ep->maxburst = 1;
+       maxburst = _ep->maxburst;
+
+       /* Get the endpoint context address */
+       ep_context = (struct mv_u3d_ep_context *)ep->ep_context;
+
+       /* Set the max burst size */
+       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       case USB_ENDPOINT_XFER_BULK:
+               if (maxburst > 16) {
+                       dev_dbg(u3d->dev,
+                               "max burst should not be greater "
+                               "than 16 on bulk ep\n");
+                       maxburst = 1;
+                       _ep->maxburst = maxburst;
+               }
+               dev_dbg(u3d->dev,
+                       "maxburst: %d on bulk %s\n", maxburst, ep->name);
+               break;
+       case USB_ENDPOINT_XFER_CONTROL:
+               /* control transfer only supports maxburst as one */
+               maxburst = 1;
+               _ep->maxburst = maxburst;
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               if (maxburst != 1) {
+                       dev_dbg(u3d->dev,
+                               "max burst should be 1 on int ep "
+                               "if transfer size is not 1024\n");
+                       maxburst = 1;
+                       _ep->maxburst = maxburst;
+               }
+               break;
+       case USB_ENDPOINT_XFER_ISOC:
+               if (maxburst != 1) {
+                       dev_dbg(u3d->dev,
+                               "max burst should be 1 on isoc ep "
+                               "if transfer size is not 1024\n");
+                       maxburst = 1;
+                       _ep->maxburst = maxburst;
+               }
+               break;
+       default:
+               goto en_done;
+       }
+
+       ep->ep.maxpacket = max;
+       ep->ep.desc = desc;
+       ep->enabled = 1;
+
+       /* Enable the endpoint for Rx or Tx and set the endpoint type */
+       if (direction == MV_U3D_EP_DIR_OUT) {
+               epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+               epxcr |= MV_U3D_EPXCR_EP_INIT;
+               iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+               udelay(5);
+               epxcr &= ~MV_U3D_EPXCR_EP_INIT;
+               iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+
+               epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
+                     | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
+                     | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+                     | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK));
+               iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
+       } else {
+               epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+               epxcr |= MV_U3D_EPXCR_EP_INIT;
+               iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+               udelay(5);
+               epxcr &= ~MV_U3D_EPXCR_EP_INIT;
+               iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+
+               epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
+                     | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
+                     | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+                     | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK));
+               iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
+       }
+
+       return 0;
+en_done:
+       return -EINVAL;
+}
+
+static int  mv_u3d_ep_disable(struct usb_ep *_ep)
+{
+       struct mv_u3d *u3d;
+       struct mv_u3d_ep *ep;
+       struct mv_u3d_ep_context *ep_context;
+       u32 epxcr, direction;
+
+       if (!_ep)
+               return -EINVAL;
+
+       ep = container_of(_ep, struct mv_u3d_ep, ep);
+       if (!ep->ep.desc)
+               return -EINVAL;
+
+       u3d = ep->u3d;
+
+       /* Get the endpoint context address */
+       ep_context = ep->ep_context;
+
+       direction = mv_u3d_ep_dir(ep);
+
+       /* nuke all pending requests (does flush) */
+       mv_u3d_nuke(ep, -ESHUTDOWN);
+
+       /* Disable the endpoint for Rx or Tx and reset the endpoint type */
+       if (direction == MV_U3D_EP_DIR_OUT) {
+               epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
+               epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+                     | USB_ENDPOINT_XFERTYPE_MASK);
+               iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
+       } else {
+               epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
+               epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+                     | USB_ENDPOINT_XFERTYPE_MASK);
+               iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
+       }
+
+       ep->enabled = 0;
+
+       ep->ep.desc = NULL;
+       return 0;
+}
+
+static struct usb_request *
+mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+       struct mv_u3d_req *req = NULL;
+
+       req = kzalloc(sizeof *req, gfp_flags);
+       if (!req)
+               return NULL;
+
+       INIT_LIST_HEAD(&req->queue);
+
+       return &req->req;
+}
+
+static void mv_u3d_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct mv_u3d_req *req = container_of(_req, struct mv_u3d_req, req);
+
+       kfree(req);
+}
+
+static void mv_u3d_ep_fifo_flush(struct usb_ep *_ep)
+{
+       struct mv_u3d *u3d;
+       u32 direction;
+       struct mv_u3d_ep *ep = container_of(_ep, struct mv_u3d_ep, ep);
+       unsigned int loops;
+       u32 tmp;
+
+       /* if endpoint is not enabled, cannot flush endpoint */
+       if (!ep->enabled)
+               return;
+
+       u3d = ep->u3d;
+       direction = mv_u3d_ep_dir(ep);
+
+       /* ep0 need clear bit after flushing fifo. */
+       if (!ep->ep_num) {
+               if (direction == MV_U3D_EP_DIR_OUT) {
+                       tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
+                       tmp |= MV_U3D_EPXCR_EP_FLUSH;
+                       iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
+                       udelay(10);
+                       tmp &= ~MV_U3D_EPXCR_EP_FLUSH;
+                       iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
+               } else {
+                       tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
+                       tmp |= MV_U3D_EPXCR_EP_FLUSH;
+                       iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
+                       udelay(10);
+                       tmp &= ~MV_U3D_EPXCR_EP_FLUSH;
+                       iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
+               }
+               return;
+       }
+
+       if (direction == MV_U3D_EP_DIR_OUT) {
+               tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+               tmp |= MV_U3D_EPXCR_EP_FLUSH;
+               iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+
+               /* Wait until flushing completed */
+               loops = LOOPS(MV_U3D_FLUSH_TIMEOUT);
+               while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0) &
+                       MV_U3D_EPXCR_EP_FLUSH) {
+                       /*
+                        * EP_FLUSH bit should be cleared to indicate this
+                        * operation is complete
+                        */
+                       if (loops == 0) {
+                               dev_dbg(u3d->dev,
+                                   "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num,
+                                   direction ? "in" : "out");
+                               return;
+                       }
+                       loops--;
+                       udelay(LOOPS_USEC);
+               }
+       } else {        /* EP_DIR_IN */
+               tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+               tmp |= MV_U3D_EPXCR_EP_FLUSH;
+               iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+
+               /* Wait until flushing completed */
+               loops = LOOPS(MV_U3D_FLUSH_TIMEOUT);
+               while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0) &
+                       MV_U3D_EPXCR_EP_FLUSH) {
+                       /*
+                       * EP_FLUSH bit should be cleared to indicate this
+                       * operation is complete
+                       */
+                       if (loops == 0) {
+                               dev_dbg(u3d->dev,
+                                   "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num,
+                                   direction ? "in" : "out");
+                               return;
+                       }
+                       loops--;
+                       udelay(LOOPS_USEC);
+               }
+       }
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+       struct mv_u3d_ep *ep;
+       struct mv_u3d_req *req;
+       struct mv_u3d *u3d;
+       unsigned long flags;
+       int is_first_req = 0;
+
+       if (unlikely(!_ep || !_req))
+               return -EINVAL;
+
+       ep = container_of(_ep, struct mv_u3d_ep, ep);
+       u3d = ep->u3d;
+
+       req = container_of(_req, struct mv_u3d_req, req);
+
+       if (!ep->ep_num
+               && u3d->ep0_state == MV_U3D_STATUS_STAGE
+               && !_req->length) {
+               dev_dbg(u3d->dev, "ep0 status stage\n");
+               u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
+               return 0;
+       }
+
+       dev_dbg(u3d->dev, "%s: %s, req: 0x%x\n",
+                       __func__, _ep->name, (u32)req);
+
+       /* catch various bogus parameters */
+       if (!req->req.complete || !req->req.buf
+                       || !list_empty(&req->queue)) {
+               dev_err(u3d->dev,
+                       "%s, bad params, _req: 0x%x,"
+                       "req->req.complete: 0x%x, req->req.buf: 0x%x,"
+                       "list_empty: 0x%x\n",
+                       __func__, (u32)_req,
+                       (u32)req->req.complete, (u32)req->req.buf,
+                       (u32)list_empty(&req->queue));
+               return -EINVAL;
+       }
+       if (unlikely(!ep->ep.desc)) {
+               dev_err(u3d->dev, "%s, bad ep\n", __func__);
+               return -EINVAL;
+       }
+       if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+               if (req->req.length > ep->ep.maxpacket)
+                       return -EMSGSIZE;
+       }
+
+       if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) {
+               dev_err(u3d->dev,
+                       "bad params of driver/speed\n");
+               return -ESHUTDOWN;
+       }
+
+       req->ep = ep;
+
+       /* Software list handles usb request. */
+       spin_lock_irqsave(&ep->req_lock, flags);
+       is_first_req = list_empty(&ep->req_list);
+       list_add_tail(&req->list, &ep->req_list);
+       spin_unlock_irqrestore(&ep->req_lock, flags);
+       if (!is_first_req) {
+               dev_dbg(u3d->dev, "list is not empty\n");
+               return 0;
+       }
+
+       dev_dbg(u3d->dev, "call mv_u3d_start_queue from usb_ep_queue\n");
+       spin_lock_irqsave(&u3d->lock, flags);
+       mv_u3d_start_queue(ep);
+       spin_unlock_irqrestore(&u3d->lock, flags);
+       return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct mv_u3d_ep *ep;
+       struct mv_u3d_req *req;
+       struct mv_u3d *u3d;
+       struct mv_u3d_ep_context *ep_context;
+       struct mv_u3d_req *next_req;
+
+       unsigned long flags;
+       int ret = 0;
+
+       if (!_ep || !_req)
+               return -EINVAL;
+
+       ep = container_of(_ep, struct mv_u3d_ep, ep);
+       u3d = ep->u3d;
+
+       spin_lock_irqsave(&ep->u3d->lock, flags);
+
+       /* make sure it's actually queued on this endpoint */
+       list_for_each_entry(req, &ep->queue, queue) {
+               if (&req->req == _req)
+                       break;
+       }
+       if (&req->req != _req) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* The request is in progress, or completed but not dequeued */
+       if (ep->queue.next == &req->queue) {
+               _req->status = -ECONNRESET;
+               mv_u3d_ep_fifo_flush(_ep);
+
+               /* The request isn't the last request in this ep queue */
+               if (req->queue.next != &ep->queue) {
+                       dev_dbg(u3d->dev,
+                               "it is the last request in this ep queue\n");
+                       ep_context = ep->ep_context;
+                       next_req = list_entry(req->queue.next,
+                                       struct mv_u3d_req, queue);
+
+                       /* Point first TRB of next request to the EP context. */
+                       iowrite32((u32) next_req->trb_head,
+                                       &ep_context->trb_addr_lo);
+               } else {
+                       struct mv_u3d_ep_context *ep_context;
+                       ep_context = ep->ep_context;
+                       ep_context->trb_addr_lo = 0;
+                       ep_context->trb_addr_hi = 0;
+               }
+
+       } else
+               WARN_ON(1);
+
+       mv_u3d_done(ep, req, -ECONNRESET);
+
+       /* remove the req from the ep req list */
+       if (!list_empty(&ep->req_list)) {
+               struct mv_u3d_req *curr_req;
+               curr_req = list_entry(ep->req_list.next,
+                                       struct mv_u3d_req, list);
+               if (curr_req == req) {
+                       list_del_init(&req->list);
+                       ep->processing = 0;
+               }
+       }
+
+out:
+       spin_unlock_irqrestore(&ep->u3d->lock, flags);
+       return ret;
+}
+
+static void
+mv_u3d_ep_set_stall(struct mv_u3d *u3d, u8 ep_num, u8 direction, int stall)
+{
+       u32 tmp;
+       struct mv_u3d_ep *ep = u3d->eps;
+
+       dev_dbg(u3d->dev, "%s\n", __func__);
+       if (direction == MV_U3D_EP_DIR_OUT) {
+               tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+               if (stall)
+                       tmp |= MV_U3D_EPXCR_EP_HALT;
+               else
+                       tmp &= ~MV_U3D_EPXCR_EP_HALT;
+               iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+       } else {
+               tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+               if (stall)
+                       tmp |= MV_U3D_EPXCR_EP_HALT;
+               else
+                       tmp &= ~MV_U3D_EPXCR_EP_HALT;
+               iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+       }
+}
+
+static int mv_u3d_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
+{
+       struct mv_u3d_ep *ep;
+       unsigned long flags = 0;
+       int status = 0;
+       struct mv_u3d *u3d;
+
+       ep = container_of(_ep, struct mv_u3d_ep, ep);
+       u3d = ep->u3d;
+       if (!ep->ep.desc) {
+               status = -EINVAL;
+               goto out;
+       }
+
+       if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+               status = -EOPNOTSUPP;
+               goto out;
+       }
+
+       /*
+        * Attempt to halt IN ep will fail if any transfer requests
+        * are still queue
+        */
+       if (halt && (mv_u3d_ep_dir(ep) == MV_U3D_EP_DIR_IN)
+                       && !list_empty(&ep->queue)) {
+               status = -EAGAIN;
+               goto out;
+       }
+
+       spin_lock_irqsave(&ep->u3d->lock, flags);
+       mv_u3d_ep_set_stall(u3d, ep->ep_num, mv_u3d_ep_dir(ep), halt);
+       if (halt && wedge)
+               ep->wedge = 1;
+       else if (!halt)
+               ep->wedge = 0;
+       spin_unlock_irqrestore(&ep->u3d->lock, flags);
+
+       if (ep->ep_num == 0)
+               u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
+out:
+       return status;
+}
+
+static int mv_u3d_ep_set_halt(struct usb_ep *_ep, int halt)
+{
+       return mv_u3d_ep_set_halt_wedge(_ep, halt, 0);
+}
+
+static int mv_u3d_ep_set_wedge(struct usb_ep *_ep)
+{
+       return mv_u3d_ep_set_halt_wedge(_ep, 1, 1);
+}
+
+static struct usb_ep_ops mv_u3d_ep_ops = {
+       .enable         = mv_u3d_ep_enable,
+       .disable        = mv_u3d_ep_disable,
+
+       .alloc_request  = mv_u3d_alloc_request,
+       .free_request   = mv_u3d_free_request,
+
+       .queue          = mv_u3d_ep_queue,
+       .dequeue        = mv_u3d_ep_dequeue,
+
+       .set_wedge      = mv_u3d_ep_set_wedge,
+       .set_halt       = mv_u3d_ep_set_halt,
+       .fifo_flush     = mv_u3d_ep_fifo_flush,
+};
+
+static void mv_u3d_controller_stop(struct mv_u3d *u3d)
+{
+       u32 tmp;
+
+       if (!u3d->clock_gating && u3d->vbus_valid_detect)
+               iowrite32(MV_U3D_INTR_ENABLE_VBUS_VALID,
+                               &u3d->vuc_regs->intrenable);
+       else
+               iowrite32(0, &u3d->vuc_regs->intrenable);
+       iowrite32(~0x0, &u3d->vuc_regs->endcomplete);
+       iowrite32(~0x0, &u3d->vuc_regs->trbunderrun);
+       iowrite32(~0x0, &u3d->vuc_regs->trbcomplete);
+       iowrite32(~0x0, &u3d->vuc_regs->linkchange);
+       iowrite32(0x1, &u3d->vuc_regs->setuplock);
+
+       /* Reset the RUN bit in the command register to stop USB */
+       tmp = ioread32(&u3d->op_regs->usbcmd);
+       tmp &= ~MV_U3D_CMD_RUN_STOP;
+       iowrite32(tmp, &u3d->op_regs->usbcmd);
+       dev_dbg(u3d->dev, "after u3d_stop, USBCMD 0x%x\n",
+               ioread32(&u3d->op_regs->usbcmd));
+}
+
+static void mv_u3d_controller_start(struct mv_u3d *u3d)
+{
+       u32 usbintr;
+       u32 temp;
+
+       /* enable link LTSSM state machine */
+       temp = ioread32(&u3d->vuc_regs->ltssm);
+       temp |= MV_U3D_LTSSM_PHY_INIT_DONE;
+       iowrite32(temp, &u3d->vuc_regs->ltssm);
+
+       /* Enable interrupts */
+       usbintr = MV_U3D_INTR_ENABLE_LINK_CHG | MV_U3D_INTR_ENABLE_TXDESC_ERR |
+               MV_U3D_INTR_ENABLE_RXDESC_ERR | MV_U3D_INTR_ENABLE_TX_COMPLETE |
+               MV_U3D_INTR_ENABLE_RX_COMPLETE | MV_U3D_INTR_ENABLE_SETUP |
+               (u3d->vbus_valid_detect ? MV_U3D_INTR_ENABLE_VBUS_VALID : 0);
+       iowrite32(usbintr, &u3d->vuc_regs->intrenable);
+
+       /* Enable ctrl ep */
+       iowrite32(0x1, &u3d->vuc_regs->ctrlepenable);
+
+       /* Set the Run bit in the command register */
+       iowrite32(MV_U3D_CMD_RUN_STOP, &u3d->op_regs->usbcmd);
+       dev_dbg(u3d->dev, "after u3d_start, USBCMD 0x%x\n",
+               ioread32(&u3d->op_regs->usbcmd));
+}
+
+static int mv_u3d_controller_reset(struct mv_u3d *u3d)
+{
+       unsigned int loops;
+       u32 tmp;
+
+       /* Stop the controller */
+       tmp = ioread32(&u3d->op_regs->usbcmd);
+       tmp &= ~MV_U3D_CMD_RUN_STOP;
+       iowrite32(tmp, &u3d->op_regs->usbcmd);
+
+       /* Reset the controller to get default values */
+       iowrite32(MV_U3D_CMD_CTRL_RESET, &u3d->op_regs->usbcmd);
+
+       /* wait for reset to complete */
+       loops = LOOPS(MV_U3D_RESET_TIMEOUT);
+       while (ioread32(&u3d->op_regs->usbcmd) & MV_U3D_CMD_CTRL_RESET) {
+               if (loops == 0) {
+                       dev_err(u3d->dev,
+                               "Wait for RESET completed TIMEOUT\n");
+                       return -ETIMEDOUT;
+               }
+               loops--;
+               udelay(LOOPS_USEC);
+       }
+
+       /* Configure the Endpoint Context Address */
+       iowrite32(u3d->ep_context_dma, &u3d->op_regs->dcbaapl);
+       iowrite32(0, &u3d->op_regs->dcbaaph);
+
+       return 0;
+}
+
+static int mv_u3d_enable(struct mv_u3d *u3d)
+{
+       struct mv_usb_platform_data *pdata = u3d->dev->platform_data;
+       int retval;
+
+       if (u3d->active)
+               return 0;
+
+       if (!u3d->clock_gating) {
+               u3d->active = 1;
+               return 0;
+       }
+
+       dev_dbg(u3d->dev, "enable u3d\n");
+       clk_enable(u3d->clk);
+       if (pdata->phy_init) {
+               retval = pdata->phy_init(u3d->phy_regs);
+               if (retval) {
+                       dev_err(u3d->dev,
+                               "init phy error %d\n", retval);
+                       clk_disable(u3d->clk);
+                       return retval;
+               }
+       }
+       u3d->active = 1;
+
+       return 0;
+}
+
+static void mv_u3d_disable(struct mv_u3d *u3d)
+{
+       struct mv_usb_platform_data *pdata = u3d->dev->platform_data;
+       if (u3d->clock_gating && u3d->active) {
+               dev_dbg(u3d->dev, "disable u3d\n");
+               if (pdata->phy_deinit)
+                       pdata->phy_deinit(u3d->phy_regs);
+               clk_disable(u3d->clk);
+               u3d->active = 0;
+       }
+}
+
+static int mv_u3d_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+       struct mv_u3d *u3d;
+       unsigned long flags;
+       int retval = 0;
+
+       u3d = container_of(gadget, struct mv_u3d, gadget);
+
+       spin_lock_irqsave(&u3d->lock, flags);
+
+       u3d->vbus_active = (is_active != 0);
+       dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n",
+               __func__, u3d->softconnect, u3d->vbus_active);
+       /*
+        * 1. external VBUS detect: we can disable/enable clock on demand.
+        * 2. UDC VBUS detect: we have to enable clock all the time.
+        * 3. No VBUS detect: we have to enable clock all the time.
+        */
+       if (u3d->driver && u3d->softconnect && u3d->vbus_active) {
+               retval = mv_u3d_enable(u3d);
+               if (retval == 0) {
+                       /*
+                        * after clock is disabled, we lost all the register
+                        *  context. We have to re-init registers
+                        */
+                       mv_u3d_controller_reset(u3d);
+                       mv_u3d_ep0_reset(u3d);
+                       mv_u3d_controller_start(u3d);
+               }
+       } else if (u3d->driver && u3d->softconnect) {
+               if (!u3d->active)
+                       goto out;
+
+               /* stop all the transfer in queue*/
+               mv_u3d_stop_activity(u3d, u3d->driver);
+               mv_u3d_controller_stop(u3d);
+               mv_u3d_disable(u3d);
+       }
+
+out:
+       spin_unlock_irqrestore(&u3d->lock, flags);
+       return retval;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int mv_u3d_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+       struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget);
+
+       u3d->power = mA;
+
+       return 0;
+}
+
+static int mv_u3d_pullup(struct usb_gadget *gadget, int is_on)
+{
+       struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget);
+       unsigned long flags;
+       int retval = 0;
+
+       spin_lock_irqsave(&u3d->lock, flags);
+
+       dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n",
+               __func__, u3d->softconnect, u3d->vbus_active);
+       u3d->softconnect = (is_on != 0);
+       if (u3d->driver && u3d->softconnect && u3d->vbus_active) {
+               retval = mv_u3d_enable(u3d);
+               if (retval == 0) {
+                       /*
+                        * after clock is disabled, we lost all the register
+                        *  context. We have to re-init registers
+                        */
+                       mv_u3d_controller_reset(u3d);
+                       mv_u3d_ep0_reset(u3d);
+                       mv_u3d_controller_start(u3d);
+               }
+       } else if (u3d->driver && u3d->vbus_active) {
+               /* stop all the transfer in queue*/
+               mv_u3d_stop_activity(u3d, u3d->driver);
+               mv_u3d_controller_stop(u3d);
+               mv_u3d_disable(u3d);
+       }
+
+       spin_unlock_irqrestore(&u3d->lock, flags);
+
+       return retval;
+}
+
+static int mv_u3d_start(struct usb_gadget *g,
+               struct usb_gadget_driver *driver)
+{
+       struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget);
+       struct mv_usb_platform_data *pdata = u3d->dev->platform_data;
+       unsigned long flags;
+
+       if (u3d->driver)
+               return -EBUSY;
+
+       spin_lock_irqsave(&u3d->lock, flags);
+
+       if (!u3d->clock_gating) {
+               clk_enable(u3d->clk);
+               if (pdata->phy_init)
+                       pdata->phy_init(u3d->phy_regs);
+       }
+
+       /* hook up the driver ... */
+       driver->driver.bus = NULL;
+       u3d->driver = driver;
+       u3d->gadget.dev.driver = &driver->driver;
+
+       u3d->ep0_dir = USB_DIR_OUT;
+
+       spin_unlock_irqrestore(&u3d->lock, flags);
+
+       u3d->vbus_valid_detect = 1;
+
+       return 0;
+}
+
+static int mv_u3d_stop(struct usb_gadget *g,
+               struct usb_gadget_driver *driver)
+{
+       struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget);
+       struct mv_usb_platform_data *pdata = u3d->dev->platform_data;
+       unsigned long flags;
+
+       u3d->vbus_valid_detect = 0;
+       spin_lock_irqsave(&u3d->lock, flags);
+
+       /* enable clock to access controller register */
+       clk_enable(u3d->clk);
+       if (pdata->phy_init)
+               pdata->phy_init(u3d->phy_regs);
+
+       mv_u3d_controller_stop(u3d);
+       /* stop all usb activities */
+       u3d->gadget.speed = USB_SPEED_UNKNOWN;
+       mv_u3d_stop_activity(u3d, driver);
+       mv_u3d_disable(u3d);
+
+       if (pdata->phy_deinit)
+               pdata->phy_deinit(u3d->phy_regs);
+       clk_disable(u3d->clk);
+
+       spin_unlock_irqrestore(&u3d->lock, flags);
+
+       u3d->gadget.dev.driver = NULL;
+       u3d->driver = NULL;
+
+       return 0;
+}
+
+/* device controller usb_gadget_ops structure */
+static const struct usb_gadget_ops mv_u3d_ops = {
+       /* notify controller that VBUS is powered or not */
+       .vbus_session   = mv_u3d_vbus_session,
+
+       /* constrain controller's VBUS power usage */
+       .vbus_draw      = mv_u3d_vbus_draw,
+
+       .pullup         = mv_u3d_pullup,
+       .udc_start      = mv_u3d_start,
+       .udc_stop       = mv_u3d_stop,
+};
+
+static int mv_u3d_eps_init(struct mv_u3d *u3d)
+{
+       struct mv_u3d_ep        *ep;
+       char name[14];
+       int i;
+
+       /* initialize ep0, ep0 in/out use eps[1] */
+       ep = &u3d->eps[1];
+       ep->u3d = u3d;
+       strncpy(ep->name, "ep0", sizeof(ep->name));
+       ep->ep.name = ep->name;
+       ep->ep.ops = &mv_u3d_ep_ops;
+       ep->wedge = 0;
+       ep->ep.maxpacket = MV_U3D_EP0_MAX_PKT_SIZE;
+       ep->ep_num = 0;
+       ep->ep.desc = &mv_u3d_ep0_desc;
+       INIT_LIST_HEAD(&ep->queue);
+       INIT_LIST_HEAD(&ep->req_list);
+       ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
+
+       /* add ep0 ep_context */
+       ep->ep_context = &u3d->ep_context[1];
+
+       /* initialize other endpoints */
+       for (i = 2; i < u3d->max_eps * 2; i++) {
+               ep = &u3d->eps[i];
+               if (i & 1) {
+                       snprintf(name, sizeof(name), "ep%din", i >> 1);
+                       ep->direction = MV_U3D_EP_DIR_IN;
+               } else {
+                       snprintf(name, sizeof(name), "ep%dout", i >> 1);
+                       ep->direction = MV_U3D_EP_DIR_OUT;
+               }
+               ep->u3d = u3d;
+               strncpy(ep->name, name, sizeof(ep->name));
+               ep->ep.name = ep->name;
+
+               ep->ep.ops = &mv_u3d_ep_ops;
+               ep->ep.maxpacket = (unsigned short) ~0;
+               ep->ep_num = i / 2;
+
+               INIT_LIST_HEAD(&ep->queue);
+               list_add_tail(&ep->ep.ep_list, &u3d->gadget.ep_list);
+
+               INIT_LIST_HEAD(&ep->req_list);
+               spin_lock_init(&ep->req_lock);
+               ep->ep_context = &u3d->ep_context[i];
+       }
+
+       return 0;
+}
+
+/* delete all endpoint requests, called with spinlock held */
+static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status)
+{
+       /* endpoint fifo flush */
+       mv_u3d_ep_fifo_flush(&ep->ep);
+
+       while (!list_empty(&ep->queue)) {
+               struct mv_u3d_req *req = NULL;
+               req = list_entry(ep->queue.next, struct mv_u3d_req, queue);
+               mv_u3d_done(ep, req, status);
+       }
+}
+
+/* stop all USB activities */
+static
+void mv_u3d_stop_activity(struct mv_u3d *u3d, struct usb_gadget_driver *driver)
+{
+       struct mv_u3d_ep        *ep;
+
+       mv_u3d_nuke(&u3d->eps[1], -ESHUTDOWN);
+
+       list_for_each_entry(ep, &u3d->gadget.ep_list, ep.ep_list) {
+               mv_u3d_nuke(ep, -ESHUTDOWN);
+       }
+
+       /* report disconnect; the driver is already quiesced */
+       if (driver) {
+               spin_unlock(&u3d->lock);
+               driver->disconnect(&u3d->gadget);
+               spin_lock(&u3d->lock);
+       }
+}
+
+static void mv_u3d_irq_process_error(struct mv_u3d *u3d)
+{
+       /* Increment the error count */
+       u3d->errors++;
+       dev_err(u3d->dev, "%s\n", __func__);
+}
+
+static void mv_u3d_irq_process_link_change(struct mv_u3d *u3d)
+{
+       u32 linkchange;
+
+       linkchange = ioread32(&u3d->vuc_regs->linkchange);
+       iowrite32(linkchange, &u3d->vuc_regs->linkchange);
+
+       dev_dbg(u3d->dev, "linkchange: 0x%x\n", linkchange);
+
+       if (linkchange & MV_U3D_LINK_CHANGE_LINK_UP) {
+               dev_dbg(u3d->dev, "link up: ltssm state: 0x%x\n",
+                       ioread32(&u3d->vuc_regs->ltssmstate));
+
+               u3d->usb_state = USB_STATE_DEFAULT;
+               u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
+               u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
+
+               /* set speed */
+               u3d->gadget.speed = USB_SPEED_SUPER;
+       }
+
+       if (linkchange & MV_U3D_LINK_CHANGE_SUSPEND) {
+               dev_dbg(u3d->dev, "link suspend\n");
+               u3d->resume_state = u3d->usb_state;
+               u3d->usb_state = USB_STATE_SUSPENDED;
+       }
+
+       if (linkchange & MV_U3D_LINK_CHANGE_RESUME) {
+               dev_dbg(u3d->dev, "link resume\n");
+               u3d->usb_state = u3d->resume_state;
+               u3d->resume_state = 0;
+       }
+
+       if (linkchange & MV_U3D_LINK_CHANGE_WRESET) {
+               dev_dbg(u3d->dev, "warm reset\n");
+               u3d->usb_state = USB_STATE_POWERED;
+       }
+
+       if (linkchange & MV_U3D_LINK_CHANGE_HRESET) {
+               dev_dbg(u3d->dev, "hot reset\n");
+               u3d->usb_state = USB_STATE_DEFAULT;
+       }
+
+       if (linkchange & MV_U3D_LINK_CHANGE_INACT)
+               dev_dbg(u3d->dev, "inactive\n");
+
+       if (linkchange & MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0)
+               dev_dbg(u3d->dev, "ss.disabled\n");
+
+       if (linkchange & MV_U3D_LINK_CHANGE_VBUS_INVALID) {
+               dev_dbg(u3d->dev, "vbus invalid\n");
+               u3d->usb_state = USB_STATE_ATTACHED;
+               u3d->vbus_valid_detect = 1;
+               /* if external vbus detect is not supported,
+                * we handle it here.
+                */
+               if (!u3d->vbus) {
+                       spin_unlock(&u3d->lock);
+                       mv_u3d_vbus_session(&u3d->gadget, 0);
+                       spin_lock(&u3d->lock);
+               }
+       }
+}
+
+static void mv_u3d_ch9setaddress(struct mv_u3d *u3d,
+                               struct usb_ctrlrequest *setup)
+{
+       u32 tmp;
+
+       if (u3d->usb_state != USB_STATE_DEFAULT) {
+               dev_err(u3d->dev,
+                       "%s, cannot setaddr in this state (%d)\n",
+                       __func__, u3d->usb_state);
+               goto err;
+       }
+
+       u3d->dev_addr = (u8)setup->wValue;
+
+       dev_dbg(u3d->dev, "%s: 0x%x\n", __func__, u3d->dev_addr);
+
+       if (u3d->dev_addr > 127) {
+               dev_err(u3d->dev,
+                       "%s, u3d address is wrong (out of range)\n", __func__);
+               u3d->dev_addr = 0;
+               goto err;
+       }
+
+       /* update usb state */
+       u3d->usb_state = USB_STATE_ADDRESS;
+
+       /* set the new address */
+       tmp = ioread32(&u3d->vuc_regs->devaddrtiebrkr);
+       tmp &= ~0x7F;
+       tmp |= (u32)u3d->dev_addr;
+       iowrite32(tmp, &u3d->vuc_regs->devaddrtiebrkr);
+
+       return;
+err:
+       mv_u3d_ep0_stall(u3d);
+}
+
+static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup)
+{
+       if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+               if (setup->bRequest == USB_REQ_SET_CONFIGURATION)
+                       return 1;
+
+       return 0;
+}
+
+static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num,
+       struct usb_ctrlrequest *setup)
+{
+       bool delegate = false;
+
+       mv_u3d_nuke(&u3d->eps[ep_num * 2 + MV_U3D_EP_DIR_IN], -ESHUTDOWN);
+
+       dev_dbg(u3d->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+                       setup->bRequestType, setup->bRequest,
+                       setup->wValue, setup->wIndex, setup->wLength);
+
+       /* We process some stardard setup requests here */
+       if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+               switch (setup->bRequest) {
+               case USB_REQ_GET_STATUS:
+                       delegate = true;
+                       break;
+
+               case USB_REQ_SET_ADDRESS:
+                       mv_u3d_ch9setaddress(u3d, setup);
+                       break;
+
+               case USB_REQ_CLEAR_FEATURE:
+                       delegate = true;
+                       break;
+
+               case USB_REQ_SET_FEATURE:
+                       delegate = true;
+                       break;
+
+               default:
+                       delegate = true;
+               }
+       } else
+               delegate = true;
+
+       /* delegate USB standard requests to the gadget driver */
+       if (delegate == true) {
+               /* USB requests handled by gadget */
+               if (setup->wLength) {
+                       /* DATA phase from gadget, STATUS phase from u3d */
+                       u3d->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+                                       ? MV_U3D_EP_DIR_IN : MV_U3D_EP_DIR_OUT;
+                       spin_unlock(&u3d->lock);
+                       if (u3d->driver->setup(&u3d->gadget,
+                               &u3d->local_setup_buff) < 0) {
+                               dev_err(u3d->dev, "setup error!\n");
+                               mv_u3d_ep0_stall(u3d);
+                       }
+                       spin_lock(&u3d->lock);
+               } else {
+                       /* no DATA phase, STATUS phase from gadget */
+                       u3d->ep0_dir = MV_U3D_EP_DIR_IN;
+                       u3d->ep0_state = MV_U3D_STATUS_STAGE;
+                       spin_unlock(&u3d->lock);
+                       if (u3d->driver->setup(&u3d->gadget,
+                               &u3d->local_setup_buff) < 0)
+                               mv_u3d_ep0_stall(u3d);
+                       spin_lock(&u3d->lock);
+               }
+
+               if (mv_u3d_is_set_configuration(setup)) {
+                       dev_dbg(u3d->dev, "u3d configured\n");
+                       u3d->usb_state = USB_STATE_CONFIGURED;
+               }
+       }
+}
+
+static void mv_u3d_get_setup_data(struct mv_u3d *u3d, u8 ep_num, u8 *buffer_ptr)
+{
+       struct mv_u3d_ep_context *epcontext;
+
+       epcontext = &u3d->ep_context[ep_num * 2 + MV_U3D_EP_DIR_IN];
+
+       /* Copy the setup packet to local buffer */
+       memcpy(buffer_ptr, (u8 *) &epcontext->setup_buffer, 8);
+}
+
+static void mv_u3d_irq_process_setup(struct mv_u3d *u3d)
+{
+       u32 tmp, i;
+       /* Process all Setup packet received interrupts */
+       tmp = ioread32(&u3d->vuc_regs->setuplock);
+       if (tmp) {
+               for (i = 0; i < u3d->max_eps; i++) {
+                       if (tmp & (1 << i)) {
+                               mv_u3d_get_setup_data(u3d, i,
+                                       (u8 *)(&u3d->local_setup_buff));
+                               mv_u3d_handle_setup_packet(u3d, i,
+                                       &u3d->local_setup_buff);
+                       }
+               }
+       }
+
+       iowrite32(tmp, &u3d->vuc_regs->setuplock);
+}
+
+static void mv_u3d_irq_process_tr_complete(struct mv_u3d *u3d)
+{
+       u32 tmp, bit_pos;
+       int i, ep_num = 0, direction = 0;
+       struct mv_u3d_ep        *curr_ep;
+       struct mv_u3d_req *curr_req, *temp_req;
+       int status;
+
+       tmp = ioread32(&u3d->vuc_regs->endcomplete);
+
+       dev_dbg(u3d->dev, "tr_complete: ep: 0x%x\n", tmp);
+       if (!tmp)
+               return;
+       iowrite32(tmp, &u3d->vuc_regs->endcomplete);
+
+       for (i = 0; i < u3d->max_eps * 2; i++) {
+               ep_num = i >> 1;
+               direction = i % 2;
+
+               bit_pos = 1 << (ep_num + 16 * direction);
+
+               if (!(bit_pos & tmp))
+                       continue;
+
+               if (i == 0)
+                       curr_ep = &u3d->eps[1];
+               else
+                       curr_ep = &u3d->eps[i];
+
+               /* remove req out of ep request list after completion */
+               dev_dbg(u3d->dev, "tr comp: check req_list\n");
+               spin_lock(&curr_ep->req_lock);
+               if (!list_empty(&curr_ep->req_list)) {
+                       struct mv_u3d_req *req;
+                       req = list_entry(curr_ep->req_list.next,
+                                               struct mv_u3d_req, list);
+                       list_del_init(&req->list);
+                       curr_ep->processing = 0;
+               }
+               spin_unlock(&curr_ep->req_lock);
+
+               /* process the req queue until an uncomplete request */
+               list_for_each_entry_safe(curr_req, temp_req,
+                       &curr_ep->queue, queue) {
+                       status = mv_u3d_process_ep_req(u3d, i, curr_req);
+                       if (status)
+                               break;
+                       /* write back status to req */
+                       curr_req->req.status = status;
+
+                       /* ep0 request completion */
+                       if (ep_num == 0) {
+                               mv_u3d_done(curr_ep, curr_req, 0);
+                               break;
+                       } else {
+                               mv_u3d_done(curr_ep, curr_req, status);
+                       }
+               }
+
+               dev_dbg(u3d->dev, "call mv_u3d_start_queue from ep complete\n");
+               mv_u3d_start_queue(curr_ep);
+       }
+}
+
+static irqreturn_t mv_u3d_irq(int irq, void *dev)
+{
+       struct mv_u3d *u3d = (struct mv_u3d *)dev;
+       u32 status, intr;
+       u32 bridgesetting;
+       u32 trbunderrun;
+
+       spin_lock(&u3d->lock);
+
+       status = ioread32(&u3d->vuc_regs->intrcause);
+       intr = ioread32(&u3d->vuc_regs->intrenable);
+       status &= intr;
+
+       if (status == 0) {
+               spin_unlock(&u3d->lock);
+               dev_err(u3d->dev, "irq error!\n");
+               return IRQ_NONE;
+       }
+
+       if (status & MV_U3D_USBINT_VBUS_VALID) {
+               bridgesetting = ioread32(&u3d->vuc_regs->bridgesetting);
+               if (bridgesetting & MV_U3D_BRIDGE_SETTING_VBUS_VALID) {
+                       /* write vbus valid bit of bridge setting to clear */
+                       bridgesetting = MV_U3D_BRIDGE_SETTING_VBUS_VALID;
+                       iowrite32(bridgesetting, &u3d->vuc_regs->bridgesetting);
+                       dev_dbg(u3d->dev, "vbus valid\n");
+
+                       u3d->usb_state = USB_STATE_POWERED;
+                       u3d->vbus_valid_detect = 0;
+                       /* if external vbus detect is not supported,
+                        * we handle it here.
+                        */
+                       if (!u3d->vbus) {
+                               spin_unlock(&u3d->lock);
+                               mv_u3d_vbus_session(&u3d->gadget, 1);
+                               spin_lock(&u3d->lock);
+                       }
+               } else
+                       dev_err(u3d->dev, "vbus bit is not set\n");
+       }
+
+       /* RX data is already in the 16KB FIFO.*/
+       if (status & MV_U3D_USBINT_UNDER_RUN) {
+               trbunderrun = ioread32(&u3d->vuc_regs->trbunderrun);
+               dev_err(u3d->dev, "under run, ep%d\n", trbunderrun);
+               iowrite32(trbunderrun, &u3d->vuc_regs->trbunderrun);
+               mv_u3d_irq_process_error(u3d);
+       }
+
+       if (status & (MV_U3D_USBINT_RXDESC_ERR | MV_U3D_USBINT_TXDESC_ERR)) {
+               /* write one to clear */
+               iowrite32(status & (MV_U3D_USBINT_RXDESC_ERR
+                       | MV_U3D_USBINT_TXDESC_ERR),
+                       &u3d->vuc_regs->intrcause);
+               dev_err(u3d->dev, "desc err 0x%x\n", status);
+               mv_u3d_irq_process_error(u3d);
+       }
+
+       if (status & MV_U3D_USBINT_LINK_CHG)
+               mv_u3d_irq_process_link_change(u3d);
+
+       if (status & MV_U3D_USBINT_TX_COMPLETE)
+               mv_u3d_irq_process_tr_complete(u3d);
+
+       if (status & MV_U3D_USBINT_RX_COMPLETE)
+               mv_u3d_irq_process_tr_complete(u3d);
+
+       if (status & MV_U3D_USBINT_SETUP)
+               mv_u3d_irq_process_setup(u3d);
+
+       spin_unlock(&u3d->lock);
+       return IRQ_HANDLED;
+}
+
+static void mv_u3d_gadget_release(struct device *dev)
+{
+       dev_dbg(dev, "%s\n", __func__);
+}
+
+static __devexit int mv_u3d_remove(struct platform_device *dev)
+{
+       struct mv_u3d *u3d = platform_get_drvdata(dev);
+
+       BUG_ON(u3d == NULL);
+
+       usb_del_gadget_udc(&u3d->gadget);
+
+       /* free memory allocated in probe */
+       if (u3d->trb_pool)
+               dma_pool_destroy(u3d->trb_pool);
+
+       if (u3d->ep_context)
+               dma_free_coherent(&dev->dev, u3d->ep_context_size,
+                       u3d->ep_context, u3d->ep_context_dma);
+
+       kfree(u3d->eps);
+
+       if (u3d->irq)
+               free_irq(u3d->irq, &dev->dev);
+
+       if (u3d->cap_regs)
+               iounmap(u3d->cap_regs);
+       u3d->cap_regs = NULL;
+
+       kfree(u3d->status_req);
+
+       clk_put(u3d->clk);
+
+       device_unregister(&u3d->gadget.dev);
+
+       platform_set_drvdata(dev, NULL);
+
+       kfree(u3d);
+
+       return 0;
+}
+
+static int mv_u3d_probe(struct platform_device *dev)
+{
+       struct mv_u3d *u3d = NULL;
+       struct mv_usb_platform_data *pdata = dev->dev.platform_data;
+       int retval = 0;
+       struct resource *r;
+       size_t size;
+
+       if (!dev->dev.platform_data) {
+               dev_err(&dev->dev, "missing platform_data\n");
+               retval = -ENODEV;
+               goto err_pdata;
+       }
+
+       u3d = kzalloc(sizeof(*u3d), GFP_KERNEL);
+       if (!u3d) {
+               dev_err(&dev->dev, "failed to allocate memory for u3d\n");
+               retval = -ENOMEM;
+               goto err_alloc_private;
+       }
+
+       spin_lock_init(&u3d->lock);
+
+       platform_set_drvdata(dev, u3d);
+
+       u3d->dev = &dev->dev;
+       u3d->vbus = pdata->vbus;
+
+       u3d->clk = clk_get(&dev->dev, pdata->clkname[0]);
+       if (IS_ERR(u3d->clk)) {
+               retval = PTR_ERR(u3d->clk);
+               goto err_get_clk;
+       }
+
+       r = platform_get_resource_byname(dev, IORESOURCE_MEM, "capregs");
+       if (!r) {
+               dev_err(&dev->dev, "no I/O memory resource defined\n");
+               retval = -ENODEV;
+               goto err_get_cap_regs;
+       }
+
+       u3d->cap_regs = (struct mv_u3d_cap_regs __iomem *)
+               ioremap(r->start, resource_size(r));
+       if (!u3d->cap_regs) {
+               dev_err(&dev->dev, "failed to map I/O memory\n");
+               retval = -EBUSY;
+               goto err_map_cap_regs;
+       } else {
+               dev_dbg(&dev->dev, "cap_regs address: 0x%x/0x%x\n",
+                       (unsigned int)r->start, (unsigned int)u3d->cap_regs);
+       }
+
+       /* we will access controller register, so enable the u3d controller */
+       clk_enable(u3d->clk);
+
+       if (pdata->phy_init) {
+               retval = pdata->phy_init(u3d->phy_regs);
+               if (retval) {
+                       dev_err(&dev->dev, "init phy error %d\n", retval);
+                       goto err_u3d_enable;
+               }
+       }
+
+       u3d->op_regs = (struct mv_u3d_op_regs __iomem *)((u32)u3d->cap_regs
+               + MV_U3D_USB3_OP_REGS_OFFSET);
+
+       u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)((u32)u3d->cap_regs
+               + ioread32(&u3d->cap_regs->vuoff));
+
+       u3d->max_eps = 16;
+
+       /*
+        * some platform will use usb to download image, it may not disconnect
+        * usb gadget before loading kernel. So first stop u3d here.
+        */
+       mv_u3d_controller_stop(u3d);
+       iowrite32(0xFFFFFFFF, &u3d->vuc_regs->intrcause);
+
+       if (pdata->phy_deinit)
+               pdata->phy_deinit(u3d->phy_regs);
+       clk_disable(u3d->clk);
+
+       size = u3d->max_eps * sizeof(struct mv_u3d_ep_context) * 2;
+       size = (size + MV_U3D_EP_CONTEXT_ALIGNMENT - 1)
+               & ~(MV_U3D_EP_CONTEXT_ALIGNMENT - 1);
+       u3d->ep_context = dma_alloc_coherent(&dev->dev, size,
+                                       &u3d->ep_context_dma, GFP_KERNEL);
+       if (!u3d->ep_context) {
+               dev_err(&dev->dev, "allocate ep context memory failed\n");
+               retval = -ENOMEM;
+               goto err_alloc_ep_context;
+       }
+       u3d->ep_context_size = size;
+
+       /* create TRB dma_pool resource */
+       u3d->trb_pool = dma_pool_create("u3d_trb",
+                       &dev->dev,
+                       sizeof(struct mv_u3d_trb_hw),
+                       MV_U3D_TRB_ALIGNMENT,
+                       MV_U3D_DMA_BOUNDARY);
+
+       if (!u3d->trb_pool) {
+               retval = -ENOMEM;
+               goto err_alloc_trb_pool;
+       }
+
+       size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2;
+       u3d->eps = kzalloc(size, GFP_KERNEL);
+       if (!u3d->eps) {
+               dev_err(&dev->dev, "allocate ep memory failed\n");
+               retval = -ENOMEM;
+               goto err_alloc_eps;
+       }
+
+       /* initialize ep0 status request structure */
+       u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL);
+       if (!u3d->status_req) {
+               dev_err(&dev->dev, "allocate status_req memory failed\n");
+               retval = -ENOMEM;
+               goto err_alloc_status_req;
+       }
+       INIT_LIST_HEAD(&u3d->status_req->queue);
+
+       /* allocate a small amount of memory to get valid address */
+       u3d->status_req->req.buf = (char *)u3d->status_req
+                                       + sizeof(struct mv_u3d_req);
+       u3d->status_req->req.dma = virt_to_phys(u3d->status_req->req.buf);
+
+       u3d->resume_state = USB_STATE_NOTATTACHED;
+       u3d->usb_state = USB_STATE_ATTACHED;
+       u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
+       u3d->remote_wakeup = 0;
+
+       r = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+       if (!r) {
+               dev_err(&dev->dev, "no IRQ resource defined\n");
+               retval = -ENODEV;
+               goto err_get_irq;
+       }
+       u3d->irq = r->start;
+       if (request_irq(u3d->irq, mv_u3d_irq,
+               IRQF_DISABLED | IRQF_SHARED, driver_name, u3d)) {
+               u3d->irq = 0;
+               dev_err(&dev->dev, "Request irq %d for u3d failed\n",
+                       u3d->irq);
+               retval = -ENODEV;
+               goto err_request_irq;
+       }
+
+       /* initialize gadget structure */
+       u3d->gadget.ops = &mv_u3d_ops;  /* usb_gadget_ops */
+       u3d->gadget.ep0 = &u3d->eps[1].ep;      /* gadget ep0 */
+       INIT_LIST_HEAD(&u3d->gadget.ep_list);   /* ep_list */
+       u3d->gadget.speed = USB_SPEED_UNKNOWN;  /* speed */
+
+       /* the "gadget" abstracts/virtualizes the controller */
+       dev_set_name(&u3d->gadget.dev, "gadget");
+       u3d->gadget.dev.parent = &dev->dev;
+       u3d->gadget.dev.dma_mask = dev->dev.dma_mask;
+       u3d->gadget.dev.release = mv_u3d_gadget_release;
+       u3d->gadget.name = driver_name;         /* gadget name */
+
+       retval = device_register(&u3d->gadget.dev);
+       if (retval)
+               goto err_register_gadget_device;
+
+       mv_u3d_eps_init(u3d);
+
+       /* external vbus detection */
+       if (u3d->vbus) {
+               u3d->clock_gating = 1;
+               dev_err(&dev->dev, "external vbus detection\n");
+       }
+
+       if (!u3d->clock_gating)
+               u3d->vbus_active = 1;
+
+       /* enable usb3 controller vbus detection */
+       u3d->vbus_valid_detect = 1;
+
+       retval = usb_add_gadget_udc(&dev->dev, &u3d->gadget);
+       if (retval)
+               goto err_unregister;
+
+       dev_dbg(&dev->dev, "successful probe usb3 device %s clock gating.\n",
+               u3d->clock_gating ? "with" : "without");
+
+       return 0;
+
+err_unregister:
+       device_unregister(&u3d->gadget.dev);
+err_register_gadget_device:
+       free_irq(u3d->irq, &dev->dev);
+err_request_irq:
+err_get_irq:
+       kfree(u3d->status_req);
+err_alloc_status_req:
+       kfree(u3d->eps);
+err_alloc_eps:
+       dma_pool_destroy(u3d->trb_pool);
+err_alloc_trb_pool:
+       dma_free_coherent(&dev->dev, u3d->ep_context_size,
+               u3d->ep_context, u3d->ep_context_dma);
+err_alloc_ep_context:
+       if (pdata->phy_deinit)
+               pdata->phy_deinit(u3d->phy_regs);
+       clk_disable(u3d->clk);
+err_u3d_enable:
+       iounmap(u3d->cap_regs);
+err_map_cap_regs:
+err_get_cap_regs:
+err_get_clk:
+       clk_put(u3d->clk);
+       platform_set_drvdata(dev, NULL);
+       kfree(u3d);
+err_alloc_private:
+err_pdata:
+       return retval;
+}
+
+#ifdef CONFIG_PM
+static int mv_u3d_suspend(struct device *dev)
+{
+       struct mv_u3d *u3d = dev_get_drvdata(dev);
+
+       /*
+        * only cable is unplugged, usb can suspend.
+        * So do not care about clock_gating == 1, it is handled by
+        * vbus session.
+        */
+       if (!u3d->clock_gating) {
+               mv_u3d_controller_stop(u3d);
+
+               spin_lock_irq(&u3d->lock);
+               /* stop all usb activities */
+               mv_u3d_stop_activity(u3d, u3d->driver);
+               spin_unlock_irq(&u3d->lock);
+
+               mv_u3d_disable(u3d);
+       }
+
+       return 0;
+}
+
+static int mv_u3d_resume(struct device *dev)
+{
+       struct mv_u3d *u3d = dev_get_drvdata(dev);
+       int retval;
+
+       if (!u3d->clock_gating) {
+               retval = mv_u3d_enable(u3d);
+               if (retval)
+                       return retval;
+
+               if (u3d->driver && u3d->softconnect) {
+                       mv_u3d_controller_reset(u3d);
+                       mv_u3d_ep0_reset(u3d);
+                       mv_u3d_controller_start(u3d);
+               }
+       }
+
+       return 0;
+}
+
+SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume);
+#endif
+
+static void mv_u3d_shutdown(struct platform_device *dev)
+{
+       struct mv_u3d *u3d = dev_get_drvdata(&dev->dev);
+       u32 tmp;
+
+       tmp = ioread32(&u3d->op_regs->usbcmd);
+       tmp &= ~MV_U3D_CMD_RUN_STOP;
+       iowrite32(tmp, &u3d->op_regs->usbcmd);
+}
+
+static struct platform_driver mv_u3d_driver = {
+       .probe          = mv_u3d_probe,
+       .remove         = __exit_p(mv_u3d_remove),
+       .shutdown       = mv_u3d_shutdown,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "mv-u3d",
+#ifdef CONFIG_PM
+               .pm     = &mv_u3d_pm_ops,
+#endif
+       },
+};
+
+module_platform_driver(mv_u3d_driver);
+MODULE_ALIAS("platform:mv-u3d");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>");
+MODULE_LICENSE("GPL");
index 117a4bb..75db2c3 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/timer.h>
 #include <linux/list.h>
@@ -1381,7 +1382,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
                return retval;
        }
 
-       if (udc->transceiver) {
+       if (!IS_ERR_OR_NULL(udc->transceiver)) {
                retval = otg_set_peripheral(udc->transceiver->otg,
                                        &udc->gadget);
                if (retval) {
@@ -2107,7 +2108,7 @@ static int __devexit mv_udc_remove(struct platform_device *dev)
         * then vbus irq will not be requested in udc driver.
         */
        if (udc->pdata && udc->pdata->vbus
-               && udc->clock_gating && udc->transceiver == NULL)
+               && udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver))
                free_irq(udc->pdata->vbus->irq, &dev->dev);
 
        /* free memory allocated in probe */
@@ -2180,7 +2181,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
 
 #ifdef CONFIG_USB_OTG_UTILS
        if (pdata->mode == MV_USB_MODE_OTG)
-               udc->transceiver = usb_get_transceiver();
+               udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
 #endif
 
        udc->clknum = pdata->clknum;
@@ -2325,7 +2326,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
        eps_init(udc);
 
        /* VBUS detect: we can disable/enable clock on demand.*/
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                udc->clock_gating = 1;
        else if (pdata->vbus) {
                udc->clock_gating = 1;
@@ -2369,7 +2370,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
 
 err_unregister:
        if (udc->pdata && udc->pdata->vbus
-               && udc->clock_gating && udc->transceiver == NULL)
+               && udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver))
                free_irq(pdata->vbus->irq, &dev->dev);
        device_unregister(&udc->gadget.dev);
 err_free_irq:
@@ -2404,7 +2405,7 @@ static int mv_udc_suspend(struct device *_dev)
        struct mv_udc *udc = the_controller;
 
        /* if OTG is enabled, the following will be done in OTG driver*/
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                return 0;
 
        if (udc->pdata->vbus && udc->pdata->vbus->poll)
@@ -2437,7 +2438,7 @@ static int mv_udc_resume(struct device *_dev)
        int retval;
 
        /* if OTG is enabled, the following will be done in OTG driver*/
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                return 0;
 
        if (!udc->clock_gating) {
index 89cbd2b..f9132ad 100644 (file)
 #include <linux/usb/otg.h>
 #include <linux/dma-mapping.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/prefetch.h>
+#include <linux/io.h>
 
 #include <asm/byteorder.h>
-#include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/unaligned.h>
 #include <asm/mach-types.h>
 #define        DRIVER_DESC     "OMAP UDC driver"
 #define        DRIVER_VERSION  "4 October 2004"
 
-#define        DMA_ADDR_INVALID        (~(dma_addr_t)0)
-
-#define OMAP2_DMA_CH(ch)       (((ch) - 1) << 1)
-#define OMAP24XX_DMA(name, ch) (OMAP24XX_DMA_##name + OMAP2_DMA_CH(ch))
-
 /*
  * The OMAP UDC needs _very_ early endpoint setup:  before enabling the
  * D+ pullup to allow enumeration.  That's too early for the gadget
 #ifdef USE_ISO
 static unsigned fifo_mode = 3;
 #else
-static unsigned fifo_mode = 0;
+static unsigned fifo_mode;
 #endif
 
 /* "modprobe omap_udc fifo_mode=42", or else as a kernel
  * boot parameter "omap_udc:fifo_mode=42"
  */
-module_param (fifo_mode, uint, 0);
-MODULE_PARM_DESC (fifo_mode, "endpoint configuration");
+module_param(fifo_mode, uint, 0);
+MODULE_PARM_DESC(fifo_mode, "endpoint configuration");
 
 #ifdef USE_DMA
 static bool use_dma = 1;
@@ -103,8 +99,8 @@ static bool use_dma = 1;
 /* "modprobe omap_udc use_dma=y", or else as a kernel
  * boot parameter "omap_udc:use_dma=y"
  */
-module_param (use_dma, bool, 0);
-MODULE_PARM_DESC (use_dma, "enable/disable DMA");
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "enable/disable DMA");
 #else  /* !USE_DMA */
 
 /* save a bit of code */
@@ -112,8 +108,8 @@ MODULE_PARM_DESC (use_dma, "enable/disable DMA");
 #endif /* !USE_DMA */
 
 
-static const char driver_name [] = "omap_udc";
-static const char driver_desc [] = DRIVER_DESC;
+static const char driver_name[] = "omap_udc";
+static const char driver_desc[] = DRIVER_DESC;
 
 /*-------------------------------------------------------------------------*/
 
@@ -251,7 +247,7 @@ static int omap_ep_disable(struct usb_ep *_ep)
 
        spin_lock_irqsave(&ep->udc->lock, flags);
        ep->ep.desc = NULL;
-       nuke (ep, -ESHUTDOWN);
+       nuke(ep, -ESHUTDOWN);
        ep->ep.maxpacket = ep->maxpacket;
        ep->has_dma = 0;
        omap_writew(UDC_SET_HALT, UDC_CTRL);
@@ -272,10 +268,11 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
        struct omap_req *req;
 
        req = kzalloc(sizeof(*req), gfp_flags);
-       if (req) {
-               req->req.dma = DMA_ADDR_INVALID;
-               INIT_LIST_HEAD (&req->queue);
-       }
+       if (!req)
+               return NULL;
+
+       INIT_LIST_HEAD(&req->queue);
+
        return &req->req;
 }
 
@@ -284,8 +281,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req)
 {
        struct omap_req *req = container_of(_req, struct omap_req, req);
 
-       if (_req)
-               kfree (req);
+       kfree(req);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -293,6 +289,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req)
 static void
 done(struct omap_ep *ep, struct omap_req *req, int status)
 {
+       struct omap_udc         *udc = ep->udc;
        unsigned                stopped = ep->stopped;
 
        list_del_init(&req->queue);
@@ -302,22 +299,9 @@ done(struct omap_ep *ep, struct omap_req *req, int status)
        else
                status = req->req.status;
 
-       if (use_dma && ep->has_dma) {
-               if (req->mapped) {
-                       dma_unmap_single(ep->udc->gadget.dev.parent,
-                               req->req.dma, req->req.length,
-                               (ep->bEndpointAddress & USB_DIR_IN)
-                                       ? DMA_TO_DEVICE
-                                       : DMA_FROM_DEVICE);
-                       req->req.dma = DMA_ADDR_INVALID;
-                       req->mapped = 0;
-               } else
-                       dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
-                               req->req.dma, req->req.length,
-                               (ep->bEndpointAddress & USB_DIR_IN)
-                                       ? DMA_TO_DEVICE
-                                       : DMA_FROM_DEVICE);
-       }
+       if (use_dma && ep->has_dma)
+               usb_gadget_unmap_request(&udc->gadget, &req->req,
+                               (ep->bEndpointAddress & USB_DIR_IN));
 
 #ifndef        USB_TRACE
        if (status && status != -ESHUTDOWN)
@@ -365,10 +349,10 @@ write_packet(u8 *buf, struct omap_req *req, unsigned max)
        return len;
 }
 
-// FIXME change r/w fifo calling convention
+/* FIXME change r/w fifo calling convention */
 
 
-// return:  0 = still running, 1 = completed, negative = errno
+/* return:  0 = still running, 1 = completed, negative = errno */
 static int write_fifo(struct omap_ep *ep, struct omap_req *req)
 {
        u8              *buf;
@@ -430,7 +414,7 @@ read_packet(u8 *buf, struct omap_req *req, unsigned avail)
        return len;
 }
 
-// return:  0 = still running, 1 = queue empty, negative = errno
+/* return:  0 = still running, 1 = queue empty, negative = errno */
 static int read_fifo(struct omap_ep *ep, struct omap_req *req)
 {
        u8              *buf;
@@ -537,12 +521,8 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
                                : OMAP_DMA_SYNC_ELEMENT;
        int             dma_trigger = 0;
 
-       if (cpu_is_omap24xx())
-               dma_trigger = OMAP24XX_DMA(USB_W2FC_TX0, ep->dma_channel);
-
        /* measure length in either bytes or packets */
        if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC)
-                       || (cpu_is_omap24xx() && length < ep->maxpacket)
                        || (cpu_is_omap15xx() && length < ep->maxpacket)) {
                txdma_ctrl = UDC_TXN_EOT | length;
                omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
@@ -601,28 +581,14 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
        int dma_trigger = 0;
        u16 w;
 
-       if (cpu_is_omap24xx())
-               dma_trigger = OMAP24XX_DMA(USB_W2FC_RX0, ep->dma_channel);
-
-       /* NOTE:  we filtered out "short reads" before, so we know
-        * the buffer has only whole numbers of packets.
-        * except MODE SELECT(6) sent the 24 bytes data in OMAP24XX DMA mode
-        */
-       if (cpu_is_omap24xx() && packets < ep->maxpacket) {
-               omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
-                               packets, 1, OMAP_DMA_SYNC_ELEMENT,
-                               dma_trigger, 0);
-               req->dma_bytes = packets;
-       } else {
-               /* set up this DMA transfer, enable the fifo, start */
-               packets /= ep->ep.maxpacket;
-               packets = min(packets, (unsigned)UDC_RXN_TC + 1);
-               req->dma_bytes = packets * ep->ep.maxpacket;
-               omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
-                               ep->ep.maxpacket >> 1, packets,
-                               OMAP_DMA_SYNC_ELEMENT,
-                               dma_trigger, 0);
-       }
+       /* set up this DMA transfer, enable the fifo, start */
+       packets /= ep->ep.maxpacket;
+       packets = min(packets, (unsigned)UDC_RXN_TC + 1);
+       req->dma_bytes = packets * ep->ep.maxpacket;
+       omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
+                       ep->ep.maxpacket >> 1, packets,
+                       OMAP_DMA_SYNC_ELEMENT,
+                       dma_trigger, 0);
        omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
                OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
                0, 0);
@@ -684,7 +650,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src)
                }
                omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC);
 
-               if (!list_empty (&ep->queue)) {
+               if (!list_empty(&ep->queue)) {
                        req = container_of(ep->queue.next,
                                        struct omap_req, queue);
                        next_in_dma(ep, req);
@@ -703,7 +669,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src)
                }
                omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC);
 
-               if (!list_empty (&ep->queue)) {
+               if (!list_empty(&ep->queue)) {
                        req = container_of(ep->queue.next,
                                        struct omap_req, queue);
                        next_out_dma(ep, req);
@@ -761,10 +727,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
        ep->dma_channel = channel;
 
        if (is_in) {
-               if (cpu_is_omap24xx())
-                       dma_channel = OMAP24XX_DMA(USB_W2FC_TX0, channel);
-               else
-                       dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel;
+               dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel;
                status = omap_request_dma(dma_channel,
                        ep->ep.name, dma_error, ep, &ep->lch);
                if (status == 0) {
@@ -781,11 +744,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
                                0, 0);
                }
        } else {
-               if (cpu_is_omap24xx())
-                       dma_channel = OMAP24XX_DMA(USB_W2FC_RX0, channel);
-               else
-                       dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel;
-
+               dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel;
                status = omap_request_dma(dma_channel,
                        ep->ep.name, dma_error, ep, &ep->lch);
                if (status == 0) {
@@ -809,7 +768,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
                omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ);
 
                /* channel type P: hw synch (fifo) */
-               if (cpu_class_is_omap1() && !cpu_is_omap15xx())
+               if (!cpu_is_omap15xx())
                        omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P);
        }
 
@@ -929,13 +888,11 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 
        /* this isn't bogus, but OMAP DMA isn't the only hardware to
         * have a hard time with partial packet reads...  reject it.
-        * Except OMAP2 can handle the small packets.
         */
        if (use_dma
                        && ep->has_dma
                        && ep->bEndpointAddress != 0
                        && (ep->bEndpointAddress & USB_DIR_IN) == 0
-                       && !cpu_class_is_omap2()
                        && (req->req.length % ep->ep.maxpacket) != 0) {
                DBG("%s, no partial packet OUT reads\n", __func__);
                return -EMSGSIZE;
@@ -945,26 +902,9 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
        if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
                return -ESHUTDOWN;
 
-       if (use_dma && ep->has_dma) {
-               if (req->req.dma == DMA_ADDR_INVALID) {
-                       req->req.dma = dma_map_single(
-                               ep->udc->gadget.dev.parent,
-                               req->req.buf,
-                               req->req.length,
-                               (ep->bEndpointAddress & USB_DIR_IN)
-                                       ? DMA_TO_DEVICE
-                                       : DMA_FROM_DEVICE);
-                       req->mapped = 1;
-               } else {
-                       dma_sync_single_for_device(
-                               ep->udc->gadget.dev.parent,
-                               req->req.dma, req->req.length,
-                               (ep->bEndpointAddress & USB_DIR_IN)
-                                       ? DMA_TO_DEVICE
-                                       : DMA_FROM_DEVICE);
-                       req->mapped = 0;
-               }
-       }
+       if (use_dma && ep->has_dma)
+               usb_gadget_map_request(&udc->gadget, &req->req,
+                               (ep->bEndpointAddress & USB_DIR_IN));
 
        VDBG("%s queue req %p, len %d buf %p\n",
                ep->ep.name, _req, _req->length, _req->buf);
@@ -985,7 +925,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
                int     is_in;
 
                if (ep->bEndpointAddress == 0) {
-                       if (!udc->ep0_pending || !list_empty (&ep->queue)) {
+                       if (!udc->ep0_pending || !list_empty(&ep->queue)) {
                                spin_unlock_irqrestore(&udc->lock, flags);
                                return -EL2HLT;
                        }
@@ -1012,7 +952,8 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
                                 * always an IN ... even for IN transfers,
                                 * a weird case which seem to stall OMAP.
                                 */
-                               omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM);
+                               omap_writew(UDC_EP_SEL | UDC_EP_DIR,
+                                               UDC_EP_NUM);
                                omap_writew(UDC_CLR_EP, UDC_CTRL);
                                omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
                                omap_writew(UDC_EP_DIR, UDC_EP_NUM);
@@ -1024,7 +965,8 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 
                        /* non-empty DATA stage */
                        } else if (is_in) {
-                               omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM);
+                               omap_writew(UDC_EP_SEL | UDC_EP_DIR,
+                                               UDC_EP_NUM);
                        } else {
                                if (udc->ep0_setup)
                                        goto irq_wait;
@@ -1072,7 +1014,7 @@ static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
        spin_lock_irqsave(&ep->udc->lock, flags);
 
        /* make sure it's actually queued on this endpoint */
-       list_for_each_entry (req, &ep->queue, queue) {
+       list_for_each_entry(req, &ep->queue, queue) {
                if (&req->req == _req)
                        break;
        }
@@ -1179,8 +1121,8 @@ static struct usb_ep_ops omap_ep_ops = {
        .dequeue        = omap_ep_dequeue,
 
        .set_halt       = omap_ep_set_halt,
-       // fifo_status ... report bytes in fifo
-       // fifo_flush ... flush fifo
+       /* fifo_status ... report bytes in fifo */
+       /* fifo_flush ... flush fifo */
 };
 
 /*-------------------------------------------------------------------------*/
@@ -1212,7 +1154,7 @@ static int omap_wakeup(struct usb_gadget *gadget)
 
        /* NOTE:  non-OTG systems may use SRP TOO... */
        } else if (!(udc->devstat & UDC_ATT)) {
-               if (udc->transceiver)
+               if (!IS_ERR_OR_NULL(udc->transceiver))
                        retval = otg_start_srp(udc->transceiver->otg);
        }
        spin_unlock_irqrestore(&udc->lock, flags);
@@ -1344,7 +1286,7 @@ static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA)
        struct omap_udc *udc;
 
        udc = container_of(gadget, struct omap_udc, gadget);
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                return usb_phy_set_power(udc->transceiver, mA);
        return -EOPNOTSUPP;
 }
@@ -1410,7 +1352,7 @@ static void udc_quiesce(struct omap_udc *udc)
 
        udc->gadget.speed = USB_SPEED_UNKNOWN;
        nuke(&udc->ep[0], -ESHUTDOWN);
-       list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list)
+       list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list)
                nuke(ep, -ESHUTDOWN);
 }
 
@@ -1526,7 +1468,8 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
                                /* read next OUT packet of request, maybe
                                 * reactiviting the fifo; stall on errors.
                                 */
-                               if (!req || (stat = read_fifo(ep0, req)) < 0) {
+                               stat = read_fifo(ep0, req);
+                               if (!req || stat < 0) {
                                        omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
                                        udc->ep0_pending = 0;
                                        stat = 0;
@@ -1659,7 +1602,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
                                /* this has rude side-effects (aborts) and
                                 * can't really work if DMA-IN is active
                                 */
-                               DBG("%s host set_halt, NYET \n", ep->name);
+                               DBG("%s host set_halt, NYET\n", ep->name);
                                goto do_stall;
                        }
                        use_ep(ep, 0);
@@ -1750,7 +1693,7 @@ delegate:
                         */
                        udc->ep0_setup = 1;
                        spin_unlock(&udc->lock);
-                       status = udc->driver->setup (&udc->gadget, &u.r);
+                       status = udc->driver->setup(&udc->gadget, &u.r);
                        spin_lock(&udc->lock);
                        udc->ep0_setup = 0;
                }
@@ -1793,12 +1736,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
                        if (devstat & UDC_ATT) {
                                udc->gadget.speed = USB_SPEED_FULL;
                                VDBG("connect\n");
-                               if (!udc->transceiver)
+                               if (IS_ERR_OR_NULL(udc->transceiver))
                                        pullup_enable(udc);
-                               // if (driver->connect) call it
+                               /* if (driver->connect) call it */
                        } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
                                udc->gadget.speed = USB_SPEED_UNKNOWN;
-                               if (!udc->transceiver)
+                               if (IS_ERR_OR_NULL(udc->transceiver))
                                        pullup_disable(udc);
                                DBG("disconnect, gadget %s\n",
                                        udc->driver->driver.name);
@@ -1827,7 +1770,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
        }
        if (change & UDC_SUS) {
                if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
-                       // FIXME tell isp1301 to suspend/resume (?)
+                       /* FIXME tell isp1301 to suspend/resume (?) */
                        if (devstat & UDC_SUS) {
                                VDBG("suspend\n");
                                update_otg(udc);
@@ -1838,12 +1781,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
                                        udc->driver->suspend(&udc->gadget);
                                        spin_lock(&udc->lock);
                                }
-                               if (udc->transceiver)
+                               if (!IS_ERR_OR_NULL(udc->transceiver))
                                        usb_phy_set_suspend(
                                                        udc->transceiver, 1);
                        } else {
                                VDBG("resume\n");
-                               if (udc->transceiver)
+                               if (!IS_ERR_OR_NULL(udc->transceiver))
                                        usb_phy_set_suspend(
                                                        udc->transceiver, 0);
                                if (udc->gadget.speed == USB_SPEED_FULL
@@ -2030,7 +1973,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
        spin_lock_irqsave(&udc->lock, flags);
 
        /* handle all non-DMA ISO transfers */
-       list_for_each_entry (ep, &udc->iso, iso) {
+       list_for_each_entry(ep, &udc->iso, iso) {
                u16             stat;
                struct omap_req *req;
 
@@ -2089,15 +2032,11 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
 
 static inline int machine_without_vbus_sense(void)
 {
-       return (machine_is_omap_innovator()
+       return machine_is_omap_innovator()
                || machine_is_omap_osk()
-               || machine_is_omap_apollon()
-#ifndef CONFIG_MACH_OMAP_H4_OTG
-               || machine_is_omap_h4()
-#endif
                || machine_is_sx1()
-               || cpu_is_omap7xx() /* No known omap7xx boards with vbus sense */
-               );
+               /* No known omap7xx boards with vbus sense */
+               || cpu_is_omap7xx();
 }
 
 static int omap_udc_start(struct usb_gadget_driver *driver,
@@ -2111,7 +2050,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
        if (!udc)
                return -ENODEV;
        if (!driver
-                       // FIXME if otg, check:  driver->is_otg
+                       /* FIXME if otg, check:  driver->is_otg */
                        || driver->max_speed < USB_SPEED_FULL
                        || !bind || !driver->setup)
                return -EINVAL;
@@ -2123,7 +2062,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
        }
 
        /* reset state */
-       list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {
+       list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
                ep->irqs = 0;
                if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
                        continue;
@@ -2155,13 +2094,13 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
        omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC);
 
        /* connect to bus through transceiver */
-       if (udc->transceiver) {
+       if (!IS_ERR_OR_NULL(udc->transceiver)) {
                status = otg_set_peripheral(udc->transceiver->otg,
                                                &udc->gadget);
                if (status < 0) {
                        ERR("can't bind to transceiver\n");
                        if (driver->unbind) {
-                               driver->unbind (&udc->gadget);
+                               driver->unbind(&udc->gadget);
                                udc->gadget.dev.driver = NULL;
                                udc->driver = NULL;
                        }
@@ -2169,9 +2108,9 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
                }
        } else {
                if (can_pullup(udc))
-                       pullup_enable (udc);
+                       pullup_enable(udc);
                else
-                       pullup_disable (udc);
+                       pullup_disable(udc);
        }
 
        /* boards that don't have VBUS sensing can't autogate 48MHz;
@@ -2202,7 +2141,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver)
        if (machine_without_vbus_sense())
                omap_vbus_session(&udc->gadget, 0);
 
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                (void) otg_set_peripheral(udc->transceiver->otg, NULL);
        else
                pullup_disable(udc);
@@ -2230,7 +2169,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver)
 static const char proc_filename[] = "driver/udc";
 
 #define FOURBITS "%s%s%s%s"
-#define EIGHTBITS FOURBITS FOURBITS
+#define EIGHTBITS "%s%s%s%s%s%s%s%s"
 
 static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
 {
@@ -2252,12 +2191,21 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
                "\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n",
                ep->name, buf,
                ep->double_buf ? "dbuf " : "",
-               ({char *s; switch(ep->ackwait){
-               case 0: s = ""; break;
-               case 1: s = "(ackw) "; break;
-               case 2: s = "(ackw2) "; break;
-               default: s = "(?) "; break;
-               } s;}),
+               ({ char *s;
+               switch (ep->ackwait) {
+               case 0:
+                       s = "";
+                       break;
+               case 1:
+                       s = "(ackw) ";
+                       break;
+               case 2:
+                       s = "(ackw2) ";
+                       break;
+               default:
+                       s = "(?) ";
+                       break;
+               } s; }),
                ep->irqs, stat_flg,
                (stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "",
                (stat_flg & UDC_MISS_IN) ? "miss_in " : "",
@@ -2273,10 +2221,10 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
                (stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "",
                (stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : "");
 
-       if (list_empty (&ep->queue))
+       if (list_empty(&ep->queue))
                seq_printf(s, "\t(queue empty)\n");
        else
-               list_for_each_entry (req, &ep->queue, queue) {
+               list_for_each_entry(req, &ep->queue, queue) {
                        unsigned        length = req->req.actual;
 
                        if (use_dma && buf[0]) {
@@ -2294,11 +2242,16 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
 static char *trx_mode(unsigned m, int enabled)
 {
        switch (m) {
-       case 0:         return enabled ? "*6wire" : "unused";
-       case 1:         return "4wire";
-       case 2:         return "3wire";
-       case 3:         return "6wire";
-       default:        return "unknown";
+       case 0:
+               return enabled ? "*6wire" : "unused";
+       case 1:
+               return "4wire";
+       case 2:
+               return "3wire";
+       case 3:
+               return "6wire";
+       default:
+               return "unknown";
        }
 }
 
@@ -2308,12 +2261,9 @@ static int proc_otg_show(struct seq_file *s)
        u32             trans = 0;
        char            *ctrl_name = "(UNKNOWN)";
 
-       /* XXX This needs major revision for OMAP2+ */
        tmp = omap_readl(OTG_REV);
-       if (cpu_class_is_omap1()) {
-               ctrl_name = "tranceiver_ctrl";
-               trans = omap_readw(USB_TRANSCEIVER_CTRL);
-       }
+       ctrl_name = "tranceiver_ctrl";
+       trans = omap_readw(USB_TRANSCEIVER_CTRL);
        seq_printf(s, "\nOTG rev %d.%d, %s %05x\n",
                tmp >> 4, tmp & 0xf, ctrl_name, trans);
        tmp = omap_readw(OTG_SYSCON_1);
@@ -2333,7 +2283,7 @@ static int proc_otg_show(struct seq_file *s)
                        " b_ase_brst=%d hmc=%d\n", tmp,
                (tmp & OTG_EN) ? " otg_en" : "",
                (tmp & USBX_SYNCHRO) ? " synchro" : "",
-               // much more SRP stuff
+               /* much more SRP stuff */
                (tmp & SRP_DATA) ? " srp_data" : "",
                (tmp & SRP_VBUS) ? " srp_vbus" : "",
                (tmp & OTG_PADEN) ? " otg_paden" : "",
@@ -2400,14 +2350,12 @@ static int proc_udc_show(struct seq_file *s, void *_)
                HMC,
                udc->transceiver
                        ? udc->transceiver->label
-                       : ((cpu_is_omap1710() || cpu_is_omap24xx())
+                       : (cpu_is_omap1710()
                                ? "external" : "(none)"));
-       if (cpu_class_is_omap1()) {
-               seq_printf(s, "ULPD control %04x req %04x status %04x\n",
-                       omap_readw(ULPD_CLOCK_CTRL),
-                       omap_readw(ULPD_SOFT_REQ),
-                       omap_readw(ULPD_STATUS_REQ));
-       }
+       seq_printf(s, "ULPD control %04x req %04x status %04x\n",
+               omap_readw(ULPD_CLOCK_CTRL),
+               omap_readw(ULPD_SOFT_REQ),
+               omap_readw(ULPD_STATUS_REQ));
 
        /* OTG controller registers */
        if (!cpu_is_omap15xx())
@@ -2423,7 +2371,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
                (tmp & UDC_SELF_PWR) ? " self_pwr" : "",
                (tmp & UDC_SOFF_DIS) ? " soff_dis" : "",
                (tmp & UDC_PULLUP_EN) ? " PULLUP" : "");
-       // syscon2 is write-only
+       /* syscon2 is write-only */
 
        /* UDC controller registers */
        if (!(tmp & UDC_PULLUP_EN)) {
@@ -2507,7 +2455,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
        if (tmp & UDC_ATT) {
                proc_ep_show(s, &udc->ep[0]);
                if (tmp & UDC_ADD) {
-                       list_for_each_entry (ep, &udc->gadget.ep_list,
+                       list_for_each_entry(ep, &udc->gadget.ep_list,
                                        ep.ep_list) {
                                if (ep->ep.desc)
                                        proc_ep_show(s, ep);
@@ -2558,7 +2506,7 @@ static inline void remove_proc_file(void) {}
  * UDC_SYSCON_1.CFG_LOCK is set can now work.  We won't use that
  * capability yet though.
  */
-static unsigned __init
+static unsigned __devinit
 omap_ep_setup(char *name, u8 addr, u8 type,
                unsigned buf, unsigned maxp, int dbuf)
 {
@@ -2576,14 +2524,29 @@ omap_ep_setup(char *name, u8 addr, u8 type,
        /* chip setup ... bit values are same for IN, OUT */
        if (type == USB_ENDPOINT_XFER_ISOC) {
                switch (maxp) {
-               case 8:         epn_rxtx = 0 << 12; break;
-               case 16:        epn_rxtx = 1 << 12; break;
-               case 32:        epn_rxtx = 2 << 12; break;
-               case 64:        epn_rxtx = 3 << 12; break;
-               case 128:       epn_rxtx = 4 << 12; break;
-               case 256:       epn_rxtx = 5 << 12; break;
-               case 512:       epn_rxtx = 6 << 12; break;
-               default:        BUG();
+               case 8:
+                       epn_rxtx = 0 << 12;
+                       break;
+               case 16:
+                       epn_rxtx = 1 << 12;
+                       break;
+               case 32:
+                       epn_rxtx = 2 << 12;
+                       break;
+               case 64:
+                       epn_rxtx = 3 << 12;
+                       break;
+               case 128:
+                       epn_rxtx = 4 << 12;
+                       break;
+               case 256:
+                       epn_rxtx = 5 << 12;
+                       break;
+               case 512:
+                       epn_rxtx = 6 << 12;
+                       break;
+               default:
+                       BUG();
                }
                epn_rxtx |= UDC_EPN_RX_ISO;
                dbuf = 1;
@@ -2592,15 +2555,24 @@ omap_ep_setup(char *name, u8 addr, u8 type,
                 * and ignored for PIO-IN on newer chips
                 * (for more reliable behavior)
                 */
-               if (!use_dma || cpu_is_omap15xx() || cpu_is_omap24xx())
+               if (!use_dma || cpu_is_omap15xx())
                        dbuf = 0;
 
                switch (maxp) {
-               case 8:         epn_rxtx = 0 << 12; break;
-               case 16:        epn_rxtx = 1 << 12; break;
-               case 32:        epn_rxtx = 2 << 12; break;
-               case 64:        epn_rxtx = 3 << 12; break;
-               default:        BUG();
+               case 8:
+                       epn_rxtx = 0 << 12;
+                       break;
+               case 16:
+                       epn_rxtx = 1 << 12;
+                       break;
+               case 32:
+                       epn_rxtx = 2 << 12;
+                       break;
+               case 64:
+                       epn_rxtx = 3 << 12;
+                       break;
+               default:
+                       BUG();
                }
                if (dbuf && addr)
                        epn_rxtx |= UDC_EPN_RX_DB;
@@ -2640,7 +2612,7 @@ omap_ep_setup(char *name, u8 addr, u8 type,
        ep->ep.name = ep->name;
        ep->ep.ops = &omap_ep_ops;
        ep->ep.maxpacket = ep->maxpacket = maxp;
-       list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list);
+       list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
 
        return buf;
 }
@@ -2648,11 +2620,11 @@ omap_ep_setup(char *name, u8 addr, u8 type,
 static void omap_udc_release(struct device *dev)
 {
        complete(udc->done);
-       kfree (udc);
+       kfree(udc);
        udc = NULL;
 }
 
-static int __init
+static int __devinit
 omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
 {
        unsigned        tmp, buf;
@@ -2666,13 +2638,13 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
        omap_writew(0, UDC_TXDMA_CFG);
 
        /* UDC_PULLUP_EN gates the chip clock */
-       // OTG_SYSCON_1 |= DEV_IDLE_EN;
+       /* OTG_SYSCON_1 |= DEV_IDLE_EN; */
 
        udc = kzalloc(sizeof(*udc), GFP_KERNEL);
        if (!udc)
                return -ENOMEM;
 
-       spin_lock_init (&udc->lock);
+       spin_lock_init(&udc->lock);
 
        udc->gadget.ops = &omap_gadget_ops;
        udc->gadget.ep0 = &udc->ep[0].ep;
@@ -2702,13 +2674,13 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
                omap_writew(0, UDC_EP_TX(tmp));
        }
 
-#define OMAP_BULK_EP(name,addr) \
+#define OMAP_BULK_EP(name, addr) \
        buf = omap_ep_setup(name "-bulk", addr, \
                        USB_ENDPOINT_XFER_BULK, buf, 64, 1);
-#define OMAP_INT_EP(name,addr, maxp) \
+#define OMAP_INT_EP(name, addr, maxp) \
        buf = omap_ep_setup(name "-int", addr, \
                        USB_ENDPOINT_XFER_INT, buf, maxp, 0);
-#define OMAP_ISO_EP(name,addr, maxp) \
+#define OMAP_ISO_EP(name, addr, maxp) \
        buf = omap_ep_setup(name "-iso", addr, \
                        USB_ENDPOINT_XFER_ISOC, buf, maxp, 1);
 
@@ -2789,15 +2761,18 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
        return 0;
 }
 
-static int __init omap_udc_probe(struct platform_device *pdev)
+static int __devinit omap_udc_probe(struct platform_device *pdev)
 {
        int                     status = -ENODEV;
        int                     hmc;
        struct usb_phy          *xceiv = NULL;
        const char              *type = NULL;
        struct omap_usb_config  *config = pdev->dev.platform_data;
-       struct clk              *dc_clk;
-       struct clk              *hhc_clk;
+       struct clk              *dc_clk = NULL;
+       struct clk              *hhc_clk = NULL;
+
+       if (cpu_is_omap7xx())
+               use_dma = 0;
 
        /* NOTE:  "knows" the order of the resources! */
        if (!request_mem_region(pdev->resource[0].start,
@@ -2817,16 +2792,6 @@ static int __init omap_udc_probe(struct platform_device *pdev)
                udelay(100);
        }
 
-       if (cpu_is_omap24xx()) {
-               dc_clk = clk_get(&pdev->dev, "usb_fck");
-               hhc_clk = clk_get(&pdev->dev, "usb_l4_ick");
-               BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
-               /* can't use omap_udc_enable_clock yet */
-               clk_enable(dc_clk);
-               clk_enable(hhc_clk);
-               udelay(100);
-       }
-
        if (cpu_is_omap7xx()) {
                dc_clk = clk_get(&pdev->dev, "usb_dc_ck");
                hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck");
@@ -2866,8 +2831,8 @@ static int __init omap_udc_probe(struct platform_device *pdev)
                 * use it.  Except for OTG, we don't _need_ to talk to one;
                 * but not having one probably means no VBUS detection.
                 */
-               xceiv = usb_get_transceiver();
-               if (xceiv)
+               xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+               if (!IS_ERR_OR_NULL(xceiv))
                        type = xceiv->label;
                else if (config->otg) {
                        DBG("OTG requires external transceiver!\n");
@@ -2876,14 +2841,6 @@ static int __init omap_udc_probe(struct platform_device *pdev)
 
                hmc = HMC_1610;
 
-               if (cpu_is_omap24xx()) {
-                       /* this could be transceiverless in one of the
-                        * "we don't need to know" modes.
-                        */
-                       type = "external";
-                       goto known;
-               }
-
                switch (hmc) {
                case 0:                 /* POWERUP DEFAULT == 0 */
                case 4:
@@ -2899,7 +2856,7 @@ static int __init omap_udc_probe(struct platform_device *pdev)
                case 16:
                case 19:
                case 25:
-                       if (!xceiv) {
+                       if (IS_ERR_OR_NULL(xceiv)) {
                                DBG("external transceiver not registered!\n");
                                type = "unknown";
                        }
@@ -2922,16 +2879,16 @@ bad_on_1710:
                        goto cleanup0;
                }
        }
-known:
+
        INFO("hmc mode %d, %s transceiver\n", hmc, type);
 
        /* a "gadget" abstracts/virtualizes the controller */
        status = omap_udc_setup(pdev, xceiv);
-       if (status) {
+       if (status)
                goto cleanup0;
-       }
+
        xceiv = NULL;
-       // "udc" is now valid
+       /* "udc" is now valid */
        pullup_disable(udc);
 #if    defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
        udc->gadget.is_otg = (config->otg != 0);
@@ -2945,7 +2902,7 @@ known:
 
        /* USB general purpose IRQ:  ep0, state changes, dma, etc */
        status = request_irq(pdev->resource[1].start, omap_udc_irq,
-                       IRQF_SAMPLE_RANDOM, driver_name, udc);
+                       0, driver_name, udc);
        if (status != 0) {
                ERR("can't get irq %d, err %d\n",
                        (int) pdev->resource[1].start, status);
@@ -2954,7 +2911,7 @@ known:
 
        /* USB "non-iso" IRQ (PIO for all but ep0) */
        status = request_irq(pdev->resource[2].start, omap_udc_pio_irq,
-                       IRQF_SAMPLE_RANDOM, "omap_udc pio", udc);
+                       0, "omap_udc pio", udc);
        if (status != 0) {
                ERR("can't get irq %d, err %d\n",
                        (int) pdev->resource[2].start, status);
@@ -2976,16 +2933,6 @@ known:
                clk_disable(dc_clk);
        }
 
-       if (cpu_is_omap24xx()) {
-               udc->dc_clk = dc_clk;
-               udc->hhc_clk = hhc_clk;
-               /* FIXME OMAP2 don't release hhc & dc clock */
-#if 0
-               clk_disable(hhc_clk);
-               clk_disable(dc_clk);
-#endif
-       }
-
        create_proc_file();
        status = device_add(&udc->gadget.dev);
        if (status)
@@ -3007,14 +2954,14 @@ cleanup2:
        free_irq(pdev->resource[1].start, udc);
 
 cleanup1:
-       kfree (udc);
+       kfree(udc);
        udc = NULL;
 
 cleanup0:
-       if (xceiv)
-               usb_put_transceiver(xceiv);
+       if (!IS_ERR_OR_NULL(xceiv))
+               usb_put_phy(xceiv);
 
-       if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) {
+       if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
                clk_disable(hhc_clk);
                clk_disable(dc_clk);
                clk_put(hhc_clk);
@@ -3027,7 +2974,7 @@ cleanup0:
        return status;
 }
 
-static int __exit omap_udc_remove(struct platform_device *pdev)
+static int __devexit omap_udc_remove(struct platform_device *pdev)
 {
        DECLARE_COMPLETION_ONSTACK(done);
 
@@ -3041,8 +2988,8 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
        udc->done = &done;
 
        pullup_disable(udc);
-       if (udc->transceiver) {
-               usb_put_transceiver(udc->transceiver);
+       if (!IS_ERR_OR_NULL(udc->transceiver)) {
+               usb_put_phy(udc->transceiver);
                udc->transceiver = NULL;
        }
        omap_writew(0, UDC_SYSCON1);
@@ -3112,7 +3059,8 @@ static int omap_udc_resume(struct platform_device *dev)
 /*-------------------------------------------------------------------------*/
 
 static struct platform_driver udc_driver = {
-       .remove         = __exit_p(omap_udc_remove),
+       .probe          = omap_udc_probe,
+       .remove         = __devexit_p(omap_udc_remove),
        .suspend        = omap_udc_suspend,
        .resume         = omap_udc_resume,
        .driver         = {
@@ -3121,27 +3069,7 @@ static struct platform_driver udc_driver = {
        },
 };
 
-static int __init udc_init(void)
-{
-       /* Disable DMA for omap7xx -- it doesn't work right. */
-       if (cpu_is_omap7xx())
-               use_dma = 0;
-
-       INFO("%s, version: " DRIVER_VERSION
-#ifdef USE_ISO
-               " (iso)"
-#endif
-               "%s\n", driver_desc,
-               use_dma ?  " (dma)" : "");
-       return platform_driver_probe(&udc_driver, omap_udc_probe);
-}
-module_init(udc_init);
-
-static void __exit udc_exit(void)
-{
-       platform_driver_unregister(&udc_driver);
-}
-module_exit(udc_exit);
+module_platform_driver(udc_driver);
 
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
index 1cfcc9e..f4fb71c 100644 (file)
@@ -2208,7 +2208,7 @@ static void pch_udc_complete_receiver(struct pch_udc_ep *ep)
                        return;
                }
                if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE)
-                       if (td->status | PCH_UDC_DMA_LAST) {
+                       if (td->status & PCH_UDC_DMA_LAST) {
                                count = td->status & PCH_UDC_RXTX_BYTES;
                                break;
                        }
index f7ff9e8..53c093b 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/ioport.h>
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
@@ -993,7 +994,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
 
        udc = container_of(_gadget, struct pxa25x_udc, gadget);
 
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                return usb_phy_set_power(udc->transceiver, mA);
        return -EOPNOTSUPP;
 }
@@ -1299,7 +1300,7 @@ fail:
        DMSG("registered gadget driver '%s'\n", driver->driver.name);
 
        /* connect to bus through transceiver */
-       if (dev->transceiver) {
+       if (!IS_ERR_OR_NULL(dev->transceiver)) {
                retval = otg_set_peripheral(dev->transceiver->otg,
                                                &dev->gadget);
                if (retval) {
@@ -1359,7 +1360,7 @@ static int pxa25x_stop(struct usb_gadget_driver *driver)
        stop_activity(dev, driver);
        local_irq_enable();
 
-       if (dev->transceiver)
+       if (!IS_ERR_OR_NULL(dev->transceiver))
                (void) otg_set_peripheral(dev->transceiver->otg, NULL);
 
        driver->unbind(&dev->gadget);
@@ -2159,7 +2160,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
        dev->dev = &pdev->dev;
        dev->mach = pdev->dev.platform_data;
 
-       dev->transceiver = usb_get_transceiver();
+       dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
 
        if (gpio_is_valid(dev->mach->gpio_pullup)) {
                if ((retval = gpio_request(dev->mach->gpio_pullup,
@@ -2237,8 +2238,8 @@ lubbock_fail0:
        if (gpio_is_valid(dev->mach->gpio_pullup))
                gpio_free(dev->mach->gpio_pullup);
  err_gpio_pullup:
-       if (dev->transceiver) {
-               usb_put_transceiver(dev->transceiver);
+       if (!IS_ERR_OR_NULL(dev->transceiver)) {
+               usb_put_phy(dev->transceiver);
                dev->transceiver = NULL;
        }
        clk_put(dev->clk);
@@ -2279,8 +2280,8 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
 
        clk_put(dev->clk);
 
-       if (dev->transceiver) {
-               usb_put_transceiver(dev->transceiver);
+       if (!IS_ERR_OR_NULL(dev->transceiver)) {
+               usb_put_phy(dev->transceiver);
                dev->transceiver = NULL;
        }
 
index 98acb3a..644b430 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/list.h>
@@ -1573,7 +1574,7 @@ static int should_enable_udc(struct pxa_udc *udc)
        int put_on;
 
        put_on = ((udc->pullup_on) && (udc->driver));
-       put_on &= ((udc->vbus_sensed) || (!udc->transceiver));
+       put_on &= ((udc->vbus_sensed) || (IS_ERR_OR_NULL(udc->transceiver)));
        return put_on;
 }
 
@@ -1594,7 +1595,7 @@ static int should_disable_udc(struct pxa_udc *udc)
        int put_off;
 
        put_off = ((!udc->pullup_on) || (!udc->driver));
-       put_off |= ((!udc->vbus_sensed) && (udc->transceiver));
+       put_off |= ((!udc->vbus_sensed) && (!IS_ERR_OR_NULL(udc->transceiver)));
        return put_off;
 }
 
@@ -1665,7 +1666,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
        struct pxa_udc *udc;
 
        udc = to_gadget_udc(_gadget);
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                return usb_phy_set_power(udc->transceiver, mA);
        return -EOPNOTSUPP;
 }
@@ -1834,7 +1835,7 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver,
        dev_dbg(udc->dev, "registered gadget driver '%s'\n",
                driver->driver.name);
 
-       if (udc->transceiver) {
+       if (!IS_ERR_OR_NULL(udc->transceiver)) {
                retval = otg_set_peripheral(udc->transceiver->otg,
                                                &udc->gadget);
                if (retval) {
@@ -1908,7 +1909,7 @@ static int pxa27x_udc_stop(struct usb_gadget_driver *driver)
        dev_info(udc->dev, "unregistered gadget driver '%s'\n",
                 driver->driver.name);
 
-       if (udc->transceiver)
+       if (!IS_ERR_OR_NULL(udc->transceiver))
                return otg_set_peripheral(udc->transceiver->otg, NULL);
        return 0;
 }
@@ -2464,7 +2465,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev)
 
        udc->dev = &pdev->dev;
        udc->mach = pdev->dev.platform_data;
-       udc->transceiver = usb_get_transceiver();
+       udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
 
        gpio = udc->mach->gpio_pullup;
        if (gpio_is_valid(gpio)) {
@@ -2543,7 +2544,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev)
        if (gpio_is_valid(gpio))
                gpio_free(gpio);
 
-       usb_put_transceiver(udc->transceiver);
+       usb_put_phy(udc->transceiver);
 
        udc->transceiver = NULL;
        platform_set_drvdata(_dev, NULL);
index f4abb0e..b13e0bb 100644 (file)
@@ -112,7 +112,6 @@ struct s3c_hsotg_ep {
        struct s3c_hsotg_req    *req;
        struct dentry           *debugfs;
 
-       spinlock_t              lock;
 
        unsigned long           total_data;
        unsigned int            size_loaded;
@@ -136,7 +135,6 @@ struct s3c_hsotg_ep {
  * @driver: USB gadget driver
  * @plat: The platform specific configuration data.
  * @regs: The memory area mapped for accessing registers.
- * @regs_res: The resource that was allocated when claiming register space.
  * @irq: The IRQ number we are using
  * @supplies: Definition of USB power supplies
  * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
@@ -157,8 +155,9 @@ struct s3c_hsotg {
        struct usb_gadget_driver *driver;
        struct s3c_hsotg_plat    *plat;
 
+       spinlock_t              lock;
+
        void __iomem            *regs;
-       struct resource         *regs_res;
        int                     irq;
        struct clk              *clk;
 
@@ -896,7 +895,6 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
        struct s3c_hsotg_req *hs_req = our_req(req);
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
        struct s3c_hsotg *hs = hs_ep->parent;
-       unsigned long irqflags;
        bool first;
 
        dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
@@ -915,19 +913,30 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
                        return ret;
        }
 
-       spin_lock_irqsave(&hs_ep->lock, irqflags);
-
        first = list_empty(&hs_ep->queue);
        list_add_tail(&hs_req->queue, &hs_ep->queue);
 
        if (first)
                s3c_hsotg_start_req(hs, hs_ep, hs_req, false);
 
-       spin_unlock_irqrestore(&hs_ep->lock, irqflags);
-
        return 0;
 }
 
+static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
+                             gfp_t gfp_flags)
+{
+       struct s3c_hsotg_ep *hs_ep = our_ep(ep);
+       struct s3c_hsotg *hs = hs_ep->parent;
+       unsigned long flags = 0;
+       int ret = 0;
+
+       spin_lock_irqsave(&hs->lock, flags);
+       ret = s3c_hsotg_ep_queue(ep, req, gfp_flags);
+       spin_unlock_irqrestore(&hs->lock, flags);
+
+       return ret;
+}
+
 static void s3c_hsotg_ep_free_request(struct usb_ep *ep,
                                      struct usb_request *req)
 {
@@ -1383,9 +1392,9 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
         */
 
        if (hs_req->req.complete) {
-               spin_unlock(&hs_ep->lock);
+               spin_unlock(&hsotg->lock);
                hs_req->req.complete(&hs_ep->ep, &hs_req->req);
-               spin_lock(&hs_ep->lock);
+               spin_lock(&hsotg->lock);
        }
 
        /*
@@ -1404,28 +1413,6 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
 }
 
 /**
- * s3c_hsotg_complete_request_lock - complete a request given to us (locked)
- * @hsotg: The device state.
- * @hs_ep: The endpoint the request was on.
- * @hs_req: The request to complete.
- * @result: The result code (0 => Ok, otherwise errno)
- *
- * See s3c_hsotg_complete_request(), but called with the endpoint's
- * lock held.
- */
-static void s3c_hsotg_complete_request_lock(struct s3c_hsotg *hsotg,
-                                           struct s3c_hsotg_ep *hs_ep,
-                                           struct s3c_hsotg_req *hs_req,
-                                           int result)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&hs_ep->lock, flags);
-       s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
-       spin_unlock_irqrestore(&hs_ep->lock, flags);
-}
-
-/**
  * s3c_hsotg_rx_data - receive data from the FIFO for an endpoint
  * @hsotg: The device state.
  * @ep_idx: The endpoint index for the data
@@ -1444,6 +1431,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
        int max_req;
        int read_ptr;
 
+
        if (!hs_req) {
                u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx));
                int ptr;
@@ -1459,8 +1447,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
                return;
        }
 
-       spin_lock(&hs_ep->lock);
-
        to_read = size;
        read_ptr = hs_req->req.actual;
        max_req = hs_req->req.length - read_ptr;
@@ -1487,8 +1473,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
         * alignment of the data.
         */
        readsl(fifo, hs_req->req.buf + read_ptr, to_read);
-
-       spin_unlock(&hs_ep->lock);
 }
 
 /**
@@ -1609,7 +1593,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
                        s3c_hsotg_send_zlp(hsotg, hs_req);
        }
 
-       s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, result);
+       s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
 }
 
 /**
@@ -1864,7 +1848,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
        /* Finish ZLP handling for IN EP0 transactions */
        if (hsotg->eps[0].sent_zlp) {
                dev_dbg(hsotg->dev, "zlp packet received\n");
-               s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, 0);
+               s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
                return;
        }
 
@@ -1915,7 +1899,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
                dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
                s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
        } else
-               s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, 0);
+               s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
 }
 
 /**
@@ -2123,9 +2107,6 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
                              int result, bool force)
 {
        struct s3c_hsotg_req *req, *treq;
-       unsigned long flags;
-
-       spin_lock_irqsave(&ep->lock, flags);
 
        list_for_each_entry_safe(req, treq, &ep->queue, queue) {
                /*
@@ -2139,14 +2120,15 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
                s3c_hsotg_complete_request(hsotg, ep, req,
                                           result);
        }
-
-       spin_unlock_irqrestore(&ep->lock, flags);
 }
 
 #define call_gadget(_hs, _entry) \
        if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
-           (_hs)->driver && (_hs)->driver->_entry)     \
-               (_hs)->driver->_entry(&(_hs)->gadget);
+           (_hs)->driver && (_hs)->driver->_entry) { \
+               spin_unlock(&_hs->lock); \
+               (_hs)->driver->_entry(&(_hs)->gadget); \
+               spin_lock(&_hs->lock); \
+               }
 
 /**
  * s3c_hsotg_disconnect - disconnect service
@@ -2388,6 +2370,7 @@ static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
        u32 gintsts;
        u32 gintmsk;
 
+       spin_lock(&hsotg->lock);
 irq_retry:
        gintsts = readl(hsotg->regs + GINTSTS);
        gintmsk = readl(hsotg->regs + GINTMSK);
@@ -2557,6 +2540,8 @@ irq_retry:
        if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
                        goto irq_retry;
 
+       spin_unlock(&hsotg->lock);
+
        return IRQ_HANDLED;
 }
 
@@ -2604,7 +2589,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
        dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
                __func__, epctrl, epctrl_reg);
 
-       spin_lock_irqsave(&hs_ep->lock, flags);
+       spin_lock_irqsave(&hsotg->lock, flags);
 
        epctrl &= ~(DxEPCTL_EPType_MASK | DxEPCTL_MPS_MASK);
        epctrl |= DxEPCTL_MPS(mps);
@@ -2683,7 +2668,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
        s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
 
 out:
-       spin_unlock_irqrestore(&hs_ep->lock, flags);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
        return ret;
 }
 
@@ -2710,10 +2695,10 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
 
        epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
 
+       spin_lock_irqsave(&hsotg->lock, flags);
        /* terminate all requests with shutdown */
        kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false);
 
-       spin_lock_irqsave(&hs_ep->lock, flags);
 
        ctrl = readl(hsotg->regs + epctrl_reg);
        ctrl &= ~DxEPCTL_EPEna;
@@ -2726,7 +2711,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
        /* disable endpoint interrupts */
        s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
 
-       spin_unlock_irqrestore(&hs_ep->lock, flags);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
        return 0;
 }
 
@@ -2761,15 +2746,15 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
 
        dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
 
-       spin_lock_irqsave(&hs_ep->lock, flags);
+       spin_lock_irqsave(&hs->lock, flags);
 
        if (!on_list(hs_ep, hs_req)) {
-               spin_unlock_irqrestore(&hs_ep->lock, flags);
+               spin_unlock_irqrestore(&hs->lock, flags);
                return -EINVAL;
        }
 
        s3c_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET);
-       spin_unlock_irqrestore(&hs_ep->lock, flags);
+       spin_unlock_irqrestore(&hs->lock, flags);
 
        return 0;
 }
@@ -2784,15 +2769,12 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
        struct s3c_hsotg *hs = hs_ep->parent;
        int index = hs_ep->index;
-       unsigned long irqflags;
        u32 epreg;
        u32 epctl;
        u32 xfertype;
 
        dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
 
-       spin_lock_irqsave(&hs_ep->lock, irqflags);
-
        /* write both IN and OUT control registers */
 
        epreg = DIEPCTL(index);
@@ -2827,19 +2809,36 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 
        writel(epctl, hs->regs + epreg);
 
-       spin_unlock_irqrestore(&hs_ep->lock, irqflags);
-
        return 0;
 }
 
+/**
+ * s3c_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held
+ * @ep: The endpoint to set halt.
+ * @value: Set or unset the halt.
+ */
+static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
+{
+       struct s3c_hsotg_ep *hs_ep = our_ep(ep);
+       struct s3c_hsotg *hs = hs_ep->parent;
+       unsigned long flags = 0;
+       int ret = 0;
+
+       spin_lock_irqsave(&hs->lock, flags);
+       ret = s3c_hsotg_ep_sethalt(ep, value);
+       spin_unlock_irqrestore(&hs->lock, flags);
+
+       return ret;
+}
+
 static struct usb_ep_ops s3c_hsotg_ep_ops = {
        .enable         = s3c_hsotg_ep_enable,
        .disable        = s3c_hsotg_ep_disable,
        .alloc_request  = s3c_hsotg_ep_alloc_request,
        .free_request   = s3c_hsotg_ep_free_request,
-       .queue          = s3c_hsotg_ep_queue,
+       .queue          = s3c_hsotg_ep_queue_lock,
        .dequeue        = s3c_hsotg_ep_dequeue,
-       .set_halt       = s3c_hsotg_ep_sethalt,
+       .set_halt       = s3c_hsotg_ep_sethalt_lock,
        /* note, don't believe we have any call for the fifo routines */
 };
 
@@ -2954,6 +2953,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
        driver->driver.bus = NULL;
        hsotg->driver = driver;
        hsotg->gadget.dev.driver = &driver->driver;
+       hsotg->gadget.dev.of_node = hsotg->dev->of_node;
        hsotg->gadget.dev.dma_mask = hsotg->dev->dma_mask;
        hsotg->gadget.speed = USB_SPEED_UNKNOWN;
 
@@ -2964,9 +2964,6 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
                goto err;
        }
 
-       s3c_hsotg_phy_enable(hsotg);
-
-       s3c_hsotg_core_init(hsotg);
        hsotg->last_rst = jiffies;
        dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
        return 0;
@@ -2988,6 +2985,7 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
                          struct usb_gadget_driver *driver)
 {
        struct s3c_hsotg *hsotg = to_hsotg(gadget);
+       unsigned long flags = 0;
        int ep;
 
        if (!hsotg)
@@ -3000,6 +2998,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
        for (ep = 0; ep < hsotg->num_of_eps; ep++)
                s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
 
+       spin_lock_irqsave(&hsotg->lock, flags);
+
        s3c_hsotg_phy_disable(hsotg);
        regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
 
@@ -3007,6 +3007,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
        hsotg->gadget.speed = USB_SPEED_UNKNOWN;
        hsotg->gadget.dev.driver = NULL;
 
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
        dev_info(hsotg->dev, "unregistered gadget driver '%s'\n",
                 driver->driver.name);
 
@@ -3024,10 +3026,40 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
        return s3c_hsotg_read_frameno(to_hsotg(gadget));
 }
 
+/**
+ * s3c_hsotg_pullup - connect/disconnect the USB PHY
+ * @gadget: The usb gadget state
+ * @is_on: Current state of the USB PHY
+ *
+ * Connect/Disconnect the USB PHY pullup
+ */
+static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
+{
+       struct s3c_hsotg *hsotg = to_hsotg(gadget);
+       unsigned long flags = 0;
+
+       dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on);
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       if (is_on) {
+               s3c_hsotg_phy_enable(hsotg);
+               s3c_hsotg_core_init(hsotg);
+       } else {
+               s3c_hsotg_disconnect(hsotg);
+               s3c_hsotg_phy_disable(hsotg);
+       }
+
+       hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       return 0;
+}
+
 static struct usb_gadget_ops s3c_hsotg_gadget_ops = {
        .get_frame      = s3c_hsotg_gadget_getframe,
        .udc_start              = s3c_hsotg_udc_start,
        .udc_stop               = s3c_hsotg_udc_stop,
+       .pullup                 = s3c_hsotg_pullup,
 };
 
 /**
@@ -3063,8 +3095,6 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
        INIT_LIST_HEAD(&hs_ep->queue);
        INIT_LIST_HEAD(&hs_ep->ep.ep_list);
 
-       spin_lock_init(&hs_ep->lock);
-
        /* add to the list of endpoints known by the gadget driver */
        if (epnum)
                list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list);
@@ -3342,7 +3372,7 @@ static int ep_show(struct seq_file *seq, void *v)
        seq_printf(seq, "request list (%p,%p):\n",
                   ep->queue.next, ep->queue.prev);
 
-       spin_lock_irqsave(&ep->lock, flags);
+       spin_lock_irqsave(&hsotg->lock, flags);
 
        list_for_each_entry(req, &ep->queue, queue) {
                if (--show_limit < 0) {
@@ -3357,7 +3387,7 @@ static int ep_show(struct seq_file *seq, void *v)
                           req->req.actual, req->req.status);
        }
 
-       spin_unlock_irqrestore(&ep->lock, flags);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
 
        return 0;
 }
@@ -3477,7 +3507,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       hsotg = kzalloc(sizeof(struct s3c_hsotg), GFP_KERNEL);
+       hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
        if (!hsotg) {
                dev_err(dev, "cannot get memory\n");
                return -ENOMEM;
@@ -3489,46 +3519,35 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
        hsotg->clk = clk_get(&pdev->dev, "otg");
        if (IS_ERR(hsotg->clk)) {
                dev_err(dev, "cannot get otg clock\n");
-               ret = PTR_ERR(hsotg->clk);
-               goto err_mem;
+               return PTR_ERR(hsotg->clk);
        }
 
        platform_set_drvdata(pdev, hsotg);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "cannot find register resource 0\n");
-               ret = -EINVAL;
-               goto err_clk;
-       }
-
-       hsotg->regs_res = request_mem_region(res->start, resource_size(res),
-                                            dev_name(dev));
-       if (!hsotg->regs_res) {
-               dev_err(dev, "cannot reserve registers\n");
-               ret = -ENOENT;
-               goto err_clk;
-       }
 
-       hsotg->regs = ioremap(res->start, resource_size(res));
+       hsotg->regs = devm_request_and_ioremap(&pdev->dev, res);
        if (!hsotg->regs) {
                dev_err(dev, "cannot map registers\n");
                ret = -ENXIO;
-               goto err_regs_res;
+               goto err_clk;
        }
 
        ret = platform_get_irq(pdev, 0);
        if (ret < 0) {
                dev_err(dev, "cannot find IRQ\n");
-               goto err_regs;
+               goto err_clk;
        }
 
+       spin_lock_init(&hsotg->lock);
+
        hsotg->irq = ret;
 
-       ret = request_irq(ret, s3c_hsotg_irq, 0, dev_name(dev), hsotg);
+       ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
+                               dev_name(dev), hsotg);
        if (ret < 0) {
                dev_err(dev, "cannot claim IRQ\n");
-               goto err_regs;
+               goto err_clk;
        }
 
        dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
@@ -3558,7 +3577,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
                                 hsotg->supplies);
        if (ret) {
                dev_err(dev, "failed to request supplies: %d\n", ret);
-               goto err_irq;
+               goto err_clk;
        }
 
        ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
@@ -3642,19 +3661,11 @@ err_ep_mem:
 err_supplies:
        s3c_hsotg_phy_disable(hsotg);
        regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
-err_irq:
-       free_irq(hsotg->irq, hsotg);
-err_regs:
-       iounmap(hsotg->regs);
-
-err_regs_res:
-       release_resource(hsotg->regs_res);
-       kfree(hsotg->regs_res);
+
 err_clk:
        clk_disable_unprepare(hsotg->clk);
        clk_put(hsotg->clk);
-err_mem:
-       kfree(hsotg);
+
        return ret;
 }
 
@@ -3675,12 +3686,6 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev)
                usb_gadget_unregister_driver(hsotg->driver);
        }
 
-       free_irq(hsotg->irq, hsotg);
-       iounmap(hsotg->regs);
-
-       release_resource(hsotg->regs_res);
-       kfree(hsotg->regs_res);
-
        s3c_hsotg_phy_disable(hsotg);
        regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
 
index 236b271..e26a4e7 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
@@ -1165,7 +1166,7 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
        }
 
        /* connect to bus through transceiver */
-       if (hsudc->transceiver) {
+       if (!IS_ERR_OR_NULL(hsudc->transceiver)) {
                ret = otg_set_peripheral(hsudc->transceiver->otg,
                                        &hsudc->gadget);
                if (ret) {
@@ -1220,7 +1221,7 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget,
        s3c_hsudc_stop_activity(hsudc);
        spin_unlock_irqrestore(&hsudc->lock, flags);
 
-       if (hsudc->transceiver)
+       if (!IS_ERR_OR_NULL(hsudc->transceiver))
                (void) otg_set_peripheral(hsudc->transceiver->otg, NULL);
 
        disable_irq(hsudc->irq);
@@ -1249,7 +1250,7 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
        if (!hsudc)
                return -ENODEV;
 
-       if (hsudc->transceiver)
+       if (!IS_ERR_OR_NULL(hsudc->transceiver))
                return usb_phy_set_power(hsudc->transceiver, mA);
 
        return -EOPNOTSUPP;
@@ -1282,7 +1283,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
        hsudc->dev = dev;
        hsudc->pd = pdev->dev.platform_data;
 
-       hsudc->transceiver = usb_get_transceiver();
+       hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
 
        for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
                hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
@@ -1385,8 +1386,8 @@ err_irq:
 err_remap:
        release_mem_region(res->start, resource_size(res));
 err_res:
-       if (hsudc->transceiver)
-               usb_put_transceiver(hsudc->transceiver);
+       if (!IS_ERR_OR_NULL(hsudc->transceiver))
+               usb_put_phy(hsudc->transceiver);
 
        regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
 err_supplies:
index 8081ca3..ae8b188 100644 (file)
  */
 
 /*
- * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included
- * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN
- * characters rather then a pointer to void.
- */
-
-/*
  * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
  * sets the number of pipeline buffers (length of the fsg_buffhd array).
  * The valid range of num_buffers is: num >= 2 && num <= 4.
@@ -260,11 +254,7 @@ enum fsg_buffer_state {
 };
 
 struct fsg_buffhd {
-#ifdef FSG_BUFFHD_STATIC_BUFFER
-       char                            buf[FSG_BUFLEN];
-#else
        void                            *buf;
-#endif
        enum fsg_buffer_state           state;
        struct fsg_buffhd               *next;
 
@@ -627,6 +617,16 @@ static struct usb_gadget_strings   fsg_stringtab = {
  * the caller must own fsg->filesem for writing.
  */
 
+static void fsg_lun_close(struct fsg_lun *curlun)
+{
+       if (curlun->filp) {
+               LDBG(curlun, "close backing file\n");
+               fput(curlun->filp);
+               curlun->filp = NULL;
+       }
+}
+
+
 static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
 {
        int                             ro;
@@ -636,6 +636,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
        loff_t                          size;
        loff_t                          num_sectors;
        loff_t                          min_sectors;
+       unsigned int                    blkbits;
+       unsigned int                    blksize;
 
        /* R/W if we can, R/O if we must */
        ro = curlun->initially_ro;
@@ -680,17 +682,17 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
        }
 
        if (curlun->cdrom) {
-               curlun->blksize = 2048;
-               curlun->blkbits = 11;
+               blksize = 2048;
+               blkbits = 11;
        } else if (inode->i_bdev) {
-               curlun->blksize = bdev_logical_block_size(inode->i_bdev);
-               curlun->blkbits = blksize_bits(curlun->blksize);
+               blksize = bdev_logical_block_size(inode->i_bdev);
+               blkbits = blksize_bits(blksize);
        } else {
-               curlun->blksize = 512;
-               curlun->blkbits = 9;
+               blksize = 512;
+               blkbits = 9;
        }
 
-       num_sectors = size >> curlun->blkbits; /* File size in logic-block-size blocks */
+       num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
        min_sectors = 1;
        if (curlun->cdrom) {
                min_sectors = 300;      /* Smallest track is 300 frames */
@@ -707,7 +709,12 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
                goto out;
        }
 
+       if (fsg_lun_is_open(curlun))
+               fsg_lun_close(curlun);
+
        get_file(filp);
+       curlun->blksize = blksize;
+       curlun->blkbits = blkbits;
        curlun->ro = ro;
        curlun->filp = filp;
        curlun->file_length = size;
@@ -721,16 +728,6 @@ out:
 }
 
 
-static void fsg_lun_close(struct fsg_lun *curlun)
-{
-       if (curlun->filp) {
-               LDBG(curlun, "close backing file\n");
-               fput(curlun->filp);
-               curlun->filp = NULL;
-       }
-}
-
-
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -881,19 +878,17 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
        if (count > 0 && buf[count-1] == '\n')
                ((char *) buf)[count-1] = 0;            /* Ugh! */
 
-       /* Eject current medium */
-       down_write(filesem);
-       if (fsg_lun_is_open(curlun)) {
-               fsg_lun_close(curlun);
-               curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
-       }
-
        /* Load new medium */
+       down_write(filesem);
        if (count > 0 && buf[0]) {
+               /* fsg_lun_open() will close existing file if any. */
                rc = fsg_lun_open(curlun, buf);
                if (rc == 0)
                        curlun->unit_attention_data =
                                        SS_NOT_READY_TO_READY_TRANSITION;
+       } else if (fsg_lun_is_open(curlun)) {
+               fsg_lun_close(curlun);
+               curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
        }
        up_write(filesem);
        return (rc < 0 ? rc : count);
index b9e1925..90e82e2 100644 (file)
@@ -798,12 +798,6 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
 
        SET_ETHTOOL_OPS(net, &ops);
 
-       /* two kinds of host-initiated state changes:
-        *  - iff DATA transfer is active, carrier is "on"
-        *  - tx queueing enabled if open *and* carrier is "on"
-        */
-       netif_carrier_off(net);
-
        dev->gadget = g;
        SET_NETDEV_DEV(net, &g->dev);
        SET_NETDEV_DEVTYPE(net, &gadget_type);
@@ -817,6 +811,12 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
                INFO(dev, "HOST MAC %pM\n", dev->host_mac);
 
                the_dev = dev;
+
+               /* two kinds of host-initiated state changes:
+                *  - iff DATA transfer is active, carrier is "on"
+                *  - tx queueing enabled if open *and* carrier is "on"
+                */
+               netif_carrier_off(net);
        }
 
        return status;
index ca4e03a..93b0c11 100644 (file)
@@ -153,9 +153,11 @@ struct uvc_device
 
        /* Descriptors */
        struct {
-               const struct uvc_descriptor_header * const *control;
+               const struct uvc_descriptor_header * const *fs_control;
+               const struct uvc_descriptor_header * const *ss_control;
                const struct uvc_descriptor_header * const *fs_streaming;
                const struct uvc_descriptor_header * const *hs_streaming;
+               const struct uvc_descriptor_header * const *ss_streaming;
        } desc;
 
        unsigned int control_intf;
index 668fe12..120e134 100644 (file)
@@ -272,7 +272,15 @@ static const struct uvc_color_matching_descriptor uvc_color_matching = {
        .bMatrixCoefficients    = 4,
 };
 
-static const struct uvc_descriptor_header * const uvc_control_cls[] = {
+static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
+       (const struct uvc_descriptor_header *) &uvc_control_header,
+       (const struct uvc_descriptor_header *) &uvc_camera_terminal,
+       (const struct uvc_descriptor_header *) &uvc_processing,
+       (const struct uvc_descriptor_header *) &uvc_output_terminal,
+       NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
        (const struct uvc_descriptor_header *) &uvc_control_header,
        (const struct uvc_descriptor_header *) &uvc_camera_terminal,
        (const struct uvc_descriptor_header *) &uvc_processing,
@@ -304,6 +312,18 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
        NULL,
 };
 
+static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
+       (const struct uvc_descriptor_header *) &uvc_input_header,
+       (const struct uvc_descriptor_header *) &uvc_format_yuv,
+       (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
+       (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+       (const struct uvc_descriptor_header *) &uvc_format_mjpg,
+       (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
+       (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
+       (const struct uvc_descriptor_header *) &uvc_color_matching,
+       NULL,
+};
+
 /* --------------------------------------------------------------------------
  * USB configuration
  */
@@ -311,8 +331,9 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
 static int __init
 webcam_config_bind(struct usb_configuration *c)
 {
-       return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls,
-                              uvc_hs_streaming_cls);
+       return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls,
+               uvc_fs_streaming_cls, uvc_hs_streaming_cls,
+               uvc_ss_streaming_cls);
 }
 
 static struct usb_configuration webcam_config_driver = {
@@ -373,7 +394,7 @@ static struct usb_composite_driver webcam_driver = {
        .name           = "g_webcam",
        .dev            = &webcam_device_descriptor,
        .strings        = webcam_device_strings,
-       .max_speed      = USB_SPEED_HIGH,
+       .max_speed      = USB_SPEED_SUPER,
        .unbind         = webcam_unbind,
 };
 
index dcfaaa9..075d2ec 100644 (file)
@@ -652,7 +652,7 @@ config USB_HCD_BCMA
        select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
        select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
        help
-         Enbale support for the EHCI and OCHI host controller on an bcma bus.
+         Enable support for the EHCI and OCHI host controller on an bcma bus.
          It converts the bcma driver into two platform device drivers
          for ehci and ohci.
 
@@ -664,7 +664,7 @@ config USB_HCD_SSB
        select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
        select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
        help
-         Enbale support for the EHCI and OCHI host controller on an bcma bus.
+         Enable support for the EHCI and OCHI host controller on an bcma bus.
          It converts the bcma driver into two platform device drivers
          for ehci and ohci.
 
index cf14c95..a47e2cf 100644 (file)
@@ -53,30 +53,15 @@ static void atmel_stop_ehci(struct platform_device *pdev)
 static int ehci_atmel_setup(struct usb_hcd *hcd)
 {
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       int retval = 0;
+       int retval;
 
        /* registers start at offset 0x0 */
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-       dbg_hcs_params(ehci, "reset");
-       dbg_hcc_params(ehci, "reset");
-
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
 
-       /* data structure init */
-       retval = ehci_init(hcd);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
-       ehci->sbrn = 0x20;
-
-       ehci_reset(ehci);
        ehci_port_power(ehci, 0);
 
        return retval;
index bf7441a..cba10d6 100644 (file)
@@ -20,10 +20,12 @@ extern int usb_disabled(void);
 static int au1xxx_ehci_setup(struct usb_hcd *hcd)
 {
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       int ret = ehci_init(hcd);
+       int ret;
+
+       ehci->caps = hcd->regs;
+       ret = ehci_setup(hcd);
 
        ehci->need_io_watchdog = 0;
-       ehci_reset(ehci);
        return ret;
 }
 
@@ -78,7 +80,6 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
 static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
 {
        struct usb_hcd *hcd;
-       struct ehci_hcd *ehci;
        struct resource *res;
        int ret;
 
@@ -116,13 +117,6 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
                goto err3;
        }
 
-       ehci = hcd_to_ehci(hcd);
-       ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = readl(&ehci->caps->hcs_params);
-
        ret = usb_add_hcd(hcd, pdev->resource[1].start,
                          IRQF_SHARED);
        if (ret == 0) {
@@ -158,28 +152,10 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
 static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       unsigned long flags;
-       int rc = 0;
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(10);
-
-       /* Root hub was already suspended. Disable irq emission and
-        * mark HW unaccessible.  The PM and USB cores make sure that
-        * the root hub is either suspended or stopped.
-        */
-       ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
-       spin_lock_irqsave(&ehci->lock, flags);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       (void)ehci_readl(ehci, &ehci->regs->intr_enable);
-
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       spin_unlock_irqrestore(&ehci->lock, flags);
-
-       // could save FLADJ in case of Vaux power loss
-       // ... we'd only use it to handle clock skew
+       bool do_wakeup = device_may_wakeup(dev);
+       int rc;
 
+       rc = ehci_suspend(hcd, do_wakeup);
        alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
 
        return rc;
@@ -188,56 +164,9 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
 static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 
        alchemy_usb_control(ALCHEMY_USB_EHCI0, 1);
-
-       // maybe restore FLADJ
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       /* Mark hardware accessible again as we are out of D3 state by now */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       /* If CF is still set, we maintained PCI Vaux power.
-        * Just undo the effect of ehci_pci_suspend().
-        */
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
-               int     mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       ehci_dbg(ehci, "lost power, restarting\n");
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       /* Else reset, to cope with power loss or flush-to-storage
-        * style "resume" having let BIOS kick in during reboot.
-        */
-       (void) ehci_halt(ehci);
-       (void) ehci_reset(ehci);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
-
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
-
-       ehci->rh_state = EHCI_RH_SUSPENDED;
+       ehci_resume(hcd, false);
 
        return 0;
 }
index 6536abd..caaa3e5 100644 (file)
@@ -33,14 +33,10 @@ static int cns3xxx_ehci_init(struct usb_hcd *hcd)
        }
 
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs
-               + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
 
        hcd->has_tt = 0;
-       ehci_reset(ehci);
 
-       retval = ehci_init(hcd);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
index 7561966..f0c00de 100644 (file)
@@ -404,9 +404,9 @@ struct debug_buffer {
 
 #define speed_char(info1) ({ char tmp; \
                switch (info1 & (3 << 12)) { \
-               case 0 << 12: tmp = 'f'; break; \
-               case 1 << 12: tmp = 'l'; break; \
-               case 2 << 12: tmp = 'h'; break; \
+               case QH_FULL_SPEED: tmp = 'f'; break; \
+               case QH_LOW_SPEED:  tmp = 'l'; break; \
+               case QH_HIGH_SPEED: tmp = 'h'; break; \
                default: tmp = '?'; break; \
                }; tmp; })
 
@@ -538,12 +538,13 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
        spin_lock_irqsave (&ehci->lock, flags);
        for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
                qh_lines (ehci, qh, &next, &size);
-       if (ehci->reclaim && size > 0) {
-               temp = scnprintf (next, size, "\nreclaim =\n");
+       if (ehci->async_unlink && size > 0) {
+               temp = scnprintf(next, size, "\nunlink =\n");
                size -= temp;
                next += temp;
 
-               for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim)
+               for (qh = ehci->async_unlink; size > 0 && qh;
+                               qh = qh->unlink_next)
                        qh_lines (ehci, qh, &next, &size);
        }
        spin_unlock_irqrestore (&ehci->lock, flags);
@@ -705,6 +706,8 @@ static const char *rh_state_string(struct ehci_hcd *ehci)
                return "suspended";
        case EHCI_RH_RUNNING:
                return "running";
+       case EHCI_RH_STOPPING:
+               return "stopping";
        }
        return "?";
 }
@@ -841,16 +844,17 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
                }
        }
 
-       if (ehci->reclaim) {
-               temp = scnprintf(next, size, "reclaim qh %p\n", ehci->reclaim);
+       if (ehci->async_unlink) {
+               temp = scnprintf(next, size, "async unlink qh %p\n",
+                               ehci->async_unlink);
                size -= temp;
                next += temp;
        }
 
 #ifdef EHCI_STATS
        temp = scnprintf (next, size,
-               "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
-               ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
+               "irq normal %ld err %ld iaa %ld (lost %ld)\n",
+               ehci->stats.normal, ehci->stats.error, ehci->stats.iaa,
                ehci->stats.lost_iaa);
        size -= temp;
        next += temp;
index 4336257..b7451b2 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/types.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
 
@@ -142,19 +143,19 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
        if (pdata->operating_mode == FSL_USB2_DR_OTG) {
                struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 
-               ehci->transceiver = usb_get_transceiver();
-               dev_dbg(&pdev->dev, "hcd=0x%p  ehci=0x%p, transceiver=0x%p\n",
-                       hcd, ehci, ehci->transceiver);
+               hcd->phy = usb_get_phy(USB_PHY_TYPE_USB2);
+               dev_dbg(&pdev->dev, "hcd=0x%p  ehci=0x%p, phy=0x%p\n",
+                       hcd, ehci, hcd->phy);
 
-               if (ehci->transceiver) {
-                       retval = otg_set_host(ehci->transceiver->otg,
+               if (!IS_ERR_OR_NULL(hcd->phy)) {
+                       retval = otg_set_host(hcd->phy->otg,
                                              &ehci_to_hcd(ehci)->self);
                        if (retval) {
-                               usb_put_transceiver(ehci->transceiver);
+                               usb_put_phy(hcd->phy);
                                goto err4;
                        }
                } else {
-                       dev_err(&pdev->dev, "can't find transceiver\n");
+                       dev_err(&pdev->dev, "can't find phy\n");
                        retval = -ENODEV;
                        goto err4;
                }
@@ -190,11 +191,10 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
                               struct platform_device *pdev)
 {
        struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 
-       if (ehci->transceiver) {
-               otg_set_host(ehci->transceiver->otg, NULL);
-               usb_put_transceiver(ehci->transceiver);
+       if (!IS_ERR_OR_NULL(hcd->phy)) {
+               otg_set_host(hcd->phy->otg, NULL);
+               usb_put_phy(hcd->phy);
        }
 
        usb_remove_hcd(hcd);
@@ -313,7 +313,7 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
        }
 
        if (pdata->have_sysif_regs) {
-#ifdef CONFIG_PPC_85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
                out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
                out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
 #else
@@ -348,29 +348,13 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
 
        /* EHCI registers start at offset 0x100 */
        ehci->caps = hcd->regs + 0x100;
-       ehci->regs = hcd->regs + 0x100 +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-       dbg_hcs_params(ehci, "reset");
-       dbg_hcc_params(ehci, "reset");
-
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
 
        hcd->has_tt = 1;
 
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
-
-       /* data structure init */
-       retval = ehci_init(hcd);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
-       ehci->sbrn = 0x20;
-
-       ehci_reset(ehci);
-
        if (of_device_is_compatible(dev->parent->of_node,
                                    "fsl,mpc5121-usb2-dr")) {
                /*
index fdfd8c5..22ca45c 100644 (file)
@@ -40,18 +40,13 @@ static int ehci_grlib_setup(struct usb_hcd *hcd)
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        int             retval;
 
-       retval = ehci_halt(ehci);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
-       retval = ehci_init(hcd);
-       if (retval)
-               return retval;
-
-       ehci->sbrn = 0x20;
        ehci_port_power(ehci, 1);
 
-       return ehci_reset(ehci);
+       return retval;
 }
 
 
@@ -164,12 +159,6 @@ static int __devinit ehci_hcd_grlib_probe(struct platform_device *op)
                ehci->big_endian_capbase = 1;
        }
 
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
        rv = usb_add_hcd(hcd, irq, 0);
        if (rv)
                goto err_ehci;
index 1d9401e..b05c686 100644 (file)
@@ -30,8 +30,7 @@
 #include <linux/vmalloc.h>
 #include <linux/errno.h>
 #include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/ktime.h>
+#include <linux/hrtimer.h>
 #include <linux/list.h>
 #include <linux/interrupt.h>
 #include <linux/usb.h>
@@ -94,12 +93,6 @@ static const char    hcd_name [] = "ehci_hcd";
  */
 #define        EHCI_TUNE_FLS           1       /* (medium) 512-frame schedule */
 
-#define EHCI_IAA_MSECS         10              /* arbitrary */
-#define EHCI_IO_JIFFIES                (HZ/10)         /* io watchdog > irq_thresh */
-#define EHCI_ASYNC_JIFFIES     (HZ/20)         /* async idle timeout */
-#define EHCI_SHRINK_JIFFIES    (DIV_ROUND_UP(HZ, 200) + 1)
-                                               /* 5-ms async qh unlink delay */
-
 /* Initial IRQ latency:  faster than hw default */
 static int log2_irq_thresh = 0;                // 0 to 6
 module_param (log2_irq_thresh, int, S_IRUGO);
@@ -130,41 +123,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");
 
 /*-------------------------------------------------------------------------*/
 
-static void
-timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
-{
-       /* Don't override timeouts which shrink or (later) disable
-        * the async ring; just the I/O watchdog.  Note that if a
-        * SHRINK were pending, OFF would never be requested.
-        */
-       if (timer_pending(&ehci->watchdog)
-                       && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF))
-                               & ehci->actions))
-               return;
-
-       if (!test_and_set_bit(action, &ehci->actions)) {
-               unsigned long t;
-
-               switch (action) {
-               case TIMER_IO_WATCHDOG:
-                       if (!ehci->need_io_watchdog)
-                               return;
-                       t = EHCI_IO_JIFFIES;
-                       break;
-               case TIMER_ASYNC_OFF:
-                       t = EHCI_ASYNC_JIFFIES;
-                       break;
-               /* case TIMER_ASYNC_SHRINK: */
-               default:
-                       t = EHCI_SHRINK_JIFFIES;
-                       break;
-               }
-               mod_timer(&ehci->watchdog, t + jiffies);
-       }
-}
-
-/*-------------------------------------------------------------------------*/
-
 /*
  * handshake - spin reading hc until handshake completes or fails
  * @ptr: address of hc register to be read
@@ -203,29 +161,30 @@ static int handshake (struct ehci_hcd *ehci, void __iomem *ptr,
 /* check TDI/ARC silicon is in host mode */
 static int tdi_in_host_mode (struct ehci_hcd *ehci)
 {
-       u32 __iomem     *reg_ptr;
        u32             tmp;
 
-       reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
-       tmp = ehci_readl(ehci, reg_ptr);
+       tmp = ehci_readl(ehci, &ehci->regs->usbmode);
        return (tmp & 3) == USBMODE_CM_HC;
 }
 
-/* force HC to halt state from unknown (EHCI spec section 2.3) */
+/*
+ * Force HC to halt state from unknown (EHCI spec section 2.3).
+ * Must be called with interrupts enabled and the lock not held.
+ */
 static int ehci_halt (struct ehci_hcd *ehci)
 {
-       u32     temp = ehci_readl(ehci, &ehci->regs->status);
+       u32     temp;
+
+       spin_lock_irq(&ehci->lock);
 
        /* disable any irqs left enabled by previous code */
        ehci_writel(ehci, 0, &ehci->regs->intr_enable);
 
-       if (ehci_is_TDI(ehci) && tdi_in_host_mode(ehci) == 0) {
+       if (ehci_is_TDI(ehci) && !tdi_in_host_mode(ehci)) {
+               spin_unlock_irq(&ehci->lock);
                return 0;
        }
 
-       if ((temp & STS_HALT) != 0)
-               return 0;
-
        /*
         * This routine gets called during probe before ehci->command
         * has been initialized, so we can't rely on its value.
@@ -234,80 +193,20 @@ static int ehci_halt (struct ehci_hcd *ehci)
        temp = ehci_readl(ehci, &ehci->regs->command);
        temp &= ~(CMD_RUN | CMD_IAAD);
        ehci_writel(ehci, temp, &ehci->regs->command);
-       return handshake (ehci, &ehci->regs->status,
-                         STS_HALT, STS_HALT, 16 * 125);
-}
-
-#if defined(CONFIG_USB_SUSPEND) && defined(CONFIG_PPC_PS3)
-
-/*
- * The EHCI controller of the Cell Super Companion Chip used in the
- * PS3 will stop the root hub after all root hub ports are suspended.
- * When in this condition handshake will return -ETIMEDOUT.  The
- * STS_HLT bit will not be set, so inspection of the frame index is
- * used here to test for the condition.  If the condition is found
- * return success to allow the USB suspend to complete.
- */
-
-static int handshake_for_broken_root_hub(struct ehci_hcd *ehci,
-                                        void __iomem *ptr, u32 mask, u32 done,
-                                        int usec)
-{
-       unsigned int old_index;
-       int error;
-
-       if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
-               return -ETIMEDOUT;
-
-       old_index = ehci_read_frame_index(ehci);
-
-       error = handshake(ehci, ptr, mask, done, usec);
-
-       if (error == -ETIMEDOUT && ehci_read_frame_index(ehci) == old_index)
-               return 0;
-
-       return error;
-}
-
-#else
-
-static int handshake_for_broken_root_hub(struct ehci_hcd *ehci,
-                                        void __iomem *ptr, u32 mask, u32 done,
-                                        int usec)
-{
-       return -ETIMEDOUT;
-}
-
-#endif
 
-static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
-                                      u32 mask, u32 done, int usec)
-{
-       int error;
-
-       error = handshake(ehci, ptr, mask, done, usec);
-       if (error == -ETIMEDOUT)
-               error = handshake_for_broken_root_hub(ehci, ptr, mask, done,
-                                                     usec);
-
-       if (error) {
-               ehci_halt(ehci);
-               ehci->rh_state = EHCI_RH_HALTED;
-               ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n",
-                       ptr, mask, done, error);
-       }
+       spin_unlock_irq(&ehci->lock);
+       synchronize_irq(ehci_to_hcd(ehci)->irq);
 
-       return error;
+       return handshake(ehci, &ehci->regs->status,
+                         STS_HALT, STS_HALT, 16 * 125);
 }
 
 /* put TDI/ARC silicon into EHCI mode */
 static void tdi_reset (struct ehci_hcd *ehci)
 {
-       u32 __iomem     *reg_ptr;
        u32             tmp;
 
-       reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
-       tmp = ehci_readl(ehci, reg_ptr);
+       tmp = ehci_readl(ehci, &ehci->regs->usbmode);
        tmp |= USBMODE_CM_HC;
        /* The default byte access to MMR space is LE after
         * controller reset. Set the required endian mode
@@ -315,10 +214,13 @@ static void tdi_reset (struct ehci_hcd *ehci)
         */
        if (ehci_big_endian_mmio(ehci))
                tmp |= USBMODE_BE;
-       ehci_writel(ehci, tmp, reg_ptr);
+       ehci_writel(ehci, tmp, &ehci->regs->usbmode);
 }
 
-/* reset a non-running (STS_HALT == 1) controller */
+/*
+ * Reset a non-running (STS_HALT == 1) controller.
+ * Must be called with interrupts enabled and the lock not held.
+ */
 static int ehci_reset (struct ehci_hcd *ehci)
 {
        int     retval;
@@ -339,9 +241,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
 
        if (ehci->has_hostpc) {
                ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS,
-                       (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX));
-               ehci_writel(ehci, TXFIFO_DEFAULT,
-                       (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING));
+                               &ehci->regs->usbmode_ex);
+               ehci_writel(ehci, TXFIFO_DEFAULT, &ehci->regs->txfill_tuning);
        }
        if (retval)
                return retval;
@@ -357,36 +258,40 @@ static int ehci_reset (struct ehci_hcd *ehci)
        return retval;
 }
 
-/* idle the controller (from running) */
+/*
+ * Idle the controller (turn off the schedules).
+ * Must be called with interrupts enabled and the lock not held.
+ */
 static void ehci_quiesce (struct ehci_hcd *ehci)
 {
        u32     temp;
 
-#ifdef DEBUG
        if (ehci->rh_state != EHCI_RH_RUNNING)
-               BUG ();
-#endif
+               return;
 
        /* wait for any schedule enables/disables to take effect */
        temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
-       if (handshake_on_error_set_halt(ehci, &ehci->regs->status,
-                                       STS_ASS | STS_PSS, temp, 16 * 125))
-               return;
+       handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125);
 
        /* then disable anything that's still active */
+       spin_lock_irq(&ehci->lock);
        ehci->command &= ~(CMD_ASE | CMD_PSE);
        ehci_writel(ehci, ehci->command, &ehci->regs->command);
+       spin_unlock_irq(&ehci->lock);
 
        /* hardware can take 16 microframes to turn off ... */
-       handshake_on_error_set_halt(ehci, &ehci->regs->status,
-                                   STS_ASS | STS_PSS, 0, 16 * 125);
+       handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125);
 }
 
 /*-------------------------------------------------------------------------*/
 
 static void end_unlink_async(struct ehci_hcd *ehci);
+static void unlink_empty_async(struct ehci_hcd *ehci);
 static void ehci_work(struct ehci_hcd *ehci);
+static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
+static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
 
+#include "ehci-timer.c"
 #include "ehci-hub.c"
 #include "ehci-lpm.c"
 #include "ehci-mem.c"
@@ -396,68 +301,6 @@ static void ehci_work(struct ehci_hcd *ehci);
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_iaa_watchdog(unsigned long param)
-{
-       struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
-       unsigned long           flags;
-
-       spin_lock_irqsave (&ehci->lock, flags);
-
-       /* Lost IAA irqs wedge things badly; seen first with a vt8235.
-        * So we need this watchdog, but must protect it against both
-        * (a) SMP races against real IAA firing and retriggering, and
-        * (b) clean HC shutdown, when IAA watchdog was pending.
-        */
-       if (ehci->reclaim
-                       && !timer_pending(&ehci->iaa_watchdog)
-                       && ehci->rh_state == EHCI_RH_RUNNING) {
-               u32 cmd, status;
-
-               /* If we get here, IAA is *REALLY* late.  It's barely
-                * conceivable that the system is so busy that CMD_IAAD
-                * is still legitimately set, so let's be sure it's
-                * clear before we read STS_IAA.  (The HC should clear
-                * CMD_IAAD when it sets STS_IAA.)
-                */
-               cmd = ehci_readl(ehci, &ehci->regs->command);
-
-               /* If IAA is set here it either legitimately triggered
-                * before we cleared IAAD above (but _way_ late, so we'll
-                * still count it as lost) ... or a silicon erratum:
-                * - VIA seems to set IAA without triggering the IRQ;
-                * - IAAD potentially cleared without setting IAA.
-                */
-               status = ehci_readl(ehci, &ehci->regs->status);
-               if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
-                       COUNT (ehci->stats.lost_iaa);
-                       ehci_writel(ehci, STS_IAA, &ehci->regs->status);
-               }
-
-               ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n",
-                               status, cmd);
-               end_unlink_async(ehci);
-       }
-
-       spin_unlock_irqrestore(&ehci->lock, flags);
-}
-
-static void ehci_watchdog(unsigned long param)
-{
-       struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
-       unsigned long           flags;
-
-       spin_lock_irqsave(&ehci->lock, flags);
-
-       /* stop async processing after it's idled a bit */
-       if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
-               start_unlink_async (ehci, ehci->async);
-
-       /* ehci could run by timer, without IRQs ... */
-       ehci_work (ehci);
-
-       spin_unlock_irqrestore (&ehci->lock, flags);
-}
-
 /* On some systems, leaving remote wakeup enabled prevents system shutdown.
  * The firmware seems to think that powering off is a wakeup event!
  * This routine turns off remote wakeup and everything else, on all ports.
@@ -473,11 +316,14 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
 
 /*
  * Halt HC, turn off all ports, and let the BIOS use the companion controllers.
- * Should be called with ehci->lock held.
+ * Must be called with interrupts enabled and the lock not held.
  */
 static void ehci_silence_controller(struct ehci_hcd *ehci)
 {
        ehci_halt(ehci);
+
+       spin_lock_irq(&ehci->lock);
+       ehci->rh_state = EHCI_RH_HALTED;
        ehci_turn_off_all_ports(ehci);
 
        /* make BIOS/etc use companion controller during reboot */
@@ -485,6 +331,7 @@ static void ehci_silence_controller(struct ehci_hcd *ehci)
 
        /* unblock posted writes */
        ehci_readl(ehci, &ehci->regs->configured_flag);
+       spin_unlock_irq(&ehci->lock);
 }
 
 /* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
@@ -495,12 +342,15 @@ static void ehci_shutdown(struct usb_hcd *hcd)
 {
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 
-       del_timer_sync(&ehci->watchdog);
-       del_timer_sync(&ehci->iaa_watchdog);
-
        spin_lock_irq(&ehci->lock);
-       ehci_silence_controller(ehci);
+       ehci->shutdown = true;
+       ehci->rh_state = EHCI_RH_STOPPING;
+       ehci->enabled_hrtimer_events = 0;
        spin_unlock_irq(&ehci->lock);
+
+       ehci_silence_controller(ehci);
+
+       hrtimer_cancel(&ehci->hrtimer);
 }
 
 static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
@@ -529,28 +379,33 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
  */
 static void ehci_work (struct ehci_hcd *ehci)
 {
-       timer_action_done (ehci, TIMER_IO_WATCHDOG);
-
        /* another CPU may drop ehci->lock during a schedule scan while
         * it reports urb completions.  this flag guards against bogus
         * attempts at re-entrant schedule scanning.
         */
-       if (ehci->scanning)
+       if (ehci->scanning) {
+               ehci->need_rescan = true;
                return;
-       ehci->scanning = 1;
-       scan_async (ehci);
-       if (ehci->next_uframe != -1)
-               scan_periodic (ehci);
-       ehci->scanning = 0;
+       }
+       ehci->scanning = true;
+
+ rescan:
+       ehci->need_rescan = false;
+       if (ehci->async_count)
+               scan_async(ehci);
+       if (ehci->intr_count > 0)
+               scan_intr(ehci);
+       if (ehci->isoc_count > 0)
+               scan_isoc(ehci);
+       if (ehci->need_rescan)
+               goto rescan;
+       ehci->scanning = false;
 
        /* the IO watchdog guards against hardware or driver bugs that
         * misplace IRQs, and should let us run completely without IRQs.
         * such lossage has been observed on both VT6202 and VT8235.
         */
-       if (ehci->rh_state == EHCI_RH_RUNNING &&
-                       (ehci->async->qh_next.ptr != NULL ||
-                        ehci->periodic_sched != 0))
-               timer_action (ehci, TIMER_IO_WATCHDOG);
+       turn_on_io_watchdog(ehci);
 }
 
 /*
@@ -563,24 +418,22 @@ static void ehci_stop (struct usb_hcd *hcd)
        ehci_dbg (ehci, "stop\n");
 
        /* no more interrupts ... */
-       del_timer_sync (&ehci->watchdog);
-       del_timer_sync(&ehci->iaa_watchdog);
 
        spin_lock_irq(&ehci->lock);
-       if (ehci->rh_state == EHCI_RH_RUNNING)
-               ehci_quiesce (ehci);
+       ehci->enabled_hrtimer_events = 0;
+       spin_unlock_irq(&ehci->lock);
 
+       ehci_quiesce(ehci);
        ehci_silence_controller(ehci);
        ehci_reset (ehci);
-       spin_unlock_irq(&ehci->lock);
 
+       hrtimer_cancel(&ehci->hrtimer);
        remove_sysfs_files(ehci);
        remove_debug_files (ehci);
 
        /* root hub is shut down separately (first, when possible) */
        spin_lock_irq (&ehci->lock);
-       if (ehci->async)
-               ehci_work (ehci);
+       end_free_itds(ehci);
        spin_unlock_irq (&ehci->lock);
        ehci_mem_cleanup (ehci);
 
@@ -588,8 +441,8 @@ static void ehci_stop (struct usb_hcd *hcd)
                usb_amd_dev_put();
 
 #ifdef EHCI_STATS
-       ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
-               ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
+       ehci_dbg(ehci, "irq normal %ld err %ld iaa %ld (lost %ld)\n",
+               ehci->stats.normal, ehci->stats.error, ehci->stats.iaa,
                ehci->stats.lost_iaa);
        ehci_dbg (ehci, "complete %ld unlink %ld\n",
                ehci->stats.complete, ehci->stats.unlink);
@@ -614,13 +467,10 @@ static int ehci_init(struct usb_hcd *hcd)
         * keep io watchdog by default, those good HCDs could turn off it later
         */
        ehci->need_io_watchdog = 1;
-       init_timer(&ehci->watchdog);
-       ehci->watchdog.function = ehci_watchdog;
-       ehci->watchdog.data = (unsigned long) ehci;
 
-       init_timer(&ehci->iaa_watchdog);
-       ehci->iaa_watchdog.function = ehci_iaa_watchdog;
-       ehci->iaa_watchdog.data = (unsigned long) ehci;
+       hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+       ehci->hrtimer.function = ehci_hrtimer_func;
+       ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
 
        hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
 
@@ -635,6 +485,7 @@ static int ehci_init(struct usb_hcd *hcd)
         * periodic_size can shrink by USBCMD update if hcc_params allows.
         */
        ehci->periodic_size = DEFAULT_I_TDPS;
+       INIT_LIST_HEAD(&ehci->intr_qh_list);
        INIT_LIST_HEAD(&ehci->cached_itd_list);
        INIT_LIST_HEAD(&ehci->cached_sitd_list);
 
@@ -656,10 +507,6 @@ static int ehci_init(struct usb_hcd *hcd)
        else                                    // N microframes cached
                ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 
-       ehci->reclaim = NULL;
-       ehci->next_uframe = -1;
-       ehci->clock_frame = -1;
-
        /*
         * dedicate a qh for the async ring head, since we couldn't unlink
         * a 'real' qh without stopping the async schedule [4.8].  use it
@@ -672,7 +519,7 @@ static int ehci_init(struct usb_hcd *hcd)
        hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
        hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
 #if defined(CONFIG_PPC_PS3)
-       hw->hw_info1 |= cpu_to_hc32(ehci, (1 << 7));    /* I = 1 */
+       hw->hw_info1 |= cpu_to_hc32(ehci, QH_INACTIVATE);
 #endif
        hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
        hw->hw_qtd_next = EHCI_LIST_END(ehci);
@@ -813,7 +660,7 @@ static int ehci_run (struct usb_hcd *hcd)
        return 0;
 }
 
-static int __maybe_unused ehci_setup (struct usb_hcd *hcd)
+static int ehci_setup(struct usb_hcd *hcd)
 {
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        int retval;
@@ -828,15 +675,18 @@ static int __maybe_unused ehci_setup (struct usb_hcd *hcd)
 
        ehci->sbrn = HCD_USB2;
 
-       retval = ehci_halt(ehci);
+       /* data structure init */
+       retval = ehci_init(hcd);
        if (retval)
                return retval;
 
-       /* data structure init */
-       retval = ehci_init(hcd);
+       retval = ehci_halt(ehci);
        if (retval)
                return retval;
 
+       if (ehci_is_TDI(ehci))
+               tdi_reset(ehci);
+
        ehci_reset(ehci);
 
        return 0;
@@ -895,14 +745,28 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
 
        /* complete the unlinking of some qh [4.15.2.3] */
        if (status & STS_IAA) {
+
+               /* Turn off the IAA watchdog */
+               ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_IAA_WATCHDOG);
+
+               /*
+                * Mild optimization: Allow another IAAD to reset the
+                * hrtimer, if one occurs before the next expiration.
+                * In theory we could always cancel the hrtimer, but
+                * tests show that about half the time it will be reset
+                * for some other event anyway.
+                */
+               if (ehci->next_hrtimer_event == EHCI_HRTIMER_IAA_WATCHDOG)
+                       ++ehci->next_hrtimer_event;
+
                /* guard against (alleged) silicon errata */
                if (cmd & CMD_IAAD)
                        ehci_dbg(ehci, "IAA with IAAD still set?\n");
-               if (ehci->reclaim) {
-                       COUNT(ehci->stats.reclaim);
+               if (ehci->async_iaa) {
+                       COUNT(ehci->stats.iaa);
                        end_unlink_async(ehci);
                } else
-                       ehci_dbg(ehci, "IAA with nothing to reclaim?\n");
+                       ehci_dbg(ehci, "IAA with nothing unlinked?\n");
        }
 
        /* remote wakeup [4.3.1] */
@@ -956,15 +820,19 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
                ehci_err(ehci, "fatal error\n");
                dbg_cmd(ehci, "fatal", cmd);
                dbg_status(ehci, "fatal", status);
-               ehci_halt(ehci);
 dead:
-               ehci_reset(ehci);
-               ehci_writel(ehci, 0, &ehci->regs->configured_flag);
                usb_hc_died(hcd);
-               /* generic layer kills/unlinks all urbs, then
-                * uses ehci_stop to clean up the rest
-                */
-               bh = 1;
+
+               /* Don't let the controller do anything more */
+               ehci->shutdown = true;
+               ehci->rh_state = EHCI_RH_STOPPING;
+               ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
+               ehci_writel(ehci, ehci->command, &ehci->regs->command);
+               ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+               ehci_handle_controller_death(ehci);
+
+               /* Handle completions when the controller stops */
+               bh = 0;
        }
 
        if (bh)
@@ -1026,38 +894,6 @@ static int ehci_urb_enqueue (
        }
 }
 
-static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
-{
-       /* failfast */
-       if (ehci->rh_state != EHCI_RH_RUNNING && ehci->reclaim)
-               end_unlink_async(ehci);
-
-       /* If the QH isn't linked then there's nothing we can do
-        * unless we were called during a giveback, in which case
-        * qh_completions() has to deal with it.
-        */
-       if (qh->qh_state != QH_STATE_LINKED) {
-               if (qh->qh_state == QH_STATE_COMPLETING)
-                       qh->needs_rescan = 1;
-               return;
-       }
-
-       /* defer till later if busy */
-       if (ehci->reclaim) {
-               struct ehci_qh          *last;
-
-               for (last = ehci->reclaim;
-                               last->reclaim;
-                               last = last->reclaim)
-                       continue;
-               qh->qh_state = QH_STATE_UNLINK_WAIT;
-               last->reclaim = qh;
-
-       /* start IAA cycle */
-       } else
-               start_unlink_async (ehci, qh);
-}
-
 /* remove from hardware lists
  * completions normally happen asynchronously
  */
@@ -1084,7 +920,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                switch (qh->qh_state) {
                case QH_STATE_LINKED:
                case QH_STATE_COMPLETING:
-                       unlink_async(ehci, qh);
+                       start_unlink_async(ehci, qh);
                        break;
                case QH_STATE_UNLINK:
                case QH_STATE_UNLINK_WAIT:
@@ -1104,7 +940,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                switch (qh->qh_state) {
                case QH_STATE_LINKED:
                case QH_STATE_COMPLETING:
-                       intr_deschedule (ehci, qh);
+                       start_unlink_intr(ehci, qh);
                        break;
                case QH_STATE_IDLE:
                        qh_completions (ehci, qh);
@@ -1152,11 +988,17 @@ rescan:
         * accelerate iso completions ... so spin a while.
         */
        if (qh->hw == NULL) {
-               ehci_vdbg (ehci, "iso delay\n");
-               goto idle_timeout;
+               struct ehci_iso_stream  *stream = ep->hcpriv;
+
+               if (!list_empty(&stream->td_list))
+                       goto idle_timeout;
+
+               /* BUG_ON(!list_empty(&stream->free_list)); */
+               kfree(stream);
+               goto done;
        }
 
-       if (ehci->rh_state != EHCI_RH_RUNNING)
+       if (ehci->rh_state < EHCI_RH_RUNNING)
                qh->qh_state = QH_STATE_IDLE;
        switch (qh->qh_state) {
        case QH_STATE_LINKED:
@@ -1169,7 +1011,7 @@ rescan:
                 * may already be unlinked.
                 */
                if (tmp)
-                       unlink_async(ehci, qh);
+                       start_unlink_async(ehci, qh);
                /* FALL THROUGH */
        case QH_STATE_UNLINK:           /* wait for hw to finish? */
        case QH_STATE_UNLINK_WAIT:
@@ -1181,7 +1023,7 @@ idle_timeout:
                if (qh->clearing_tt)
                        goto idle_timeout;
                if (list_empty (&qh->qtd_list)) {
-                       qh_put (qh);
+                       qh_destroy(ehci, qh);
                        break;
                }
                /* else FALL THROUGH */
@@ -1194,8 +1036,8 @@ idle_timeout:
                        list_empty (&qh->qtd_list) ? "" : "(has tds)");
                break;
        }
+ done:
        ep->hcpriv = NULL;
-done:
        spin_unlock_irqrestore (&ehci->lock, flags);
 }
 
@@ -1232,9 +1074,9 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
                         * re-linking will call qh_refresh().
                         */
                        if (eptype == USB_ENDPOINT_XFER_BULK)
-                               unlink_async(ehci, qh);
+                               start_unlink_async(ehci, qh);
                        else
-                               intr_deschedule(ehci, qh);
+                               start_unlink_intr(ehci, qh);
                }
        }
        spin_unlock_irqrestore(&ehci->lock, flags);
@@ -1247,6 +1089,104 @@ static int ehci_get_frame (struct usb_hcd *hcd)
 }
 
 /*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+/* suspend/resume, section 4.3 */
+
+/* These routines handle the generic parts of controller suspend/resume */
+
+static int __maybe_unused ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+
+       if (time_before(jiffies, ehci->next_statechange))
+               msleep(10);
+
+       /*
+        * Root hub was already suspended.  Disable IRQ emission and
+        * mark HW unaccessible.  The PM and USB cores make sure that
+        * the root hub is either suspended or stopped.
+        */
+       ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
+
+       spin_lock_irq(&ehci->lock);
+       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+       (void) ehci_readl(ehci, &ehci->regs->intr_enable);
+
+       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+       spin_unlock_irq(&ehci->lock);
+
+       return 0;
+}
+
+/* Returns 0 if power was preserved, 1 if power was lost */
+static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+
+       if (time_before(jiffies, ehci->next_statechange))
+               msleep(100);
+
+       /* Mark hardware accessible again as we are back to full power by now */
+       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+       if (ehci->shutdown)
+               return 0;               /* Controller is dead */
+
+       /*
+        * If CF is still set and we aren't resuming from hibernation
+        * then we maintained suspend power.
+        * Just undo the effect of ehci_suspend().
+        */
+       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
+                       !hibernated) {
+               int     mask = INTR_MASK;
+
+               ehci_prepare_ports_for_controller_resume(ehci);
+
+               spin_lock_irq(&ehci->lock);
+               if (ehci->shutdown)
+                       goto skip;
+
+               if (!hcd->self.root_hub->do_remote_wakeup)
+                       mask &= ~STS_PCD;
+               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
+               ehci_readl(ehci, &ehci->regs->intr_enable);
+ skip:
+               spin_unlock_irq(&ehci->lock);
+               return 0;
+       }
+
+       /*
+        * Else reset, to cope with power loss or resume from hibernation
+        * having let the firmware kick in during reboot.
+        */
+       usb_root_hub_lost_power(hcd->self.root_hub);
+       (void) ehci_halt(ehci);
+       (void) ehci_reset(ehci);
+
+       spin_lock_irq(&ehci->lock);
+       if (ehci->shutdown)
+               goto skip;
+
+       ehci_writel(ehci, ehci->command, &ehci->regs->command);
+       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+
+       ehci->rh_state = EHCI_RH_SUSPENDED;
+       spin_unlock_irq(&ehci->lock);
+
+       /* here we "know" root ports should always stay powered */
+       ehci_port_power(ehci, 1);
+
+       return 1;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 /*
  * The EHCI in ChipIdea HDRC cannot be a separate module or device,
  * because its registers (and irq) are shared between host/gadget/otg
index fc9e7cc..c788022 100644 (file)
@@ -59,6 +59,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
        /* Give the connections some time to appear */
        msleep(20);
 
+       spin_lock_irq(&ehci->lock);
        port = HCS_N_PORTS(ehci->hcs_params);
        while (port--) {
                if (test_bit(port, &ehci->owned_ports)) {
@@ -70,23 +71,30 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
                                clear_bit(port, &ehci->owned_ports);
                        else if (test_bit(port, &ehci->companion_ports))
                                ehci_writel(ehci, status & ~PORT_PE, reg);
-                       else
+                       else {
+                               spin_unlock_irq(&ehci->lock);
                                ehci_hub_control(hcd, SetPortFeature,
                                                USB_PORT_FEAT_RESET, port + 1,
                                                NULL, 0);
+                               spin_lock_irq(&ehci->lock);
+                       }
                }
        }
+       spin_unlock_irq(&ehci->lock);
 
        if (!ehci->owned_ports)
                return;
        msleep(90);             /* Wait for resets to complete */
 
+       spin_lock_irq(&ehci->lock);
        port = HCS_N_PORTS(ehci->hcs_params);
        while (port--) {
                if (test_bit(port, &ehci->owned_ports)) {
+                       spin_unlock_irq(&ehci->lock);
                        ehci_hub_control(hcd, GetPortStatus,
                                        0, port + 1,
                                        (char *) &buf, sizeof(buf));
+                       spin_lock_irq(&ehci->lock);
 
                        /* The companion should now own the port,
                         * but if something went wrong the port must not
@@ -105,9 +113,10 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
        }
 
        ehci->owned_ports = 0;
+       spin_unlock_irq(&ehci->lock);
 }
 
-static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
+static int ehci_port_change(struct ehci_hcd *ehci)
 {
        int i = HCS_N_PORTS(ehci->hcs_params);
 
@@ -128,12 +137,11 @@ static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
        return 0;
 }
 
-static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
+static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
                bool suspending, bool do_wakeup)
 {
        int             port;
        u32             temp;
-       unsigned long   flags;
 
        /* If remote wakeup is enabled for the root hub but disabled
         * for the controller, we must adjust all the port wakeup flags
@@ -143,22 +151,20 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
        if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)
                return;
 
-       spin_lock_irqsave(&ehci->lock, flags);
+       spin_lock_irq(&ehci->lock);
 
        /* clear phy low-power mode before changing wakeup flags */
        if (ehci->has_hostpc) {
                port = HCS_N_PORTS(ehci->hcs_params);
                while (port--) {
-                       u32 __iomem     *hostpc_reg;
+                       u32 __iomem     *hostpc_reg = &ehci->regs->hostpc[port];
 
-                       hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
-                                       + HOSTPC0 + 4 * port);
                        temp = ehci_readl(ehci, hostpc_reg);
                        ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg);
                }
-               spin_unlock_irqrestore(&ehci->lock, flags);
+               spin_unlock_irq(&ehci->lock);
                msleep(5);
-               spin_lock_irqsave(&ehci->lock, flags);
+               spin_lock_irq(&ehci->lock);
        }
 
        port = HCS_N_PORTS(ehci->hcs_params);
@@ -185,10 +191,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
        if (ehci->has_hostpc) {
                port = HCS_N_PORTS(ehci->hcs_params);
                while (port--) {
-                       u32 __iomem     *hostpc_reg;
+                       u32 __iomem     *hostpc_reg = &ehci->regs->hostpc[port];
 
-                       hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
-                                       + HOSTPC0 + 4 * port);
                        temp = ehci_readl(ehci, hostpc_reg);
                        ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
                }
@@ -198,7 +202,7 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
        if (!suspending && ehci_port_change(ehci))
                usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
 
-       spin_unlock_irqrestore(&ehci->lock, flags);
+       spin_unlock_irq(&ehci->lock);
 }
 
 static int ehci_bus_suspend (struct usb_hcd *hcd)
@@ -212,10 +216,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
 
        if (time_before (jiffies, ehci->next_statechange))
                msleep(5);
-       del_timer_sync(&ehci->watchdog);
-       del_timer_sync(&ehci->iaa_watchdog);
+
+       /* stop the schedules */
+       ehci_quiesce(ehci);
 
        spin_lock_irq (&ehci->lock);
+       if (ehci->rh_state < EHCI_RH_RUNNING)
+               goto done;
 
        /* Once the controller is stopped, port resumes that are already
         * in progress won't complete.  Hence if remote wakeup is enabled
@@ -230,11 +237,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
                }
        }
 
-       /* stop schedules, clean any completed work */
-       if (ehci->rh_state == EHCI_RH_RUNNING)
-               ehci_quiesce (ehci);
-       ehci_work(ehci);
-
        /* Unlike other USB host controller types, EHCI doesn't have
         * any notion of "global" or bus-wide suspend.  The driver has
         * to manually suspend all the active unsuspended ports, and
@@ -285,11 +287,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
 
                port = HCS_N_PORTS(ehci->hcs_params);
                while (port--) {
-                       u32 __iomem     *hostpc_reg;
+                       u32 __iomem     *hostpc_reg = &ehci->regs->hostpc[port];
                        u32             t3;
 
-                       hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
-                                       + HOSTPC0 + 4 * port);
                        t3 = ehci_readl(ehci, hostpc_reg);
                        ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
                        t3 = ehci_readl(ehci, hostpc_reg);
@@ -298,6 +298,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
                                        "succeeded" : "failed");
                }
        }
+       spin_unlock_irq(&ehci->lock);
 
        /* Apparently some devices need a >= 1-uframe delay here */
        if (ehci->bus_suspended)
@@ -305,10 +306,18 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
 
        /* turn off now-idle HC */
        ehci_halt (ehci);
+
+       spin_lock_irq(&ehci->lock);
+       if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD))
+               ehci_handle_controller_death(ehci);
+       if (ehci->rh_state != EHCI_RH_RUNNING)
+               goto done;
        ehci->rh_state = EHCI_RH_SUSPENDED;
 
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
+       end_unlink_async(ehci);
+       unlink_empty_async(ehci);
+       ehci_handle_intr_unlinks(ehci);
+       end_free_itds(ehci);
 
        /* allow remote wakeup */
        mask = INTR_MASK;
@@ -317,13 +326,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
        ehci_writel(ehci, mask, &ehci->regs->intr_enable);
        ehci_readl(ehci, &ehci->regs->intr_enable);
 
+ done:
        ehci->next_statechange = jiffies + msecs_to_jiffies(10);
+       ehci->enabled_hrtimer_events = 0;
+       ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
        spin_unlock_irq (&ehci->lock);
 
-       /* ehci_work() may have re-enabled the watchdog timer, which we do not
-        * want, and so we must delete any pending watchdog timer events.
-        */
-       del_timer_sync(&ehci->watchdog);
+       hrtimer_cancel(&ehci->hrtimer);
        return 0;
 }
 
@@ -340,10 +349,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
        if (time_before (jiffies, ehci->next_statechange))
                msleep(5);
        spin_lock_irq (&ehci->lock);
-       if (!HCD_HW_ACCESSIBLE(hcd)) {
-               spin_unlock_irq(&ehci->lock);
-               return -ESHUTDOWN;
-       }
+       if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown)
+               goto shutdown;
 
        if (unlikely(ehci->debug)) {
                if (!dbgp_reset_prep())
@@ -382,16 +389,17 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
        spin_unlock_irq(&ehci->lock);
        msleep(8);
        spin_lock_irq(&ehci->lock);
+       if (ehci->shutdown)
+               goto shutdown;
 
        /* clear phy low-power mode before resume */
        if (ehci->bus_suspended && ehci->has_hostpc) {
                i = HCS_N_PORTS(ehci->hcs_params);
                while (i--) {
                        if (test_bit(i, &ehci->bus_suspended)) {
-                               u32 __iomem     *hostpc_reg;
+                               u32 __iomem     *hostpc_reg =
+                                                       &ehci->regs->hostpc[i];
 
-                               hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
-                                               + HOSTPC0 + 4 * i);
                                temp = ehci_readl(ehci, hostpc_reg);
                                ehci_writel(ehci, temp & ~HOSTPC_PHCD,
                                                hostpc_reg);
@@ -400,6 +408,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
                spin_unlock_irq(&ehci->lock);
                msleep(5);
                spin_lock_irq(&ehci->lock);
+               if (ehci->shutdown)
+                       goto shutdown;
        }
 
        /* manually resume the ports we suspended during bus_suspend() */
@@ -420,6 +430,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
                spin_unlock_irq(&ehci->lock);
                msleep(20);
                spin_lock_irq(&ehci->lock);
+               if (ehci->shutdown)
+                       goto shutdown;
        }
 
        i = HCS_N_PORTS (ehci->hcs_params);
@@ -431,27 +443,25 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
                        ehci_vdbg (ehci, "resumed port %d\n", i + 1);
                }
        }
-       (void) ehci_readl(ehci, &ehci->regs->command);
-
-       /* maybe re-activate the schedule(s) */
-       temp = 0;
-       if (ehci->async->qh_next.qh)
-               temp |= CMD_ASE;
-       if (ehci->periodic_sched)
-               temp |= CMD_PSE;
-       if (temp) {
-               ehci->command |= temp;
-               ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       }
 
        ehci->next_statechange = jiffies + msecs_to_jiffies(5);
+       spin_unlock_irq(&ehci->lock);
+
+       ehci_handover_companion_ports(ehci);
 
        /* Now we can safely re-enable irqs */
+       spin_lock_irq(&ehci->lock);
+       if (ehci->shutdown)
+               goto shutdown;
        ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
+       (void) ehci_readl(ehci, &ehci->regs->intr_enable);
+       spin_unlock_irq(&ehci->lock);
 
-       spin_unlock_irq (&ehci->lock);
-       ehci_handover_companion_ports(ehci);
        return 0;
+
+ shutdown:
+       spin_unlock_irq(&ehci->lock);
+       return -ESHUTDOWN;
 }
 
 #else
@@ -667,7 +677,7 @@ static int ehci_hub_control (
        int             ports = HCS_N_PORTS (ehci->hcs_params);
        u32 __iomem     *status_reg = &ehci->regs->port_status[
                                (wIndex & 0xff) - 1];
-       u32 __iomem     *hostpc_reg = NULL;
+       u32 __iomem     *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1];
        u32             temp, temp1, status;
        unsigned long   flags;
        int             retval = 0;
@@ -680,9 +690,6 @@ static int ehci_hub_control (
         * power, "this is the one", etc.  EHCI spec supports this.
         */
 
-       if (ehci->has_hostpc)
-               hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
-                               + HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
        spin_lock_irqsave (&ehci->lock, flags);
        switch (typeReq) {
        case ClearHubFeature:
@@ -724,7 +731,7 @@ static int ehci_hub_control (
 #ifdef CONFIG_USB_OTG
                        if ((hcd->self.otg_port == (wIndex + 1))
                            && hcd->self.b_hnp_enable) {
-                               otg_start_hnp(ehci->transceiver->otg);
+                               otg_start_hnp(hcd->phy->otg);
                                break;
                        }
 #endif
@@ -734,7 +741,7 @@ static int ehci_hub_control (
                                goto error;
 
                        /* clear phy low-power mode before resume */
-                       if (hostpc_reg) {
+                       if (ehci->has_hostpc) {
                                temp1 = ehci_readl(ehci, hostpc_reg);
                                ehci_writel(ehci, temp1 & ~HOSTPC_PHCD,
                                                hostpc_reg);
@@ -984,7 +991,7 @@ static int ehci_hub_control (
                        temp &= ~PORT_WKCONN_E;
                        temp |= PORT_WKDISC_E | PORT_WKOC_E;
                        ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
-                       if (hostpc_reg) {
+                       if (ehci->has_hostpc) {
                                spin_unlock_irqrestore(&ehci->lock, flags);
                                msleep(5);/* 5ms for HCD enter low pwr mode */
                                spin_lock_irqsave(&ehci->lock, flags);
@@ -1041,7 +1048,9 @@ static int ehci_hub_control (
                case USB_PORT_FEAT_TEST:
                        if (!selector || selector > 5)
                                goto error;
+                       spin_unlock_irqrestore(&ehci->lock, flags);
                        ehci_quiesce(ehci);
+                       spin_lock_irqsave(&ehci->lock, flags);
 
                        /* Put all enabled ports into suspend */
                        while (ports--) {
@@ -1053,7 +1062,11 @@ static int ehci_hub_control (
                                        ehci_writel(ehci, temp | PORT_SUSPEND,
                                                        sreg);
                        }
+
+                       spin_unlock_irqrestore(&ehci->lock, flags);
                        ehci_halt(ehci);
+                       spin_lock_irqsave(&ehci->lock, flags);
+
                        temp = ehci_readl(ehci, status_reg);
                        temp |= selector << 16;
                        ehci_writel(ehci, temp, status_reg);
index c4460f3..488d401 100644 (file)
@@ -22,14 +22,10 @@ static int ixp4xx_ehci_init(struct usb_hcd *hcd)
        ehci->big_endian_mmio = 1;
 
        ehci->caps = hcd->regs + 0x100;
-       ehci->regs = hcd->regs + 0x100
-               + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
 
        hcd->has_tt = 1;
-       ehci_reset(ehci);
 
-       retval = ehci_init(hcd);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
index 12f70c3..ef2c3a1 100644 (file)
@@ -64,10 +64,8 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
 }
 
 
-static void qh_destroy(struct ehci_qh *qh)
+static void qh_destroy(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       struct ehci_hcd *ehci = qh->ehci;
-
        /* clean qtds first, and know this is not linked */
        if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
                ehci_dbg (ehci, "unused qh not empty!\n");
@@ -92,8 +90,6 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
        if (!qh->hw)
                goto fail;
        memset(qh->hw, 0, sizeof *qh->hw);
-       qh->refcount = 1;
-       qh->ehci = ehci;
        qh->qh_dma = dma;
        // INIT_LIST_HEAD (&qh->qh_list);
        INIT_LIST_HEAD (&qh->qtd_list);
@@ -113,20 +109,6 @@ fail:
        return NULL;
 }
 
-/* to share a qh (cpu threads, or hc) */
-static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
-{
-       WARN_ON(!qh->refcount);
-       qh->refcount++;
-       return qh;
-}
-
-static inline void qh_put (struct ehci_qh *qh)
-{
-       if (!--qh->refcount)
-               qh_destroy(qh);
-}
-
 /*-------------------------------------------------------------------------*/
 
 /* The queue heads and transfer descriptors are managed from pools tied
@@ -136,13 +118,12 @@ static inline void qh_put (struct ehci_qh *qh)
 
 static void ehci_mem_cleanup (struct ehci_hcd *ehci)
 {
-       free_cached_lists(ehci);
        if (ehci->async)
-               qh_put (ehci->async);
+               qh_destroy(ehci, ehci->async);
        ehci->async = NULL;
 
        if (ehci->dummy)
-               qh_put(ehci->dummy);
+               qh_destroy(ehci, ehci->dummy);
        ehci->dummy = NULL;
 
        /* DMA consistent memory and pools */
index 9803a55..17dd9e9 100644 (file)
@@ -145,8 +145,8 @@ static int ehci_msm_probe(struct platform_device *pdev)
         * powering up VBUS, mapping of registers address space and power
         * management.
         */
-       phy = usb_get_transceiver();
-       if (!phy) {
+       phy = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(phy)) {
                dev_err(&pdev->dev, "unable to find transceiver\n");
                ret = -ENODEV;
                goto unmap;
@@ -169,7 +169,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
        return 0;
 
 put_transceiver:
-       usb_put_transceiver(phy);
+       usb_put_phy(phy);
 unmap:
        iounmap(hcd->regs);
 put_hcd:
@@ -187,7 +187,7 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
        pm_runtime_set_suspended(&pdev->dev);
 
        otg_set_host(phy->otg, NULL);
-       usb_put_transceiver(phy);
+       usb_put_phy(phy);
 
        usb_put_hcd(hcd);
 
@@ -198,24 +198,11 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
 static int ehci_msm_pm_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       bool wakeup = device_may_wakeup(dev);
+       bool do_wakeup = device_may_wakeup(dev);
 
        dev_dbg(dev, "ehci-msm PM suspend\n");
 
-       /*
-        * EHCI helper function has also the same check before manipulating
-        * port wakeup flags.  We do check here the same condition before
-        * calling the same helper function to avoid bringing hardware
-        * from Low power mode when there is no need for adjusting port
-        * wakeup flags.
-        */
-       if (hcd->self.root_hub->do_remote_wakeup && !wakeup) {
-               pm_runtime_resume(dev);
-               ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
-                               wakeup);
-       }
-
-       return 0;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static int ehci_msm_pm_resume(struct device *dev)
@@ -223,7 +210,7 @@ static int ehci_msm_pm_resume(struct device *dev)
        struct usb_hcd *hcd = dev_get_drvdata(dev);
 
        dev_dbg(dev, "ehci-msm PM resume\n");
-       ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
+       ehci_resume(hcd, false);
 
        return 0;
 }
index a936bbc..f6df1cc 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/usb/otg.h>
 #include <linux/platform_data/mv_usb.h>
 
@@ -76,7 +77,6 @@ static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
 
 static int mv_ehci_reset(struct usb_hcd *hcd)
 {
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        struct device *dev = hcd->self.controller;
        struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
        int retval;
@@ -86,25 +86,13 @@ static int mv_ehci_reset(struct usb_hcd *hcd)
                return -ENODEV;
        }
 
-       /*
-        * data structure init
-        */
-       retval = ehci_init(hcd);
-       if (retval) {
-               dev_err(dev, "ehci_init failed %d\n", retval);
-               return retval;
-       }
-
        hcd->has_tt = 1;
-       ehci->sbrn = 0x20;
 
-       retval = ehci_reset(ehci);
-       if (retval) {
-               dev_err(dev, "ehci_reset failed %d\n", retval);
-               return retval;
-       }
+       retval = ehci_setup(hcd);
+       if (retval)
+               dev_err(dev, "ehci_setup failed %d\n", retval);
 
-       return 0;
+       return retval;
 }
 
 static const struct hc_driver mv_ehci_hc_driver = {
@@ -247,14 +235,12 @@ static int mv_ehci_probe(struct platform_device *pdev)
 
        ehci = hcd_to_ehci(hcd);
        ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
-       ehci->regs = (struct ehci_regs *) ehci_mv->op_regs;
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
 
        ehci_mv->mode = pdata->mode;
        if (ehci_mv->mode == MV_USB_MODE_OTG) {
 #ifdef CONFIG_USB_OTG_UTILS
-               ehci_mv->otg = usb_get_transceiver();
-               if (!ehci_mv->otg) {
+               ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2);
+               if (IS_ERR_OR_NULL(ehci_mv->otg)) {
                        dev_err(&pdev->dev,
                                "unable to find transceiver\n");
                        retval = -ENODEV;
@@ -302,8 +288,8 @@ err_set_vbus:
                pdata->set_vbus(0);
 #ifdef CONFIG_USB_OTG_UTILS
 err_put_transceiver:
-       if (ehci_mv->otg)
-               usb_put_transceiver(ehci_mv->otg);
+       if (!IS_ERR_OR_NULL(ehci_mv->otg))
+               usb_put_phy(ehci_mv->otg);
 #endif
 err_disable_clk:
        mv_ehci_disable(ehci_mv);
@@ -331,9 +317,9 @@ static int mv_ehci_remove(struct platform_device *pdev)
        if (hcd->rh_registered)
                usb_remove_hcd(hcd);
 
-       if (ehci_mv->otg) {
+       if (!IS_ERR_OR_NULL(ehci_mv->otg)) {
                otg_set_host(ehci_mv->otg->otg, NULL);
-               usb_put_transceiver(ehci_mv->otg);
+               usb_put_phy(ehci_mv->otg);
        }
 
        if (ehci_mv->mode == MV_USB_MODE_HOST) {
index c778ffe..3420137 100644 (file)
@@ -42,27 +42,12 @@ static int ehci_mxc_setup(struct usb_hcd *hcd)
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        int retval;
 
-       dbg_hcs_params(ehci, "reset");
-       dbg_hcc_params(ehci, "reset");
-
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
        hcd->has_tt = 1;
 
-       retval = ehci_halt(ehci);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
-       /* data structure init */
-       retval = ehci_init(hcd);
-       if (retval)
-               return retval;
-
-       ehci->sbrn = 0x20;
-
-       ehci_reset(ehci);
-
        ehci_port_power(ehci, 0);
        return 0;
 }
index c010488..ba26957 100644 (file)
@@ -56,7 +56,7 @@ static const struct hc_driver ehci_octeon_hc_driver = {
        /*
         * basic lifecycle operations
         */
-       .reset                  = ehci_init,
+       .reset                  = ehci_setup,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
        .shutdown               = ehci_shutdown,
@@ -150,12 +150,6 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev)
 #endif
 
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
-       ehci_reset(ehci);
 
        ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (ret) {
index c304354..ec21f4a 100644 (file)
@@ -115,9 +115,8 @@ static void omap_ehci_erratum_i693(struct ehci_hcd *ehci)
        clk_disable(usbhost_p2_fck);
 }
 
-static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
+static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port)
 {
-       struct usb_hcd  *hcd = dev_get_drvdata(&pdev->dev);
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
        unsigned reg = 0;
 
@@ -139,12 +138,63 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
                cpu_relax();
 
                if (time_after(jiffies, timeout)) {
-                       dev_dbg(&pdev->dev, "phy reset operation timed out\n");
+                       dev_dbg(hcd->self.controller,
+                                       "phy reset operation timed out\n");
                        break;
                }
        }
 }
 
+static int omap_ehci_init(struct usb_hcd *hcd)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+       int                     rc;
+       struct ehci_hcd_omap_platform_data      *pdata;
+
+       pdata = hcd->self.controller->platform_data;
+       if (pdata->phy_reset) {
+               if (gpio_is_valid(pdata->reset_gpio_port[0]))
+                       gpio_request_one(pdata->reset_gpio_port[0],
+                                        GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
+
+               if (gpio_is_valid(pdata->reset_gpio_port[1]))
+                       gpio_request_one(pdata->reset_gpio_port[1],
+                                        GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
+
+               /* Hold the PHY in RESET for enough time till DIR is high */
+               udelay(10);
+       }
+
+       /* Soft reset the PHY using PHY reset command over ULPI */
+       if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY)
+               omap_ehci_soft_phy_reset(hcd, 0);
+       if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY)
+               omap_ehci_soft_phy_reset(hcd, 1);
+
+       /* we know this is the memory we want, no need to ioremap again */
+       ehci->caps = hcd->regs;
+
+       rc = ehci_setup(hcd);
+
+       if (pdata->phy_reset) {
+               /* Hold the PHY in RESET for enough time till
+                * PHY is settled and ready
+                */
+               udelay(10);
+
+               if (gpio_is_valid(pdata->reset_gpio_port[0]))
+                       gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1);
+
+               if (gpio_is_valid(pdata->reset_gpio_port[1]))
+                       gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1);
+       }
+
+       /* root ports should always stay powered */
+       ehci_port_power(ehci, 1);
+
+       return rc;
+}
+
 static int omap_ehci_hub_control(
        struct usb_hcd  *hcd,
        u16             typeReq,
@@ -219,7 +269,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
        struct resource                         *res;
        struct usb_hcd                          *hcd;
        void __iomem                            *regs;
-       struct ehci_hcd                         *omap_ehci;
        int                                     ret = -ENODEV;
        int                                     irq;
        int                                     i;
@@ -281,18 +330,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
                }
        }
 
-       /* Hold PHYs in reset while initializing EHCI controller */
-       if (pdata->phy_reset) {
-               if (gpio_is_valid(pdata->reset_gpio_port[0]))
-                       gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0);
-
-               if (gpio_is_valid(pdata->reset_gpio_port[1]))
-                       gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0);
-
-               /* Hold the PHY in RESET for enough time till DIR is high */
-               udelay(10);
-       }
-
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
 
@@ -308,49 +345,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
        ehci_write(regs, EHCI_INSNREG04,
                                EHCI_INSNREG04_DISABLE_UNSUSPEND);
 
-       /* Soft reset the PHY using PHY reset command over ULPI */
-       if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY)
-               omap_ehci_soft_phy_reset(pdev, 0);
-       if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY)
-               omap_ehci_soft_phy_reset(pdev, 1);
-
-       omap_ehci = hcd_to_ehci(hcd);
-       omap_ehci->sbrn = 0x20;
-
-       /* we know this is the memory we want, no need to ioremap again */
-       omap_ehci->caps = hcd->regs;
-       omap_ehci->regs = hcd->regs
-               + HC_LENGTH(ehci, readl(&omap_ehci->caps->hc_capbase));
-
-       dbg_hcs_params(omap_ehci, "reset");
-       dbg_hcc_params(omap_ehci, "reset");
-
-       /* cache this readonly data; minimize chip reads */
-       omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params);
-
-       ehci_reset(omap_ehci);
        ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (ret) {
                dev_err(dev, "failed to add hcd with err %d\n", ret);
-               goto err_add_hcd;
+               goto err_pm_runtime;
        }
 
-       if (pdata->phy_reset) {
-               /* Hold the PHY in RESET for enough time till
-                * PHY is settled and ready
-                */
-               udelay(10);
-
-               if (gpio_is_valid(pdata->reset_gpio_port[0]))
-                       gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1);
-
-               if (gpio_is_valid(pdata->reset_gpio_port[1]))
-                       gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1);
-       }
-
-       /* root ports should always stay powered */
-       ehci_port_power(omap_ehci, 1);
-
        /* get clocks */
        utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
        if (IS_ERR(utmi_p1_fck)) {
@@ -422,8 +422,12 @@ err_utmi_p1_fck:
        clk_put(utmi_p1_fck);
 
 err_add_hcd:
+       usb_remove_hcd(hcd);
+
+err_pm_runtime:
        disable_put_regulator(pdata);
        pm_runtime_put_sync(dev);
+       usb_put_hcd(hcd);
 
 err_io:
        iounmap(regs);
@@ -506,7 +510,7 @@ static const struct hc_driver ehci_omap_hc_driver = {
        /*
         * basic lifecycle operations
         */
-       .reset                  = ehci_init,
+       .reset                  = omap_ehci_init,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
        .shutdown               = ehci_shutdown,
index 82de107..9408da8 100644 (file)
@@ -106,21 +106,10 @@ static int ehci_orion_setup(struct usb_hcd *hcd)
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        int retval;
 
-       hcd->has_tt = 1;
-
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
-
-       /*
-        * data structure init
-        */
-       retval = ehci_init(hcd);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
-       ehci_reset(ehci);
-
        ehci_port_power(ehci, 0);
 
        return retval;
@@ -261,11 +250,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)
 
        ehci = hcd_to_ehci(hcd);
        ehci->caps = hcd->regs + 0x100;
-       ehci->regs = hcd->regs + 0x100 +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
        hcd->has_tt = 1;
-       ehci->sbrn = 0x20;
 
        /*
         * (Re-)program MBUS remapping windows if we are asked to.
index 1234817..2cb7d37 100644 (file)
@@ -54,6 +54,17 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
        u32                     temp;
        int                     retval;
 
+       ehci->caps = hcd->regs;
+
+       /*
+        * ehci_init() causes memory for DMA transfers to be
+        * allocated.  Thus, any vendor-specific workarounds based on
+        * limiting the type of memory used for DMA transfers must
+        * happen before ehci_setup() is called.
+        *
+        * Most other workarounds can be done either before or after
+        * init and reset; they are located here too.
+        */
        switch (pdev->vendor) {
        case PCI_VENDOR_ID_TOSHIBA_2:
                /* celleb's companion chip */
@@ -66,20 +77,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
 #endif
                }
                break;
-       }
-
-       ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-
-       dbg_hcs_params(ehci, "reset");
-       dbg_hcc_params(ehci, "reset");
-
-        /* ehci_init() causes memory for DMA transfers to be
-         * allocated.  Thus, any vendor-specific workarounds based on
-         * limiting the type of memory used for DMA transfers must
-         * happen before ehci_init() is called. */
-       switch (pdev->vendor) {
        case PCI_VENDOR_ID_NVIDIA:
                /* NVidia reports that certain chips don't handle
                 * QH, ITD, or SITD addresses above 2GB.  (But TD,
@@ -95,61 +92,24 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                                ehci_warn(ehci, "can't enable NVidia "
                                        "workaround for >2GB RAM\n");
                        break;
-               }
-               break;
-       }
-
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
 
-       if ((pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x7808) ||
-           (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x4396)) {
-               /* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
-                * read/write memory space which does not belong to it when
-                * there is NULL pointer with T-bit set to 1 in the frame list
-                * table. To avoid the issue, the frame list link pointer
-                * should always contain a valid pointer to a inactive qh.
+               /* Some NForce2 chips have problems with selective suspend;
+                * fixed in newer silicon.
                 */
-               ehci->use_dummy_qh = 1;
-               ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI "
-                               "dummy qh workaround\n");
-       }
-
-       /* data structure init */
-       retval = ehci_init(hcd);
-       if (retval)
-               return retval;
-
-       switch (pdev->vendor) {
-       case PCI_VENDOR_ID_NEC:
-               ehci->need_io_watchdog = 0;
+               case 0x0068:
+                       if (pdev->revision < 0xa4)
+                               ehci->no_selective_suspend = 1;
+                       break;
+               }
                break;
        case PCI_VENDOR_ID_INTEL:
-               ehci->need_io_watchdog = 0;
                ehci->fs_i_thresh = 1;
-               if (pdev->device == 0x27cc) {
-                       ehci->broken_periodic = 1;
-                       ehci_info(ehci, "using broken periodic workaround\n");
-               }
-               if (pdev->device == 0x0806 || pdev->device == 0x0811
-                               || pdev->device == 0x0829) {
-                       ehci_info(ehci, "disable lpm for langwell/penwell\n");
-                       ehci->has_lpm = 0;
-               }
-               if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) {
+               if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB)
                        hcd->has_tt = 1;
-                       tdi_reset(ehci);
-               }
                break;
        case PCI_VENDOR_ID_TDI:
-               if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
+               if (pdev->device == PCI_DEVICE_ID_TDI_EHCI)
                        hcd->has_tt = 1;
-                       tdi_reset(ehci);
-               }
                break;
        case PCI_VENDOR_ID_AMD:
                /* AMD PLL quirk */
@@ -161,28 +121,17 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                        retval = -EIO;
                        goto done;
                }
-               break;
-       case PCI_VENDOR_ID_NVIDIA:
-               switch (pdev->device) {
-               /* Some NForce2 chips have problems with selective suspend;
-                * fixed in newer silicon.
-                */
-               case 0x0068:
-                       if (pdev->revision < 0xa4)
-                               ehci->no_selective_suspend = 1;
-                       break;
 
-               /* MCP89 chips on the MacBookAir3,1 give EPROTO when
-                * fetching device descriptors unless LPM is disabled.
-                * There are also intermittent problems enumerating
-                * devices with PPCD enabled.
+               /*
+                * EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
+                * read/write memory space which does not belong to it when
+                * there is NULL pointer with T-bit set to 1 in the frame list
+                * table. To avoid the issue, the frame list link pointer
+                * should always contain a valid pointer to a inactive qh.
                 */
-               case 0x0d9d:
-                       ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89");
-                       ehci->has_lpm = 0;
-                       ehci->has_ppcd = 0;
-                       ehci->command &= ~CMD_PPCEE;
-                       break;
+               if (pdev->device == 0x7808) {
+                       ehci->use_dummy_qh = 1;
+                       ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n");
                }
                break;
        case PCI_VENDOR_ID_VIA:
@@ -203,6 +152,18 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                /* AMD PLL quirk */
                if (usb_amd_find_chipset_info())
                        ehci->amd_pll_fix = 1;
+
+               /*
+                * EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
+                * read/write memory space which does not belong to it when
+                * there is NULL pointer with T-bit set to 1 in the frame list
+                * table. To avoid the issue, the frame list link pointer
+                * should always contain a valid pointer to a inactive qh.
+                */
+               if (pdev->device == 0x4396) {
+                       ehci->use_dummy_qh = 1;
+                       ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n");
+               }
                /* SB600 and old version of SB700 have a bug in EHCI controller,
                 * which causes usb devices lose response in some cases.
                 */
@@ -231,6 +192,40 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                break;
        }
 
+       retval = ehci_setup(hcd);
+       if (retval)
+               return retval;
+
+       /* These workarounds need to be applied after ehci_setup() */
+       switch (pdev->vendor) {
+       case PCI_VENDOR_ID_NEC:
+               ehci->need_io_watchdog = 0;
+               break;
+       case PCI_VENDOR_ID_INTEL:
+               ehci->need_io_watchdog = 0;
+               if (pdev->device == 0x0806 || pdev->device == 0x0811
+                               || pdev->device == 0x0829) {
+                       ehci_info(ehci, "disable lpm for langwell/penwell\n");
+                       ehci->has_lpm = 0;
+               }
+               break;
+       case PCI_VENDOR_ID_NVIDIA:
+               switch (pdev->device) {
+               /* MCP89 chips on the MacBookAir3,1 give EPROTO when
+                * fetching device descriptors unless LPM is disabled.
+                * There are also intermittent problems enumerating
+                * devices with PPCD enabled.
+                */
+               case 0x0d9d:
+                       ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89");
+                       ehci->has_lpm = 0;
+                       ehci->has_ppcd = 0;
+                       ehci->command &= ~CMD_PPCEE;
+                       break;
+               }
+               break;
+       }
+
        /* optional debug port, normally in the first BAR */
        temp = pci_find_capability(pdev, 0x0a);
        if (temp) {
@@ -238,7 +233,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                temp >>= 16;
                if ((temp & (3 << 13)) == (1 << 13)) {
                        temp &= 0x1fff;
-                       ehci->debug = ehci_to_hcd(ehci)->regs + temp;
+                       ehci->debug = hcd->regs + temp;
                        temp = ehci_readl(ehci, &ehci->debug->control);
                        ehci_info(ehci, "debug port %d%s\n",
                                HCS_DEBUG_PORT(ehci->hcs_params),
@@ -250,8 +245,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                }
        }
 
-       ehci_reset(ehci);
-
        /* at least the Genesys GL880S needs fixup here */
        temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
        temp &= 0x0f;
@@ -275,10 +268,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
        }
 
        /* Serial Bus Release Number is at PCI 0x60 offset */
-       pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
        if (pdev->vendor == PCI_VENDOR_ID_STMICRO
            && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST)
-               ehci->sbrn = 0x20; /* ConneXT has no sbrn register */
+               ;       /* ConneXT has no sbrn register */
+       else
+               pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
 
        /* Keep this around for a while just in case some EHCI
         * implementation uses legacy PCI PM support.  This test
@@ -331,29 +325,7 @@ done:
 
 static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 {
-       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
-       unsigned long           flags;
-       int                     rc = 0;
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(10);
-
-       /* Root hub was already suspended. Disable irq emission and
-        * mark HW unaccessible.  The PM and USB cores make sure that
-        * the root hub is either suspended or stopped.
-        */
-       ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
-       spin_lock_irqsave (&ehci->lock, flags);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       (void)ehci_readl(ehci, &ehci->regs->intr_enable);
-
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       spin_unlock_irqrestore (&ehci->lock, flags);
-
-       // could save FLADJ in case of Vaux power loss
-       // ... we'd only use it to handle clock skew
-
-       return rc;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev)
@@ -402,54 +374,8 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
        if (usb_is_intel_switchable_ehci(pdev))
                ehci_enable_xhci_companion();
 
-       // maybe restore FLADJ
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       /* Mark hardware accessible again as we are out of D3 state by now */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       /* If CF is still set and we aren't resuming from hibernation
-        * then we maintained PCI Vaux power.
-        * Just undo the effect of ehci_pci_suspend().
-        */
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
-                               !hibernated) {
-               int     mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       /* Else reset, to cope with power loss or flush-to-storage
-        * style "resume" having let BIOS kick in during reboot.
-        */
-       (void) ehci_halt(ehci);
-       (void) ehci_reset(ehci);
-       (void) ehci_pci_reinit(ehci, pdev);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
-
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
-
-       ehci->rh_state = EHCI_RH_SUSPENDED;
+       if (ehci_resume(hcd, hibernated) != 0)
+               (void) ehci_pci_reinit(ehci, pdev);
        return 0;
 }
 #endif
index dfe881a..4b1d896 100644 (file)
@@ -153,17 +153,16 @@ static int __devexit ehci_platform_remove(struct platform_device *dev)
 static int ehci_platform_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       bool wakeup = device_may_wakeup(dev);
+       bool do_wakeup = device_may_wakeup(dev);
 
-       ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup);
-       return 0;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static int ehci_platform_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
 
-       ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
+       ehci_resume(hcd, false);
        return 0;
 }
 
index e8d54de..087aee2 100644 (file)
@@ -78,27 +78,14 @@ static int ehci_msp_setup(struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
        int                     retval;
+
        ehci->big_endian_mmio = 1;
        ehci->big_endian_desc = 1;
 
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-       dbg_hcs_params(ehci, "reset");
-       dbg_hcc_params(ehci, "reset");
-
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
        hcd->has_tt = 1;
 
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
-
-       ehci_reset(ehci);
-
-       /* data structure init */
-       retval = ehci_init(hcd);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
index 41d11fe..bbbe89d 100644 (file)
 #include <linux/of.h>
 #include <linux/of_platform.h>
 
-/* called during probe() after chip reset completes */
-static int ehci_ppc_of_setup(struct usb_hcd *hcd)
-{
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       int             retval;
-
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
-
-       retval = ehci_init(hcd);
-       if (retval)
-               return retval;
-
-       ehci->sbrn = 0x20;
-       return ehci_reset(ehci);
-}
-
 
 static const struct hc_driver ehci_ppc_of_hc_driver = {
        .description            = hcd_name,
@@ -50,7 +32,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
        /*
         * basic lifecycle operations
         */
-       .reset                  = ehci_ppc_of_setup,
+       .reset                  = ehci_setup,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
        .shutdown               = ehci_shutdown,
@@ -178,11 +160,6 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op)
                ehci->big_endian_desc = 1;
 
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
 
        if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) {
                rv = ppc44x_enable_bmt(dn);
index a20e496..45a356e 100644 (file)
@@ -55,28 +55,12 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 
        ehci->big_endian_mmio = 1;
-
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci,
-               &ehci->caps->hc_capbase));
-
-       dbg_hcs_params(ehci, "reset");
-       dbg_hcc_params(ehci, "reset");
-
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
-       result = ehci_halt(ehci);
 
+       result = ehci_setup(hcd);
        if (result)
                return result;
 
-       result = ehci_init(hcd);
-
-       if (result)
-               return result;
-
-       ehci_reset(ehci);
-
        ps3_ehci_setup_insnreg(ehci);
 
        return result;
index 4378bf7..9bc39ca 100644 (file)
@@ -100,7 +100,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
         * and set the pseudo-toggle in udev. Only usb_clear_halt() will
         * ever clear it.
         */
-       if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
+       if (!(hw->hw_info1 & cpu_to_hc32(ehci, QH_TOGGLE_CTL))) {
                unsigned        is_out, epnum;
 
                is_out = qh->is_out;
@@ -265,7 +265,6 @@ __acquires(ehci->lock)
                        /* ... update hc-wide periodic stats (for usbfs) */
                        ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
                }
-               qh_put (qh);
        }
 
        if (unlikely(urb->unlinked)) {
@@ -294,9 +293,6 @@ __acquires(ehci->lock)
        spin_lock (&ehci->lock);
 }
 
-static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
-static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
-
 static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
 
 /*
@@ -326,7 +322,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
         *
         * It's a bug for qh->qh_state to be anything other than
         * QH_STATE_IDLE, unless our caller is scan_async() or
-        * scan_periodic().
+        * scan_intr().
         */
        state = qh->qh_state;
        qh->qh_state = QH_STATE_COMPLETING;
@@ -434,7 +430,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
                /* stop scanning when we reach qtds the hc is using */
                } else if (likely (!stopped
-                               && ehci->rh_state == EHCI_RH_RUNNING)) {
+                               && ehci->rh_state >= EHCI_RH_RUNNING)) {
                        break;
 
                /* scan the whole queue for unlinks whenever it stops */
@@ -442,7 +438,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
                        stopped = 1;
 
                        /* cancel everything if we halt, suspend, etc */
-                       if (ehci->rh_state != EHCI_RH_RUNNING)
+                       if (ehci->rh_state < EHCI_RH_RUNNING)
                                last_status = -ESHUTDOWN;
 
                        /* this qtd is active; skip it unless a previous qtd
@@ -836,7 +832,6 @@ qh_make (
                                is_input, 0,
                                hb_mult(maxp) * max_packet(maxp)));
                qh->start = NO_FRAME;
-               qh->stamp = ehci->periodic_stamp;
 
                if (urb->dev->speed == USB_SPEED_HIGH) {
                        qh->c_usecs = 0;
@@ -887,7 +882,7 @@ qh_make (
        /* using TT? */
        switch (urb->dev->speed) {
        case USB_SPEED_LOW:
-               info1 |= (1 << 12);     /* EPS "low" */
+               info1 |= QH_LOW_SPEED;
                /* FALL THROUGH */
 
        case USB_SPEED_FULL:
@@ -895,8 +890,8 @@ qh_make (
                if (type != PIPE_INTERRUPT)
                        info1 |= (EHCI_TUNE_RL_TT << 28);
                if (type == PIPE_CONTROL) {
-                       info1 |= (1 << 27);     /* for TT */
-                       info1 |= 1 << 14;       /* toggle from qtd */
+                       info1 |= QH_CONTROL_EP;         /* for TT */
+                       info1 |= QH_TOGGLE_CTL;         /* toggle from qtd */
                }
                info1 |= maxp << 16;
 
@@ -921,11 +916,11 @@ qh_make (
                break;
 
        case USB_SPEED_HIGH:            /* no TT involved */
-               info1 |= (2 << 12);     /* EPS "high" */
+               info1 |= QH_HIGH_SPEED;
                if (type == PIPE_CONTROL) {
                        info1 |= (EHCI_TUNE_RL_HS << 28);
                        info1 |= 64 << 16;      /* usb2 fixed maxpacket */
-                       info1 |= 1 << 14;       /* toggle from qtd */
+                       info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
                        info2 |= (EHCI_TUNE_MULT_HS << 30);
                } else if (type == PIPE_BULK) {
                        info1 |= (EHCI_TUNE_RL_HS << 28);
@@ -946,7 +941,7 @@ qh_make (
                ehci_dbg(ehci, "bogus dev %p speed %d\n", urb->dev,
                        urb->dev->speed);
 done:
-               qh_put (qh);
+               qh_destroy(ehci, qh);
                return NULL;
        }
 
@@ -965,6 +960,31 @@ done:
 
 /*-------------------------------------------------------------------------*/
 
+static void enable_async(struct ehci_hcd *ehci)
+{
+       if (ehci->async_count++)
+               return;
+
+       /* Stop waiting to turn off the async schedule */
+       ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_ASYNC);
+
+       /* Don't start the schedule until ASS is 0 */
+       ehci_poll_ASS(ehci);
+       turn_on_io_watchdog(ehci);
+}
+
+static void disable_async(struct ehci_hcd *ehci)
+{
+       if (--ehci->async_count)
+               return;
+
+       /* The async schedule and async_unlink list are supposed to be empty */
+       WARN_ON(ehci->async->qh_next.qh || ehci->async_unlink);
+
+       /* Don't turn off the schedule until ASS is 1 */
+       ehci_poll_ASS(ehci);
+}
+
 /* move qh (and its qtds) onto async queue; maybe enable queue.  */
 
 static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
@@ -978,24 +998,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
        WARN_ON(qh->qh_state != QH_STATE_IDLE);
 
-       /* (re)start the async schedule? */
-       head = ehci->async;
-       timer_action_done (ehci, TIMER_ASYNC_OFF);
-       if (!head->qh_next.qh) {
-               if (!(ehci->command & CMD_ASE)) {
-                       /* in case a clear of CMD_ASE didn't take yet */
-                       (void)handshake(ehci, &ehci->regs->status,
-                                       STS_ASS, 0, 150);
-                       ehci->command |= CMD_ASE;
-                       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-                       /* posted write need not be known to HC yet ... */
-               }
-       }
-
        /* clear halt and/or toggle; and maybe recover from silicon quirk */
        qh_refresh(ehci, qh);
 
        /* splice right after start */
+       head = ehci->async;
        qh->qh_next = head->qh_next;
        qh->hw->hw_next = head->hw->hw_next;
        wmb ();
@@ -1003,10 +1010,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
        head->qh_next.qh = qh;
        head->hw->hw_next = dma;
 
-       qh_get(qh);
        qh->xacterrs = 0;
        qh->qh_state = QH_STATE_LINKED;
        /* qtd completions reported later by interrupt */
+
+       enable_async(ehci);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1090,7 +1098,7 @@ static struct ehci_qh *qh_append_tds (
                        wmb ();
                        dummy->hw_token = token;
 
-                       urb->hcpriv = qh_get (qh);
+                       urb->hcpriv = qh;
                }
        }
        return qh;
@@ -1155,117 +1163,155 @@ submit_async (
 
 /*-------------------------------------------------------------------------*/
 
-/* the async qh for the qtds being reclaimed are now unlinked from the HC */
-
-static void end_unlink_async (struct ehci_hcd *ehci)
+static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       struct ehci_qh          *qh = ehci->reclaim;
-       struct ehci_qh          *next;
+       struct ehci_qh          *prev;
 
-       iaa_watchdog_done(ehci);
+       /* Add to the end of the list of QHs waiting for the next IAAD */
+       qh->qh_state = QH_STATE_UNLINK;
+       if (ehci->async_unlink)
+               ehci->async_unlink_last->unlink_next = qh;
+       else
+               ehci->async_unlink = qh;
+       ehci->async_unlink_last = qh;
 
-       // qh->hw_next = cpu_to_hc32(qh->qh_dma);
-       qh->qh_state = QH_STATE_IDLE;
-       qh->qh_next.qh = NULL;
-       qh_put (qh);                    // refcount from reclaim
+       /* Unlink it from the schedule */
+       prev = ehci->async;
+       while (prev->qh_next.qh != qh)
+               prev = prev->qh_next.qh;
 
-       /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
-       next = qh->reclaim;
-       ehci->reclaim = next;
-       qh->reclaim = NULL;
+       prev->hw->hw_next = qh->hw->hw_next;
+       prev->qh_next = qh->qh_next;
+       if (ehci->qh_scan_next == qh)
+               ehci->qh_scan_next = qh->qh_next.qh;
+}
 
-       qh_completions (ehci, qh);
+static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
+{
+       /*
+        * Do nothing if an IAA cycle is already running or
+        * if one will be started shortly.
+        */
+       if (ehci->async_iaa || ehci->async_unlinking)
+               return;
 
-       if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) {
-               qh_link_async (ehci, qh);
-       } else {
-               /* it's not free to turn the async schedule on/off; leave it
-                * active but idle for a while once it empties.
-                */
-               if (ehci->rh_state == EHCI_RH_RUNNING
-                               && ehci->async->qh_next.qh == NULL)
-                       timer_action (ehci, TIMER_ASYNC_OFF);
-       }
-       qh_put(qh);                     /* refcount from async list */
+       /* Do all the waiting QHs at once */
+       ehci->async_iaa = ehci->async_unlink;
+       ehci->async_unlink = NULL;
 
-       if (next) {
-               ehci->reclaim = NULL;
-               start_unlink_async (ehci, next);
+       /* If the controller isn't running, we don't have to wait for it */
+       if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
+               if (!nested)            /* Avoid recursion */
+                       end_unlink_async(ehci);
+
+       /* Otherwise start a new IAA cycle */
+       } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
+               /* Make sure the unlinks are all visible to the hardware */
+               wmb();
+
+               ehci_writel(ehci, ehci->command | CMD_IAAD,
+                               &ehci->regs->command);
+               ehci_readl(ehci, &ehci->regs->command);
+               ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true);
        }
+}
+
+/* the async qh for the qtds being unlinked are now gone from the HC */
+
+static void end_unlink_async(struct ehci_hcd *ehci)
+{
+       struct ehci_qh          *qh;
 
        if (ehci->has_synopsys_hc_bug)
                ehci_writel(ehci, (u32) ehci->async->qh_dma,
                            &ehci->regs->async_next);
-}
-
-/* makes sure the async qh will become idle */
-/* caller must own ehci->lock */
 
-static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
-{
-       struct ehci_qh  *prev;
+       /* Process the idle QHs */
+ restart:
+       ehci->async_unlinking = true;
+       while (ehci->async_iaa) {
+               qh = ehci->async_iaa;
+               ehci->async_iaa = qh->unlink_next;
+               qh->unlink_next = NULL;
+
+               qh->qh_state = QH_STATE_IDLE;
+               qh->qh_next.qh = NULL;
+
+               qh_completions(ehci, qh);
+               if (!list_empty(&qh->qtd_list) &&
+                               ehci->rh_state == EHCI_RH_RUNNING)
+                       qh_link_async(ehci, qh);
+               disable_async(ehci);
+       }
+       ehci->async_unlinking = false;
 
-#ifdef DEBUG
-       assert_spin_locked(&ehci->lock);
-       if (ehci->reclaim
-                       || (qh->qh_state != QH_STATE_LINKED
-                               && qh->qh_state != QH_STATE_UNLINK_WAIT)
-                       )
-               BUG ();
-#endif
+       /* Start a new IAA cycle if any QHs are waiting for it */
+       if (ehci->async_unlink) {
+               start_iaa_cycle(ehci, true);
+               if (unlikely(ehci->rh_state < EHCI_RH_RUNNING))
+                       goto restart;
+       }
+}
 
-       /* stop async schedule right now? */
-       if (unlikely (qh == ehci->async)) {
-               /* can't get here without STS_ASS set */
-               if (ehci->rh_state != EHCI_RH_HALTED
-                               && !ehci->reclaim) {
-                       /* ... and CMD_IAAD clear */
-                       ehci->command &= ~CMD_ASE;
-                       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-                       wmb ();
-                       // handshake later, if we need to
-                       timer_action_done (ehci, TIMER_ASYNC_OFF);
+static void unlink_empty_async(struct ehci_hcd *ehci)
+{
+       struct ehci_qh          *qh, *next;
+       bool                    stopped = (ehci->rh_state < EHCI_RH_RUNNING);
+       bool                    check_unlinks_later = false;
+
+       /* Unlink all the async QHs that have been empty for a timer cycle */
+       next = ehci->async->qh_next.qh;
+       while (next) {
+               qh = next;
+               next = qh->qh_next.qh;
+
+               if (list_empty(&qh->qtd_list) &&
+                               qh->qh_state == QH_STATE_LINKED) {
+                       if (!stopped && qh->unlink_cycle ==
+                                       ehci->async_unlink_cycle)
+                               check_unlinks_later = true;
+                       else
+                               single_unlink_async(ehci, qh);
                }
-               return;
        }
 
-       qh->qh_state = QH_STATE_UNLINK;
-       ehci->reclaim = qh = qh_get (qh);
+       /* Start a new IAA cycle if any QHs are waiting for it */
+       if (ehci->async_unlink)
+               start_iaa_cycle(ehci, false);
 
-       prev = ehci->async;
-       while (prev->qh_next.qh != qh)
-               prev = prev->qh_next.qh;
+       /* QHs that haven't been empty for long enough will be handled later */
+       if (check_unlinks_later) {
+               ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
+               ++ehci->async_unlink_cycle;
+       }
+}
 
-       prev->hw->hw_next = qh->hw->hw_next;
-       prev->qh_next = qh->qh_next;
-       if (ehci->qh_scan_next == qh)
-               ehci->qh_scan_next = qh->qh_next.qh;
-       wmb ();
+/* makes sure the async qh will become idle */
+/* caller must own ehci->lock */
 
-       /* If the controller isn't running, we don't have to wait for it */
-       if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) {
-               /* if (unlikely (qh->reclaim != 0))
-                *      this will recurse, probably not much
-                */
-               end_unlink_async (ehci);
+static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+       /*
+        * If the QH isn't linked then there's nothing we can do
+        * unless we were called during a giveback, in which case
+        * qh_completions() has to deal with it.
+        */
+       if (qh->qh_state != QH_STATE_LINKED) {
+               if (qh->qh_state == QH_STATE_COMPLETING)
+                       qh->needs_rescan = 1;
                return;
        }
 
-       ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command);
-       (void)ehci_readl(ehci, &ehci->regs->command);
-       iaa_watchdog_start(ehci);
+       single_unlink_async(ehci, qh);
+       start_iaa_cycle(ehci, false);
 }
 
 /*-------------------------------------------------------------------------*/
 
 static void scan_async (struct ehci_hcd *ehci)
 {
-       bool                    stopped;
        struct ehci_qh          *qh;
-       enum ehci_timer_action  action = TIMER_IO_WATCHDOG;
-
-       timer_action_done (ehci, TIMER_ASYNC_SHRINK);
-       stopped = (ehci->rh_state != EHCI_RH_RUNNING);
+       bool                    check_unlinks_later = false;
 
        ehci->qh_scan_next = ehci->async->qh_next.qh;
        while (ehci->qh_scan_next) {
@@ -1281,33 +1327,30 @@ static void scan_async (struct ehci_hcd *ehci)
                         * drops the lock.  That's why ehci->qh_scan_next
                         * always holds the next qh to scan; if the next qh
                         * gets unlinked then ehci->qh_scan_next is adjusted
-                        * in start_unlink_async().
+                        * in single_unlink_async().
                         */
-                       qh = qh_get(qh);
                        temp = qh_completions(ehci, qh);
-                       if (qh->needs_rescan)
-                               unlink_async(ehci, qh);
-                       qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES;
-                       qh_put(qh);
-                       if (temp != 0)
+                       if (qh->needs_rescan) {
+                               start_unlink_async(ehci, qh);
+                       } else if (list_empty(&qh->qtd_list)
+                                       && qh->qh_state == QH_STATE_LINKED) {
+                               qh->unlink_cycle = ehci->async_unlink_cycle;
+                               check_unlinks_later = true;
+                       } else if (temp != 0)
                                goto rescan;
                }
+       }
 
-               /* unlink idle entries, reducing DMA usage as well
-                * as HCD schedule-scanning costs.  delay for any qh
-                * we just scanned, there's a not-unusual case that it
-                * doesn't stay idle for long.
-                * (plus, avoids some kind of re-activation race.)
-                */
-               if (list_empty(&qh->qtd_list)
-                               && qh->qh_state == QH_STATE_LINKED) {
-                       if (!ehci->reclaim && (stopped ||
-                                       time_after_eq(jiffies, qh->unlink_time)))
-                               start_unlink_async(ehci, qh);
-                       else
-                               action = TIMER_ASYNC_SHRINK;
-               }
+       /*
+        * Unlink empty entries, reducing DMA usage as well
+        * as HCD schedule-scanning costs.  Delay for any qh
+        * we just scanned, there's a not-unusual case that it
+        * doesn't stay idle for long.
+        */
+       if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING &&
+                       !(ehci->enabled_hrtimer_events &
+                               BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) {
+               ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
+               ++ehci->async_unlink_cycle;
        }
-       if (action == TIMER_ASYNC_SHRINK)
-               timer_action (ehci, TIMER_ASYNC_SHRINK);
 }
index c474cec..9d8f1dd 100644 (file)
@@ -13,7 +13,9 @@
  */
 
 #include <linux/clk.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/of_gpio.h>
 #include <plat/ehci.h>
 #include <plat/usb-phy.h>
 
@@ -40,7 +42,7 @@ static const struct hc_driver s5p_ehci_hc_driver = {
        .irq                    = ehci_irq,
        .flags                  = HCD_MEMORY | HCD_USB2,
 
-       .reset                  = ehci_init,
+       .reset                  = ehci_setup,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
        .shutdown               = ehci_shutdown,
@@ -63,6 +65,26 @@ static const struct hc_driver s5p_ehci_hc_driver = {
        .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
 };
 
+static void s5p_setup_vbus_gpio(struct platform_device *pdev)
+{
+       int err;
+       int gpio;
+
+       if (!pdev->dev.of_node)
+               return;
+
+       gpio = of_get_named_gpio(pdev->dev.of_node,
+                       "samsung,vbus-gpio", 0);
+       if (!gpio_is_valid(gpio))
+               return;
+
+       err = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "ehci_vbus_gpio");
+       if (err)
+               dev_err(&pdev->dev, "can't request ehci vbus gpio %d", gpio);
+}
+
+static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32);
+
 static int __devinit s5p_ehci_probe(struct platform_device *pdev)
 {
        struct s5p_ehci_platdata *pdata;
@@ -79,7 +101,20 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL);
+       /*
+        * Right now device-tree probed devices don't get dma_mask set.
+        * Since shared usb code relies on it, set it here for now.
+        * Once we move to full device tree support this will vanish off.
+        */
+       if (!pdev->dev.dma_mask)
+               pdev->dev.dma_mask = &ehci_s5p_dma_mask;
+       if (!pdev->dev.coherent_dma_mask)
+               pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+       s5p_setup_vbus_gpio(pdev);
+
+       s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd),
+                               GFP_KERNEL);
        if (!s5p_ehci)
                return -ENOMEM;
 
@@ -89,8 +124,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
                                        dev_name(&pdev->dev));
        if (!hcd) {
                dev_err(&pdev->dev, "Unable to create HCD\n");
-               err = -ENOMEM;
-               goto fail_hcd;
+               return -ENOMEM;
        }
 
        s5p_ehci->hcd = hcd;
@@ -115,7 +149,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
 
        hcd->rsrc_start = res->start;
        hcd->rsrc_len = resource_size(res);
-       hcd->regs = ioremap(res->start, resource_size(res));
+       hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
        if (!hcd->regs) {
                dev_err(&pdev->dev, "Failed to remap I/O memory\n");
                err = -ENOMEM;
@@ -126,7 +160,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
        if (!irq) {
                dev_err(&pdev->dev, "Failed to get IRQ\n");
                err = -ENODEV;
-               goto fail;
+               goto fail_io;
        }
 
        if (pdata->phy_init)
@@ -134,40 +168,26 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
 
        ehci = hcd_to_ehci(hcd);
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
 
        /* DMA burst Enable */
        writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
 
-       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);
-
-       ehci_reset(ehci);
-
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err) {
                dev_err(&pdev->dev, "Failed to add USB HCD\n");
-               goto fail;
+               goto fail_io;
        }
 
        platform_set_drvdata(pdev, s5p_ehci);
 
        return 0;
 
-fail:
-       iounmap(hcd->regs);
 fail_io:
        clk_disable(s5p_ehci->clk);
 fail_clken:
        clk_put(s5p_ehci->clk);
 fail_clk:
        usb_put_hcd(hcd);
-fail_hcd:
-       kfree(s5p_ehci);
        return err;
 }
 
@@ -182,13 +202,10 @@ static int __devexit s5p_ehci_remove(struct platform_device *pdev)
        if (pdata && pdata->phy_exit)
                pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
 
-       iounmap(hcd->regs);
-
        clk_disable(s5p_ehci->clk);
        clk_put(s5p_ehci->clk);
 
        usb_put_hcd(hcd);
-       kfree(s5p_ehci);
 
        return 0;
 }
@@ -207,27 +224,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;
-       struct ehci_hcd *ehci = hcd_to_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;
-       unsigned long flags;
-       int rc = 0;
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(20);
-
-       /*
-        * Root hub was already suspended. Disable irq emission and
-        * mark HW unaccessible.  The PM and USB cores make sure that
-        * the root hub is either suspended or stopped.
-        */
-       ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
-       spin_lock_irqsave(&ehci->lock, flags);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       (void)ehci_readl(ehci, &ehci->regs->intr_enable);
+       int rc;
 
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       spin_unlock_irqrestore(&ehci->lock, flags);
+       rc = ehci_suspend(hcd, do_wakeup);
 
        if (pdata && pdata->phy_exit)
                pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
@@ -241,7 +243,6 @@ 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 ehci_hcd *ehci = hcd_to_ehci(hcd);
        struct platform_device *pdev = to_platform_device(dev);
        struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
 
@@ -253,44 +254,7 @@ static int s5p_ehci_resume(struct device *dev)
        /* DMA burst Enable */
        writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
 
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       /* Mark hardware accessible again as we are out of D3 state by now */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
-               int     mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       (void) ehci_halt(ehci);
-       (void) ehci_reset(ehci);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
-
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
-
-       ehci->rh_state = EHCI_RH_SUSPENDED;
-
+       ehci_resume(hcd, false);
        return 0;
 }
 #else
@@ -303,6 +267,14 @@ static const struct dev_pm_ops s5p_ehci_pm_ops = {
        .resume         = s5p_ehci_resume,
 };
 
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_ehci_match[] = {
+       { .compatible = "samsung,exynos-ehci" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_ehci_match);
+#endif
+
 static struct platform_driver s5p_ehci_driver = {
        .probe          = s5p_ehci_probe,
        .remove         = __devexit_p(s5p_ehci_remove),
@@ -311,6 +283,7 @@ static struct platform_driver s5p_ehci_driver = {
                .name   = "s5p-ehci",
                .owner  = THIS_MODULE,
                .pm     = &s5p_ehci_pm_ops,
+               .of_match_table = of_match_ptr(exynos_ehci_match),
        }
 };
 
index 33182c6..7cf3da7 100644 (file)
@@ -479,70 +479,26 @@ static int tt_no_collision (
 
 /*-------------------------------------------------------------------------*/
 
-static int enable_periodic (struct ehci_hcd *ehci)
+static void enable_periodic(struct ehci_hcd *ehci)
 {
-       int     status;
-
-       if (ehci->periodic_sched++)
-               return 0;
-
-       /* did clearing PSE did take effect yet?
-        * takes effect only at frame boundaries...
-        */
-       status = handshake_on_error_set_halt(ehci, &ehci->regs->status,
-                                            STS_PSS, 0, 9 * 125);
-       if (status) {
-               usb_hc_died(ehci_to_hcd(ehci));
-               return status;
-       }
+       if (ehci->periodic_count++)
+               return;
 
-       ehci->command |= CMD_PSE;
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       /* posted write ... PSS happens later */
+       /* Stop waiting to turn off the periodic schedule */
+       ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC);
 
-       /* make sure ehci_work scans these */
-       ehci->next_uframe = ehci_read_frame_index(ehci)
-               % (ehci->periodic_size << 3);
-       if (unlikely(ehci->broken_periodic))
-               ehci->last_periodic_enable = ktime_get_real();
-       return 0;
+       /* Don't start the schedule until PSS is 0 */
+       ehci_poll_PSS(ehci);
+       turn_on_io_watchdog(ehci);
 }
 
-static int disable_periodic (struct ehci_hcd *ehci)
+static void disable_periodic(struct ehci_hcd *ehci)
 {
-       int     status;
-
-       if (--ehci->periodic_sched)
-               return 0;
-
-       if (unlikely(ehci->broken_periodic)) {
-               /* delay experimentally determined */
-               ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000);
-               ktime_t now = ktime_get_real();
-               s64 delay = ktime_us_delta(safe, now);
-
-               if (unlikely(delay > 0))
-                       udelay(delay);
-       }
-
-       /* did setting PSE not take effect yet?
-        * takes effect only at frame boundaries...
-        */
-       status = handshake_on_error_set_halt(ehci, &ehci->regs->status,
-                                            STS_PSS, STS_PSS, 9 * 125);
-       if (status) {
-               usb_hc_died(ehci_to_hcd(ehci));
-               return status;
-       }
-
-       ehci->command &= ~CMD_PSE;
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       /* posted write ... */
-
-       free_cached_lists(ehci);
+       if (--ehci->periodic_count)
+               return;
 
-       ehci->next_uframe = -1;
-       return 0;
+       /* Don't turn off the schedule until PSS is 1 */
+       ehci_poll_PSS(ehci);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -553,7 +509,7 @@ static int disable_periodic (struct ehci_hcd *ehci)
  * this just links in a qh; caller guarantees uframe masks are set right.
  * no FSTN support (yet; ehci 0.96+)
  */
-static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
+static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
        unsigned        i;
        unsigned        period = qh->period;
@@ -606,28 +562,38 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
        }
        qh->qh_state = QH_STATE_LINKED;
        qh->xacterrs = 0;
-       qh_get (qh);
 
        /* update per-qh bandwidth for usbfs */
        ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period
                ? ((qh->usecs + qh->c_usecs) / qh->period)
                : (qh->usecs * 8);
 
+       list_add(&qh->intr_node, &ehci->intr_qh_list);
+
        /* maybe enable periodic schedule processing */
-       return enable_periodic(ehci);
+       ++ehci->intr_count;
+       enable_periodic(ehci);
 }
 
-static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
+static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
        unsigned        i;
        unsigned        period;
 
-       // FIXME:
-       // IF this isn't high speed
-       //   and this qh is active in the current uframe
-       //   (and overlay token SplitXstate is false?)
-       // THEN
-       //   qh->hw_info1 |= cpu_to_hc32(1 << 7 /* "ignore" */);
+       /*
+        * If qh is for a low/full-speed device, simply unlinking it
+        * could interfere with an ongoing split transaction.  To unlink
+        * it safely would require setting the QH_INACTIVATE bit and
+        * waiting at least one frame, as described in EHCI 4.12.2.5.
+        *
+        * We won't bother with any of this.  Instead, we assume that the
+        * only reason for unlinking an interrupt QH while the current URB
+        * is still active is to dequeue all the URBs (flush the whole
+        * endpoint queue).
+        *
+        * If rebalancing the periodic schedule is ever implemented, this
+        * approach will no longer be valid.
+        */
 
        /* high bandwidth, or otherwise part of every microframe */
        if ((period = qh->period) == 0)
@@ -650,18 +616,15 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
        /* qh->qh_next still "live" to HC */
        qh->qh_state = QH_STATE_UNLINK;
        qh->qh_next.ptr = NULL;
-       qh_put (qh);
 
-       /* maybe turn off periodic schedule */
-       return disable_periodic(ehci);
+       if (ehci->qh_scan_next == qh)
+               ehci->qh_scan_next = list_entry(qh->intr_node.next,
+                               struct ehci_qh, intr_node);
+       list_del(&qh->intr_node);
 }
 
-static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
+static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       unsigned                wait;
-       struct ehci_qh_hw       *hw = qh->hw;
-       int                     rc;
-
        /* If the QH isn't linked then there's nothing we can do
         * unless we were called during a giveback, in which case
         * qh_completions() has to deal with it.
@@ -674,28 +637,45 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
        qh_unlink_periodic (ehci, qh);
 
-       /* simple/paranoid:  always delay, expecting the HC needs to read
-        * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and
-        * expect khubd to clean up after any CSPLITs we won't issue.
-        * active high speed queues may need bigger delays...
+       /* Make sure the unlinks are visible before starting the timer */
+       wmb();
+
+       /*
+        * The EHCI spec doesn't say how long it takes the controller to
+        * stop accessing an unlinked interrupt QH.  The timer delay is
+        * 9 uframes; presumably that will be long enough.
         */
-       if (list_empty (&qh->qtd_list)
-                       || (cpu_to_hc32(ehci, QH_CMASK)
-                                       & hw->hw_info2) != 0)
-               wait = 2;
+       qh->unlink_cycle = ehci->intr_unlink_cycle;
+
+       /* New entries go at the end of the intr_unlink list */
+       if (ehci->intr_unlink)
+               ehci->intr_unlink_last->unlink_next = qh;
        else
-               wait = 55;      /* worst case: 3 * 1024 */
+               ehci->intr_unlink = qh;
+       ehci->intr_unlink_last = qh;
+
+       if (ehci->intr_unlinking)
+               ;       /* Avoid recursive calls */
+       else if (ehci->rh_state < EHCI_RH_RUNNING)
+               ehci_handle_intr_unlinks(ehci);
+       else if (ehci->intr_unlink == qh) {
+               ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true);
+               ++ehci->intr_unlink_cycle;
+       }
+}
+
+static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+       struct ehci_qh_hw       *hw = qh->hw;
+       int                     rc;
 
-       udelay (wait);
        qh->qh_state = QH_STATE_IDLE;
        hw->hw_next = EHCI_LIST_END(ehci);
-       wmb ();
 
        qh_completions(ehci, qh);
 
        /* reschedule QH iff another request is queued */
-       if (!list_empty(&qh->qtd_list) &&
-                       ehci->rh_state == EHCI_RH_RUNNING) {
+       if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) {
                rc = qh_schedule(ehci, qh);
 
                /* An error here likely indicates handshake failure
@@ -708,6 +688,10 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
                        ehci_err(ehci, "can't reschedule qh %p, err %d\n",
                                        qh, rc);
        }
+
+       /* maybe turn off periodic schedule */
+       --ehci->intr_count;
+       disable_periodic(ehci);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -884,7 +868,7 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
                ehci_dbg (ehci, "reused qh %p schedule\n", qh);
 
        /* stuff into the periodic schedule */
-       status = qh_link_periodic (ehci, qh);
+       qh_link_periodic(ehci, qh);
 done:
        return status;
 }
@@ -944,6 +928,35 @@ done_not_linked:
        return status;
 }
 
+static void scan_intr(struct ehci_hcd *ehci)
+{
+       struct ehci_qh          *qh;
+
+       list_for_each_entry_safe(qh, ehci->qh_scan_next, &ehci->intr_qh_list,
+                       intr_node) {
+ rescan:
+               /* clean any finished work for this qh */
+               if (!list_empty(&qh->qtd_list)) {
+                       int temp;
+
+                       /*
+                        * Unlinks could happen here; completion reporting
+                        * drops the lock.  That's why ehci->qh_scan_next
+                        * always holds the next qh to scan; if the next qh
+                        * gets unlinked then ehci->qh_scan_next is adjusted
+                        * in qh_unlink_periodic().
+                        */
+                       temp = qh_completions(ehci, qh);
+                       if (unlikely(qh->needs_rescan ||
+                                       (list_empty(&qh->qtd_list) &&
+                                               qh->qh_state == QH_STATE_LINKED)))
+                               start_unlink_intr(ehci, qh);
+                       else if (temp != 0)
+                               goto rescan;
+               }
+       }
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* ehci_iso_stream ops work with both ITD and SITD */
@@ -958,7 +971,6 @@ iso_stream_alloc (gfp_t mem_flags)
                INIT_LIST_HEAD(&stream->td_list);
                INIT_LIST_HEAD(&stream->free_list);
                stream->next_uframe = -1;
-               stream->refcount = 1;
        }
        return stream;
 }
@@ -1058,57 +1070,6 @@ iso_stream_init (
        stream->maxp = maxp;
 }
 
-static void
-iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
-{
-       stream->refcount--;
-
-       /* free whenever just a dev->ep reference remains.
-        * not like a QH -- no persistent state (toggle, halt)
-        */
-       if (stream->refcount == 1) {
-               // BUG_ON (!list_empty(&stream->td_list));
-
-               while (!list_empty (&stream->free_list)) {
-                       struct list_head        *entry;
-
-                       entry = stream->free_list.next;
-                       list_del (entry);
-
-                       /* knows about ITD vs SITD */
-                       if (stream->highspeed) {
-                               struct ehci_itd         *itd;
-
-                               itd = list_entry (entry, struct ehci_itd,
-                                               itd_list);
-                               dma_pool_free (ehci->itd_pool, itd,
-                                               itd->itd_dma);
-                       } else {
-                               struct ehci_sitd        *sitd;
-
-                               sitd = list_entry (entry, struct ehci_sitd,
-                                               sitd_list);
-                               dma_pool_free (ehci->sitd_pool, sitd,
-                                               sitd->sitd_dma);
-                       }
-               }
-
-               stream->bEndpointAddress &= 0x0f;
-               if (stream->ep)
-                       stream->ep->hcpriv = NULL;
-
-               kfree(stream);
-       }
-}
-
-static inline struct ehci_iso_stream *
-iso_stream_get (struct ehci_iso_stream *stream)
-{
-       if (likely (stream != NULL))
-               stream->refcount++;
-       return stream;
-}
-
 static struct ehci_iso_stream *
 iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
 {
@@ -1129,7 +1090,6 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
        if (unlikely (stream == NULL)) {
                stream = iso_stream_alloc(GFP_ATOMIC);
                if (likely (stream != NULL)) {
-                       /* dev->ep owns the initial refcount */
                        ep->hcpriv = stream;
                        stream->ep = ep;
                        iso_stream_init(ehci, stream, urb->dev, urb->pipe,
@@ -1144,9 +1104,6 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
                stream = NULL;
        }
 
-       /* caller guarantees an eventual matching iso_stream_put */
-       stream = iso_stream_get (stream);
-
        spin_unlock_irqrestore (&ehci->lock, flags);
        return stream;
 }
@@ -1254,17 +1211,19 @@ itd_urb_transaction (
        spin_lock_irqsave (&ehci->lock, flags);
        for (i = 0; i < num_itds; i++) {
 
-               /* free_list.next might be cache-hot ... but maybe
-                * the HC caches it too. avoid that issue for now.
+               /*
+                * Use iTDs from the free list, but not iTDs that may
+                * still be in use by the hardware.
                 */
-
-               /* prefer previously-allocated itds */
-               if (likely (!list_empty(&stream->free_list))) {
-                       itd = list_entry (stream->free_list.prev,
+               if (likely(!list_empty(&stream->free_list))) {
+                       itd = list_first_entry(&stream->free_list,
                                        struct ehci_itd, itd_list);
+                       if (itd->frame == ehci->now_frame)
+                               goto alloc_itd;
                        list_del (&itd->itd_list);
                        itd_dma = itd->itd_dma;
                } else {
+ alloc_itd:
                        spin_unlock_irqrestore (&ehci->lock, flags);
                        itd = dma_pool_alloc (ehci->itd_pool, mem_flags,
                                        &itd_dma);
@@ -1528,6 +1487,10 @@ iso_stream_schedule (
        urb->start_frame = stream->next_uframe;
        if (!stream->highspeed)
                urb->start_frame >>= 3;
+
+       /* Make sure scan_isoc() sees these */
+       if (ehci->isoc_count == 0)
+               ehci->next_frame = now >> 3;
        return 0;
 
  fail:
@@ -1615,8 +1578,7 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
 }
 
 /* fit urb's itds into the selected schedule slot; activate as needed */
-static int
-itd_link_urb (
+static void itd_link_urb(
        struct ehci_hcd         *ehci,
        struct urb              *urb,
        unsigned                mod,
@@ -1659,7 +1621,7 @@ itd_link_urb (
                        itd = list_entry (iso_sched->td_list.next,
                                        struct ehci_itd, itd_list);
                        list_move_tail (&itd->itd_list, &stream->td_list);
-                       itd->stream = iso_stream_get (stream);
+                       itd->stream = stream;
                        itd->urb = urb;
                        itd_init (ehci, stream, itd);
                }
@@ -1686,8 +1648,8 @@ itd_link_urb (
        iso_sched_free (stream, iso_sched);
        urb->hcpriv = NULL;
 
-       timer_action (ehci, TIMER_IO_WATCHDOG);
-       return enable_periodic(ehci);
+       ++ehci->isoc_count;
+       enable_periodic(ehci);
 }
 
 #define        ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
@@ -1702,11 +1664,8 @@ itd_link_urb (
  * (b) only this endpoint's completions submit URBs.  It seems some silicon
  * corrupts things if you reuse completed descriptors very quickly...
  */
-static unsigned
-itd_complete (
-       struct ehci_hcd *ehci,
-       struct ehci_itd *itd
-) {
+static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd)
+{
        struct urb                              *urb = itd->urb;
        struct usb_iso_packet_descriptor        *desc;
        u32                                     t;
@@ -1714,7 +1673,7 @@ itd_complete (
        int                                     urb_index = -1;
        struct ehci_iso_stream                  *stream = itd->stream;
        struct usb_device                       *dev;
-       unsigned                                retval = false;
+       bool                                    retval = false;
 
        /* for each uframe with a packet */
        for (uframe = 0; uframe < 8; uframe++) {
@@ -1767,9 +1726,11 @@ itd_complete (
        ehci_urb_done(ehci, urb, 0);
        retval = true;
        urb = NULL;
-       (void) disable_periodic(ehci);
-       ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
 
+       --ehci->isoc_count;
+       disable_periodic(ehci);
+
+       ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
        if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
                if (ehci->amd_pll_fix == 1)
                        usb_amd_quirk_pll_enable();
@@ -1783,28 +1744,20 @@ itd_complete (
                        dev->devpath, stream->bEndpointAddress & 0x0f,
                        (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
        }
-       iso_stream_put (ehci, stream);
 
 done:
        itd->urb = NULL;
-       if (ehci->clock_frame != itd->frame || itd->index[7] != -1) {
-               /* OK to recycle this ITD now. */
-               itd->stream = NULL;
-               list_move(&itd->itd_list, &stream->free_list);
-               iso_stream_put(ehci, stream);
-       } else {
-               /* HW might remember this ITD, so we can't recycle it yet.
-                * Move it to a safe place until a new frame starts.
-                */
-               list_move(&itd->itd_list, &ehci->cached_itd_list);
-               if (stream->refcount == 2) {
-                       /* If iso_stream_put() were called here, stream
-                        * would be freed.  Instead, just prevent reuse.
-                        */
-                       stream->ep->hcpriv = NULL;
-                       stream->ep = NULL;
-               }
+
+       /* Add to the end of the free list for later reuse */
+       list_move_tail(&itd->itd_list, &stream->free_list);
+
+       /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */
+       if (list_empty(&stream->td_list)) {
+               list_splice_tail_init(&stream->free_list,
+                               &ehci->cached_itd_list);
+               start_free_itds(ehci);
        }
+
        return retval;
 }
 
@@ -1861,12 +1814,9 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
                itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
        else
                usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
-done_not_linked:
+ done_not_linked:
        spin_unlock_irqrestore (&ehci->lock, flags);
-
-done:
-       if (unlikely (status < 0))
-               iso_stream_put (ehci, stream);
+ done:
        return status;
 }
 
@@ -1955,17 +1905,19 @@ sitd_urb_transaction (
                 * means we never need two sitds for full speed packets.
                 */
 
-               /* free_list.next might be cache-hot ... but maybe
-                * the HC caches it too. avoid that issue for now.
+               /*
+                * Use siTDs from the free list, but not siTDs that may
+                * still be in use by the hardware.
                 */
-
-               /* prefer previously-allocated sitds */
-               if (!list_empty(&stream->free_list)) {
-                       sitd = list_entry (stream->free_list.prev,
+               if (likely(!list_empty(&stream->free_list))) {
+                       sitd = list_first_entry(&stream->free_list,
                                         struct ehci_sitd, sitd_list);
+                       if (sitd->frame == ehci->now_frame)
+                               goto alloc_sitd;
                        list_del (&sitd->sitd_list);
                        sitd_dma = sitd->sitd_dma;
                } else {
+ alloc_sitd:
                        spin_unlock_irqrestore (&ehci->lock, flags);
                        sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags,
                                        &sitd_dma);
@@ -2034,8 +1986,7 @@ sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
 }
 
 /* fit urb's sitds into the selected schedule slot; activate as needed */
-static int
-sitd_link_urb (
+static void sitd_link_urb(
        struct ehci_hcd         *ehci,
        struct urb              *urb,
        unsigned                mod,
@@ -2081,7 +2032,7 @@ sitd_link_urb (
                sitd = list_entry (sched->td_list.next,
                                struct ehci_sitd, sitd_list);
                list_move_tail (&sitd->sitd_list, &stream->td_list);
-               sitd->stream = iso_stream_get (stream);
+               sitd->stream = stream;
                sitd->urb = urb;
 
                sitd_patch(ehci, stream, sitd, sched, packet);
@@ -2096,8 +2047,8 @@ sitd_link_urb (
        iso_sched_free (stream, sched);
        urb->hcpriv = NULL;
 
-       timer_action (ehci, TIMER_IO_WATCHDOG);
-       return enable_periodic(ehci);
+       ++ehci->isoc_count;
+       enable_periodic(ehci);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2115,18 +2066,15 @@ sitd_link_urb (
  * (b) only this endpoint's completions submit URBs.  It seems some silicon
  * corrupts things if you reuse completed descriptors very quickly...
  */
-static unsigned
-sitd_complete (
-       struct ehci_hcd         *ehci,
-       struct ehci_sitd        *sitd
-) {
+static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd)
+{
        struct urb                              *urb = sitd->urb;
        struct usb_iso_packet_descriptor        *desc;
        u32                                     t;
        int                                     urb_index = -1;
        struct ehci_iso_stream                  *stream = sitd->stream;
        struct usb_device                       *dev;
-       unsigned                                retval = false;
+       bool                                    retval = false;
 
        urb_index = sitd->index;
        desc = &urb->iso_frame_desc [urb_index];
@@ -2163,9 +2111,11 @@ sitd_complete (
        ehci_urb_done(ehci, urb, 0);
        retval = true;
        urb = NULL;
-       (void) disable_periodic(ehci);
-       ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
 
+       --ehci->isoc_count;
+       disable_periodic(ehci);
+
+       ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
        if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
                if (ehci->amd_pll_fix == 1)
                        usb_amd_quirk_pll_enable();
@@ -2179,28 +2129,20 @@ sitd_complete (
                        dev->devpath, stream->bEndpointAddress & 0x0f,
                        (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
        }
-       iso_stream_put (ehci, stream);
 
 done:
        sitd->urb = NULL;
-       if (ehci->clock_frame != sitd->frame) {
-               /* OK to recycle this SITD now. */
-               sitd->stream = NULL;
-               list_move(&sitd->sitd_list, &stream->free_list);
-               iso_stream_put(ehci, stream);
-       } else {
-               /* HW might remember this SITD, so we can't recycle it yet.
-                * Move it to a safe place until a new frame starts.
-                */
-               list_move(&sitd->sitd_list, &ehci->cached_sitd_list);
-               if (stream->refcount == 2) {
-                       /* If iso_stream_put() were called here, stream
-                        * would be freed.  Instead, just prevent reuse.
-                        */
-                       stream->ep->hcpriv = NULL;
-                       stream->ep = NULL;
-               }
+
+       /* Add to the end of the free list for later reuse */
+       list_move_tail(&sitd->sitd_list, &stream->free_list);
+
+       /* Recycle the siTDs when the pipeline is empty (ep no longer in use) */
+       if (list_empty(&stream->td_list)) {
+               list_splice_tail_init(&stream->free_list,
+                               &ehci->cached_sitd_list);
+               start_free_itds(ehci);
        }
+
        return retval;
 }
 
@@ -2254,74 +2196,39 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
                sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
        else
                usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
-done_not_linked:
+ done_not_linked:
        spin_unlock_irqrestore (&ehci->lock, flags);
-
-done:
-       if (status < 0)
-               iso_stream_put (ehci, stream);
+ done:
        return status;
 }
 
 /*-------------------------------------------------------------------------*/
 
-static void free_cached_lists(struct ehci_hcd *ehci)
+static void scan_isoc(struct ehci_hcd *ehci)
 {
-       struct ehci_itd *itd, *n;
-       struct ehci_sitd *sitd, *sn;
-
-       list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
-               struct ehci_iso_stream  *stream = itd->stream;
-               itd->stream = NULL;
-               list_move(&itd->itd_list, &stream->free_list);
-               iso_stream_put(ehci, stream);
-       }
-
-       list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
-               struct ehci_iso_stream  *stream = sitd->stream;
-               sitd->stream = NULL;
-               list_move(&sitd->sitd_list, &stream->free_list);
-               iso_stream_put(ehci, stream);
-       }
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void
-scan_periodic (struct ehci_hcd *ehci)
-{
-       unsigned        now_uframe, frame, clock, clock_frame, mod;
-       unsigned        modified;
-
-       mod = ehci->periodic_size << 3;
+       unsigned        uf, now_frame, frame;
+       unsigned        fmask = ehci->periodic_size - 1;
+       bool            modified, live;
 
        /*
         * When running, scan from last scan point up to "now"
         * else clean up by scanning everything that's left.
         * Touches as few pages as possible:  cache-friendly.
         */
-       now_uframe = ehci->next_uframe;
-       if (ehci->rh_state == EHCI_RH_RUNNING) {
-               clock = ehci_read_frame_index(ehci);
-               clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
+       if (ehci->rh_state >= EHCI_RH_RUNNING) {
+               uf = ehci_read_frame_index(ehci);
+               now_frame = (uf >> 3) & fmask;
+               live = true;
        } else  {
-               clock = now_uframe + mod - 1;
-               clock_frame = -1;
+               now_frame = (ehci->next_frame - 1) & fmask;
+               live = false;
        }
-       if (ehci->clock_frame != clock_frame) {
-               free_cached_lists(ehci);
-               ehci->clock_frame = clock_frame;
-       }
-       clock &= mod - 1;
-       clock_frame = clock >> 3;
-       ++ehci->periodic_stamp;
+       ehci->now_frame = now_frame;
 
+       frame = ehci->next_frame;
        for (;;) {
                union ehci_shadow       q, *q_p;
                __hc32                  type, *hw_p;
-               unsigned                incomplete = false;
-
-               frame = now_uframe >> 3;
 
 restart:
                /* scan each element in frame's queue for completions */
@@ -2329,48 +2236,17 @@ restart:
                hw_p = &ehci->periodic [frame];
                q.ptr = q_p->ptr;
                type = Q_NEXT_TYPE(ehci, *hw_p);
-               modified = 0;
+               modified = false;
 
                while (q.ptr != NULL) {
-                       unsigned                uf;
-                       union ehci_shadow       temp;
-                       int                     live;
-
-                       live = (ehci->rh_state == EHCI_RH_RUNNING);
                        switch (hc32_to_cpu(ehci, type)) {
-                       case Q_TYPE_QH:
-                               /* handle any completions */
-                               temp.qh = qh_get (q.qh);
-                               type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next);
-                               q = q.qh->qh_next;
-                               if (temp.qh->stamp != ehci->periodic_stamp) {
-                                       modified = qh_completions(ehci, temp.qh);
-                                       if (!modified)
-                                               temp.qh->stamp = ehci->periodic_stamp;
-                                       if (unlikely(list_empty(&temp.qh->qtd_list) ||
-                                                       temp.qh->needs_rescan))
-                                               intr_deschedule(ehci, temp.qh);
-                               }
-                               qh_put (temp.qh);
-                               break;
-                       case Q_TYPE_FSTN:
-                               /* for "save place" FSTNs, look at QH entries
-                                * in the previous frame for completions.
-                                */
-                               if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) {
-                                       ehci_dbg(ehci,
-                                               "ignoring completions from FSTNs\n");
-                               }
-                               type = Q_NEXT_TYPE(ehci, q.fstn->hw_next);
-                               q = q.fstn->fstn_next;
-                               break;
                        case Q_TYPE_ITD:
                                /* If this ITD is still active, leave it for
                                 * later processing ... check the next entry.
                                 * No need to check for activity unless the
                                 * frame is current.
                                 */
-                               if (frame == clock_frame && live) {
+                               if (frame == now_frame && live) {
                                        rmb();
                                        for (uf = 0; uf < 8; uf++) {
                                                if (q.itd->hw_transaction[uf] &
@@ -2378,7 +2254,6 @@ restart:
                                                        break;
                                        }
                                        if (uf < 8) {
-                                               incomplete = true;
                                                q_p = &q.itd->itd_next;
                                                hw_p = &q.itd->hw_next;
                                                type = Q_NEXT_TYPE(ehci,
@@ -2410,14 +2285,12 @@ restart:
                                 * No need to check for activity unless the
                                 * frame is current.
                                 */
-                               if (((frame == clock_frame) ||
-                                    (((frame + 1) & (ehci->periodic_size - 1))
-                                     == clock_frame))
+                               if (((frame == now_frame) ||
+                                    (((frame + 1) & fmask) == now_frame))
                                    && live
                                    && (q.sitd->hw_results &
                                        SITD_ACTIVE(ehci))) {
 
-                                       incomplete = true;
                                        q_p = &q.sitd->sitd_next;
                                        hw_p = &q.sitd->hw_next;
                                        type = Q_NEXT_TYPE(ehci,
@@ -2445,58 +2318,23 @@ restart:
                                ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n",
                                        type, frame, q.ptr);
                                // BUG ();
+                               /* FALL THROUGH */
+                       case Q_TYPE_QH:
+                       case Q_TYPE_FSTN:
+                               /* End of the iTDs and siTDs */
                                q.ptr = NULL;
+                               break;
                        }
 
                        /* assume completion callbacks modify the queue */
-                       if (unlikely (modified)) {
-                               if (likely(ehci->periodic_sched > 0))
-                                       goto restart;
-                               /* short-circuit this scan */
-                               now_uframe = clock;
-                               break;
-                       }
+                       if (unlikely(modified && ehci->isoc_count > 0))
+                               goto restart;
                }
 
-               /* If we can tell we caught up to the hardware, stop now.
-                * We can't advance our scan without collecting the ISO
-                * transfers that are still pending in this frame.
-                */
-               if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) {
-                       ehci->next_uframe = now_uframe;
+               /* Stop when we have reached the current frame */
+               if (frame == now_frame)
                        break;
-               }
-
-               // FIXME:  this assumes we won't get lapped when
-               // latencies climb; that should be rare, but...
-               // detect it, and just go all the way around.
-               // FLR might help detect this case, so long as latencies
-               // don't exceed periodic_size msec (default 1.024 sec).
-
-               // FIXME:  likewise assumes HC doesn't halt mid-scan
-
-               if (now_uframe == clock) {
-                       unsigned        now;
-
-                       if (ehci->rh_state != EHCI_RH_RUNNING
-                                       || ehci->periodic_sched == 0)
-                               break;
-                       ehci->next_uframe = now_uframe;
-                       now = ehci_read_frame_index(ehci) & (mod - 1);
-                       if (now_uframe == now)
-                               break;
-
-                       /* rescan the rest of this frame, then ... */
-                       clock = now;
-                       clock_frame = clock >> 3;
-                       if (ehci->clock_frame != clock_frame) {
-                               free_cached_lists(ehci);
-                               ehci->clock_frame = clock_frame;
-                               ++ehci->periodic_stamp;
-                       }
-               } else {
-                       now_uframe++;
-                       now_uframe &= mod - 1;
-               }
+               frame = (frame + 1) & fmask;
        }
+       ehci->next_frame = now_frame;
 }
index cc199e8..58c96bd 100644 (file)
@@ -160,84 +160,16 @@ static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev)
 static int ehci_hcd_sead3_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       unsigned long flags;
-       int rc = 0;
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(20);
-
-       /* Root hub was already suspended. Disable irq emission and
-        * mark HW unaccessible.  The PM and USB cores make sure that
-        * the root hub is either suspended or stopped.
-        */
-       ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
-       spin_lock_irqsave(&ehci->lock, flags);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       (void)ehci_readl(ehci, &ehci->regs->intr_enable);
-
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       spin_unlock_irqrestore(&ehci->lock, flags);
+       bool do_wakeup = device_may_wakeup(dev);
 
-       /* could save FLADJ in case of Vaux power loss
-        * ... we'd only use it to handle clock skew
-        */
-
-       return rc;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static int ehci_hcd_sead3_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-
-       /* maybe restore FLADJ. */
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       /* Mark hardware accessible again as we are out of D3 state by now */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       /* If CF is still set, we maintained PCI Vaux power.
-        * Just undo the effect of ehci_pci_suspend().
-        */
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
-               int     mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       ehci_dbg(ehci, "lost power, restarting\n");
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       /* Else reset, to cope with power loss or flush-to-storage
-        * style "resume" having let BIOS kick in during reboot.
-        */
-       (void) ehci_halt(ehci);
-       (void) ehci_reset(ehci);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
-
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
-
-       ehci->rh_state = EHCI_RH_SUSPENDED;
 
+       ehci_resume(hcd, false);
        return 0;
 }
 
index e7cb392..b3f1e36 100644 (file)
@@ -24,25 +24,11 @@ static int ehci_sh_reset(struct usb_hcd *hcd)
        int ret;
 
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci,
-               &ehci->caps->hc_capbase));
 
-       dbg_hcs_params(ehci, "reset");
-       dbg_hcc_params(ehci, "reset");
-
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
-       ret = ehci_halt(ehci);
-       if (unlikely(ret))
-               return ret;
-
-       ret = ehci_init(hcd);
+       ret = ehci_setup(hcd);
        if (unlikely(ret))
                return ret;
 
-       ehci->sbrn = 0x20;
-
-       ehci_reset(ehci);
        ehci_port_power(ehci, 0);
 
        return ret;
index 37ba8c8..c718a06 100644 (file)
@@ -41,19 +41,11 @@ static int ehci_spear_setup(struct usb_hcd *hcd)
 
        /* registers start at offset 0x0 */
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci,
-                               &ehci->caps->hc_capbase));
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
 
-       retval = ehci_init(hcd);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
-       ehci_reset(ehci);
        ehci_port_power(ehci, 0);
 
        return retval;
@@ -97,71 +89,16 @@ static const struct hc_driver ehci_spear_hc_driver = {
 static int ehci_spear_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       unsigned long flags;
-       int rc = 0;
+       bool do_wakeup = device_may_wakeup(dev);
 
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(10);
-
-       /*
-        * Root hub was already suspended. Disable irq emission and mark HW
-        * unaccessible. The PM and USB cores make sure that the root hub is
-        * either suspended or stopped.
-        */
-       spin_lock_irqsave(&ehci->lock, flags);
-       ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       ehci_readl(ehci, &ehci->regs->intr_enable);
-       spin_unlock_irqrestore(&ehci->lock, flags);
-
-       return rc;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static int ehci_spear_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
-               int mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       /*
-        * Else reset, to cope with power loss or flush-to-storage style
-        * "resume" having let BIOS kick in during reboot.
-        */
-       ehci_halt(ehci);
-       ehci_reset(ehci);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
 
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
+       ehci_resume(hcd, false);
        return 0;
 }
 #endif /* CONFIG_PM */
index ab8a3bf..950e95e 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/tegra_usb.h>
 #include <linux/irq.h>
@@ -280,30 +281,14 @@ static int tegra_ehci_setup(struct usb_hcd *hcd)
 
        /* EHCI registers start at offset 0x100 */
        ehci->caps = hcd->regs + 0x100;
-       ehci->regs = hcd->regs + 0x100 +
-               HC_LENGTH(ehci, 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);
 
        /* switch to host mode */
        hcd->has_tt = 1;
-       ehci_reset(ehci);
-
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
 
-       /* data structure init */
-       retval = ehci_init(hcd);
+       retval = ehci_setup(hcd);
        if (retval)
                return retval;
 
-       ehci->sbrn = 0x20;
-
        ehci_port_power(ehci, 1);
        return retval;
 }
@@ -460,12 +445,11 @@ static int controller_suspend(struct device *dev)
        if (time_before(jiffies, ehci->next_statechange))
                msleep(10);
 
-       spin_lock_irqsave(&ehci->lock, flags);
+       ehci_halt(ehci);
 
+       spin_lock_irqsave(&ehci->lock, flags);
        tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;
-       ehci_halt(ehci);
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
        spin_unlock_irqrestore(&ehci->lock, flags);
 
        tegra_ehci_power_down(hcd);
@@ -749,8 +733,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)
 
 #ifdef CONFIG_USB_OTG_UTILS
        if (pdata->operating_mode == TEGRA_USB_OTG) {
-               tegra->transceiver = usb_get_transceiver();
-               if (tegra->transceiver)
+               tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+               if (!IS_ERR_OR_NULL(tegra->transceiver))
                        otg_set_host(tegra->transceiver->otg, &hcd->self);
        }
 #endif
@@ -773,9 +757,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
 
 fail:
 #ifdef CONFIG_USB_OTG_UTILS
-       if (tegra->transceiver) {
+       if (!IS_ERR_OR_NULL(tegra->transceiver)) {
                otg_set_host(tegra->transceiver->otg, NULL);
-               usb_put_transceiver(tegra->transceiver);
+               usb_put_phy(tegra->transceiver);
        }
 #endif
        tegra_usb_phy_close(tegra->phy);
@@ -808,9 +792,9 @@ static int tegra_ehci_remove(struct platform_device *pdev)
        pm_runtime_put_noidle(&pdev->dev);
 
 #ifdef CONFIG_USB_OTG_UTILS
-       if (tegra->transceiver) {
+       if (!IS_ERR_OR_NULL(tegra->transceiver)) {
                otg_set_host(tegra->transceiver->otg, NULL);
-               usb_put_transceiver(tegra->transceiver);
+               usb_put_phy(tegra->transceiver);
        }
 #endif
 
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
new file mode 100644 (file)
index 0000000..eb896a2
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2012 by Alan Stern
+ *
+ * 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.
+ */
+
+/* This file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/* Set a bit in the USBCMD register */
+static void ehci_set_command_bit(struct ehci_hcd *ehci, u32 bit)
+{
+       ehci->command |= bit;
+       ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+       /* unblock posted write */
+       ehci_readl(ehci, &ehci->regs->command);
+}
+
+/* Clear a bit in the USBCMD register */
+static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
+{
+       ehci->command &= ~bit;
+       ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+       /* unblock posted write */
+       ehci_readl(ehci, &ehci->regs->command);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI timer support...  Now using hrtimers.
+ *
+ * Lots of different events are triggered from ehci->hrtimer.  Whenever
+ * the timer routine runs, it checks each possible event; events that are
+ * currently enabled and whose expiration time has passed get handled.
+ * The set of enabled events is stored as a collection of bitflags in
+ * ehci->enabled_hrtimer_events, and they are numbered in order of
+ * increasing delay values (ranging between 1 ms and 100 ms).
+ *
+ * Rather than implementing a sorted list or tree of all pending events,
+ * we keep track only of the lowest-numbered pending event, in
+ * ehci->next_hrtimer_event.  Whenever ehci->hrtimer gets restarted, its
+ * expiration time is set to the timeout value for this event.
+ *
+ * As a result, events might not get handled right away; the actual delay
+ * could be anywhere up to twice the requested delay.  This doesn't
+ * matter, because none of the events are especially time-critical.  The
+ * ones that matter most all have a delay of 1 ms, so they will be
+ * handled after 2 ms at most, which is okay.  In addition to this, we
+ * allow for an expiration range of 1 ms.
+ */
+
+/*
+ * Delay lengths for the hrtimer event types.
+ * Keep this list sorted by delay length, in the same order as
+ * the event types indexed by enum ehci_hrtimer_event in ehci.h.
+ */
+static unsigned event_delays_ns[] = {
+       1 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_POLL_ASS */
+       1 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_POLL_PSS */
+       1 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_POLL_DEAD */
+       1125 * NSEC_PER_USEC,   /* EHCI_HRTIMER_UNLINK_INTR */
+       2 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_FREE_ITDS */
+       6 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_ASYNC_UNLINKS */
+       10 * NSEC_PER_MSEC,     /* EHCI_HRTIMER_IAA_WATCHDOG */
+       10 * NSEC_PER_MSEC,     /* EHCI_HRTIMER_DISABLE_PERIODIC */
+       15 * NSEC_PER_MSEC,     /* EHCI_HRTIMER_DISABLE_ASYNC */
+       100 * NSEC_PER_MSEC,    /* EHCI_HRTIMER_IO_WATCHDOG */
+};
+
+/* Enable a pending hrtimer event */
+static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
+               bool resched)
+{
+       ktime_t         *timeout = &ehci->hr_timeouts[event];
+
+       if (resched)
+               *timeout = ktime_add(ktime_get(),
+                               ktime_set(0, event_delays_ns[event]));
+       ehci->enabled_hrtimer_events |= (1 << event);
+
+       /* Track only the lowest-numbered pending event */
+       if (event < ehci->next_hrtimer_event) {
+               ehci->next_hrtimer_event = event;
+               hrtimer_start_range_ns(&ehci->hrtimer, *timeout,
+                               NSEC_PER_MSEC, HRTIMER_MODE_ABS);
+       }
+}
+
+
+/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */
+static void ehci_poll_ASS(struct ehci_hcd *ehci)
+{
+       unsigned        actual, want;
+
+       /* Don't enable anything if the controller isn't running (e.g., died) */
+       if (ehci->rh_state != EHCI_RH_RUNNING)
+               return;
+
+       want = (ehci->command & CMD_ASE) ? STS_ASS : 0;
+       actual = ehci_readl(ehci, &ehci->regs->status) & STS_ASS;
+
+       if (want != actual) {
+
+               /* Poll again later, but give up after about 20 ms */
+               if (ehci->ASS_poll_count++ < 20) {
+                       ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
+                       return;
+               }
+               ehci_warn(ehci, "Waited too long for the async schedule status, giving up\n");
+       }
+       ehci->ASS_poll_count = 0;
+
+       /* The status is up-to-date; restart or stop the schedule as needed */
+       if (want == 0) {        /* Stopped */
+               if (ehci->async_count > 0)
+                       ehci_set_command_bit(ehci, CMD_ASE);
+
+       } else {                /* Running */
+               if (ehci->async_count == 0) {
+
+                       /* Turn off the schedule after a while */
+                       ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_ASYNC,
+                                       true);
+               }
+       }
+}
+
+/* Turn off the async schedule after a brief delay */
+static void ehci_disable_ASE(struct ehci_hcd *ehci)
+{
+       ehci_clear_command_bit(ehci, CMD_ASE);
+}
+
+
+/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
+static void ehci_poll_PSS(struct ehci_hcd *ehci)
+{
+       unsigned        actual, want;
+
+       /* Don't do anything if the controller isn't running (e.g., died) */
+       if (ehci->rh_state != EHCI_RH_RUNNING)
+               return;
+
+       want = (ehci->command & CMD_PSE) ? STS_PSS : 0;
+       actual = ehci_readl(ehci, &ehci->regs->status) & STS_PSS;
+
+       if (want != actual) {
+
+               /* Poll again later, but give up after about 20 ms */
+               if (ehci->PSS_poll_count++ < 20) {
+                       ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
+                       return;
+               }
+               ehci_warn(ehci, "Waited too long for the periodic schedule status, giving up\n");
+       }
+       ehci->PSS_poll_count = 0;
+
+       /* The status is up-to-date; restart or stop the schedule as needed */
+       if (want == 0) {        /* Stopped */
+               if (ehci->periodic_count > 0)
+                       ehci_set_command_bit(ehci, CMD_PSE);
+
+       } else {                /* Running */
+               if (ehci->periodic_count == 0) {
+
+                       /* Turn off the schedule after a while */
+                       ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC,
+                                       true);
+               }
+       }
+}
+
+/* Turn off the periodic schedule after a brief delay */
+static void ehci_disable_PSE(struct ehci_hcd *ehci)
+{
+       ehci_clear_command_bit(ehci, CMD_PSE);
+}
+
+
+/* Poll the STS_HALT status bit; see when a dead controller stops */
+static void ehci_handle_controller_death(struct ehci_hcd *ehci)
+{
+       if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) {
+
+               /* Give up after a few milliseconds */
+               if (ehci->died_poll_count++ < 5) {
+                       /* Try again later */
+                       ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true);
+                       return;
+               }
+               ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n");
+       }
+
+       /* Clean up the mess */
+       ehci->rh_state = EHCI_RH_HALTED;
+       ehci_writel(ehci, 0, &ehci->regs->configured_flag);
+       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+       ehci_work(ehci);
+       end_unlink_async(ehci);
+
+       /* Not in process context, so don't try to reset the controller */
+}
+
+
+/* Handle unlinked interrupt QHs once they are gone from the hardware */
+static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
+{
+       bool            stopped = (ehci->rh_state < EHCI_RH_RUNNING);
+
+       /*
+        * Process all the QHs on the intr_unlink list that were added
+        * before the current unlink cycle began.  The list is in
+        * temporal order, so stop when we reach the first entry in the
+        * current cycle.  But if the root hub isn't running then
+        * process all the QHs on the list.
+        */
+       ehci->intr_unlinking = true;
+       while (ehci->intr_unlink) {
+               struct ehci_qh  *qh = ehci->intr_unlink;
+
+               if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle)
+                       break;
+               ehci->intr_unlink = qh->unlink_next;
+               qh->unlink_next = NULL;
+               end_unlink_intr(ehci, qh);
+       }
+
+       /* Handle remaining entries later */
+       if (ehci->intr_unlink) {
+               ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true);
+               ++ehci->intr_unlink_cycle;
+       }
+       ehci->intr_unlinking = false;
+}
+
+
+/* Start another free-iTDs/siTDs cycle */
+static void start_free_itds(struct ehci_hcd *ehci)
+{
+       if (!(ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_FREE_ITDS))) {
+               ehci->last_itd_to_free = list_entry(
+                               ehci->cached_itd_list.prev,
+                               struct ehci_itd, itd_list);
+               ehci->last_sitd_to_free = list_entry(
+                               ehci->cached_sitd_list.prev,
+                               struct ehci_sitd, sitd_list);
+               ehci_enable_event(ehci, EHCI_HRTIMER_FREE_ITDS, true);
+       }
+}
+
+/* Wait for controller to stop using old iTDs and siTDs */
+static void end_free_itds(struct ehci_hcd *ehci)
+{
+       struct ehci_itd         *itd, *n;
+       struct ehci_sitd        *sitd, *sn;
+
+       if (ehci->rh_state < EHCI_RH_RUNNING) {
+               ehci->last_itd_to_free = NULL;
+               ehci->last_sitd_to_free = NULL;
+       }
+
+       list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
+               list_del(&itd->itd_list);
+               dma_pool_free(ehci->itd_pool, itd, itd->itd_dma);
+               if (itd == ehci->last_itd_to_free)
+                       break;
+       }
+       list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
+               list_del(&sitd->sitd_list);
+               dma_pool_free(ehci->sitd_pool, sitd, sitd->sitd_dma);
+               if (sitd == ehci->last_sitd_to_free)
+                       break;
+       }
+
+       if (!list_empty(&ehci->cached_itd_list) ||
+                       !list_empty(&ehci->cached_sitd_list))
+               start_free_itds(ehci);
+}
+
+
+/* Handle lost (or very late) IAA interrupts */
+static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
+{
+       if (ehci->rh_state != EHCI_RH_RUNNING)
+               return;
+
+       /*
+        * Lost IAA irqs wedge things badly; seen first with a vt8235.
+        * So we need this watchdog, but must protect it against both
+        * (a) SMP races against real IAA firing and retriggering, and
+        * (b) clean HC shutdown, when IAA watchdog was pending.
+        */
+       if (ehci->async_iaa) {
+               u32 cmd, status;
+
+               /* If we get here, IAA is *REALLY* late.  It's barely
+                * conceivable that the system is so busy that CMD_IAAD
+                * is still legitimately set, so let's be sure it's
+                * clear before we read STS_IAA.  (The HC should clear
+                * CMD_IAAD when it sets STS_IAA.)
+                */
+               cmd = ehci_readl(ehci, &ehci->regs->command);
+
+               /*
+                * If IAA is set here it either legitimately triggered
+                * after the watchdog timer expired (_way_ late, so we'll
+                * still count it as lost) ... or a silicon erratum:
+                * - VIA seems to set IAA without triggering the IRQ;
+                * - IAAD potentially cleared without setting IAA.
+                */
+               status = ehci_readl(ehci, &ehci->regs->status);
+               if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
+                       COUNT(ehci->stats.lost_iaa);
+                       ehci_writel(ehci, STS_IAA, &ehci->regs->status);
+               }
+
+               ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n",
+                               status, cmd);
+               end_unlink_async(ehci);
+       }
+}
+
+
+/* Enable the I/O watchdog, if appropriate */
+static void turn_on_io_watchdog(struct ehci_hcd *ehci)
+{
+       /* Not needed if the controller isn't running or it's already enabled */
+       if (ehci->rh_state != EHCI_RH_RUNNING ||
+                       (ehci->enabled_hrtimer_events &
+                               BIT(EHCI_HRTIMER_IO_WATCHDOG)))
+               return;
+
+       /*
+        * Isochronous transfers always need the watchdog.
+        * For other sorts we use it only if the flag is set.
+        */
+       if (ehci->isoc_count > 0 || (ehci->need_io_watchdog &&
+                       ehci->async_count + ehci->intr_count > 0))
+               ehci_enable_event(ehci, EHCI_HRTIMER_IO_WATCHDOG, true);
+}
+
+
+/*
+ * Handler functions for the hrtimer event types.
+ * Keep this array in the same order as the event types indexed by
+ * enum ehci_hrtimer_event in ehci.h.
+ */
+static void (*event_handlers[])(struct ehci_hcd *) = {
+       ehci_poll_ASS,                  /* EHCI_HRTIMER_POLL_ASS */
+       ehci_poll_PSS,                  /* EHCI_HRTIMER_POLL_PSS */
+       ehci_handle_controller_death,   /* EHCI_HRTIMER_POLL_DEAD */
+       ehci_handle_intr_unlinks,       /* EHCI_HRTIMER_UNLINK_INTR */
+       end_free_itds,                  /* EHCI_HRTIMER_FREE_ITDS */
+       unlink_empty_async,             /* EHCI_HRTIMER_ASYNC_UNLINKS */
+       ehci_iaa_watchdog,              /* EHCI_HRTIMER_IAA_WATCHDOG */
+       ehci_disable_PSE,               /* EHCI_HRTIMER_DISABLE_PERIODIC */
+       ehci_disable_ASE,               /* EHCI_HRTIMER_DISABLE_ASYNC */
+       ehci_work,                      /* EHCI_HRTIMER_IO_WATCHDOG */
+};
+
+static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
+{
+       struct ehci_hcd *ehci = container_of(t, struct ehci_hcd, hrtimer);
+       ktime_t         now;
+       unsigned long   events;
+       unsigned long   flags;
+       unsigned        e;
+
+       spin_lock_irqsave(&ehci->lock, flags);
+
+       events = ehci->enabled_hrtimer_events;
+       ehci->enabled_hrtimer_events = 0;
+       ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
+
+       /*
+        * Check each pending event.  If its time has expired, handle
+        * the event; otherwise re-enable it.
+        */
+       now = ktime_get();
+       for_each_set_bit(e, &events, EHCI_HRTIMER_NUM_EVENTS) {
+               if (now.tv64 >= ehci->hr_timeouts[e].tv64)
+                       event_handlers[e](ehci);
+               else
+                       ehci_enable_event(ehci, e, false);
+       }
+
+       spin_unlock_irqrestore(&ehci->lock, flags);
+       return HRTIMER_NORESTART;
+}
index c1eda73..4d147c4 100644 (file)
@@ -48,7 +48,7 @@ static const struct hc_driver vt8500_ehci_hc_driver = {
        /*
         * basic lifecycle operations
         */
-       .reset                  = ehci_init,
+       .reset                  = ehci_setup,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
        .shutdown               = ehci_shutdown,
@@ -121,18 +121,6 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev)
 
        ehci = hcd_to_ehci(hcd);
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, 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);
-
-       ehci_port_power(ehci, 1);
-
-       ehci_reset(ehci);
 
        ret = usb_add_hcd(hcd, pdev->resource[1].start,
                          IRQF_SHARED);
index 3d2e26c..ec59808 100644 (file)
@@ -71,21 +71,14 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver,
        val |= ENPHY;
        __raw_writel(val, ehci->regs+PHY1_CTR);
 
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-       ehci->sbrn = 0x20;
-
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
                goto err4;
 
-       ehci_reset(ehci);
-
        retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (retval != 0)
                goto err4;
 
-       ehci_writel(ehci, 1, &ehci->regs->configured_flag);
-
        return retval;
 err4:
        iounmap(hcd->regs);
@@ -120,7 +113,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = {
        /*
         * basic lifecycle operations
         */
-       .reset = ehci_init,
+       .reset = ehci_setup,
        .start = ehci_run,
 
        .stop = ehci_stop,
index e9713d5..39f24fa 100644 (file)
 #include <linux/of_address.h>
 
 /**
- * ehci_xilinx_of_setup - Initialize the device for ehci_reset()
- * @hcd:       Pointer to the usb_hcd device to which the host controller bound
- *
- * called during probe() after chip reset completes.
- */
-static int ehci_xilinx_of_setup(struct usb_hcd *hcd)
-{
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       int             retval;
-
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
-
-       retval = ehci_init(hcd);
-       if (retval)
-               return retval;
-
-       ehci->sbrn = 0x20;
-
-       return ehci_reset(ehci);
-}
-
-/**
  * ehci_xilinx_port_handed_over - hand the port out if failed to enable it
  * @hcd:       Pointer to the usb_hcd device to which the host controller bound
  * @portnum:Port number to which the device is attached.
@@ -107,7 +83,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
        /*
         * basic lifecycle operations
         */
-       .reset                  = ehci_xilinx_of_setup,
+       .reset                  = ehci_setup,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
        .shutdown               = ehci_shutdown,
@@ -219,11 +195,6 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op)
        /* Debug registers are at the first 0x100 region
         */
        ehci->caps = hcd->regs + 0x100;
-       ehci->regs = hcd->regs + 0x100 +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
 
        rv = usb_add_hcd(hcd, irq, 0);
        if (rv == 0)
index 72f0819..8dc6a22 100644 (file)
 
 static int ehci_xls_setup(struct usb_hcd *hcd)
 {
-       int     retval;
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 
        ehci->caps = hcd->regs;
-       ehci->regs = hcd->regs +
-               HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-       dbg_hcs_params(ehci, "reset");
-       dbg_hcc_params(ehci, "reset");
 
-       /* cache this readonly data; minimize chip reads */
-       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
-       retval = ehci_halt(ehci);
-       if (retval)
-               return retval;
-
-       /* data structure init */
-       retval = ehci_init(hcd);
-       if (retval)
-               return retval;
-
-       ehci_reset(ehci);
-
-       return retval;
+       return ehci_setup(hcd);
 }
 
 int ehci_xls_probe_internal(const struct hc_driver *driver,
index 2694ed6..da07d98 100644 (file)
@@ -42,7 +42,7 @@ struct ehci_stats {
        /* irq usage */
        unsigned long           normal;
        unsigned long           error;
-       unsigned long           reclaim;
+       unsigned long           iaa;
        unsigned long           lost_iaa;
 
        /* termination of urbs from core */
@@ -51,7 +51,7 @@ struct ehci_stats {
 };
 
 /* ehci_hcd->lock guards shared data against other CPUs:
- *   ehci_hcd: async, reclaim, periodic (and shadow), ...
+ *   ehci_hcd: async, unlink, periodic (and shadow), ...
  *   usb_host_endpoint: hcpriv
  *   ehci_qh:  qh_next, qtd_list
  *   ehci_qtd: qtd_list
@@ -62,13 +62,48 @@ struct ehci_stats {
 
 #define        EHCI_MAX_ROOT_PORTS     15              /* see HCS_N_PORTS */
 
+/*
+ * ehci_rh_state values of EHCI_RH_RUNNING or above mean that the
+ * controller may be doing DMA.  Lower values mean there's no DMA.
+ */
 enum ehci_rh_state {
        EHCI_RH_HALTED,
        EHCI_RH_SUSPENDED,
-       EHCI_RH_RUNNING
+       EHCI_RH_RUNNING,
+       EHCI_RH_STOPPING
 };
 
+/*
+ * Timer events, ordered by increasing delay length.
+ * Always update event_delays_ns[] and event_handlers[] (defined in
+ * ehci-timer.c) in parallel with this list.
+ */
+enum ehci_hrtimer_event {
+       EHCI_HRTIMER_POLL_ASS,          /* Poll for async schedule off */
+       EHCI_HRTIMER_POLL_PSS,          /* Poll for periodic schedule off */
+       EHCI_HRTIMER_POLL_DEAD,         /* Wait for dead controller to stop */
+       EHCI_HRTIMER_UNLINK_INTR,       /* Wait for interrupt QH unlink */
+       EHCI_HRTIMER_FREE_ITDS,         /* Wait for unused iTDs and siTDs */
+       EHCI_HRTIMER_ASYNC_UNLINKS,     /* Unlink empty async QHs */
+       EHCI_HRTIMER_IAA_WATCHDOG,      /* Handle lost IAA interrupts */
+       EHCI_HRTIMER_DISABLE_PERIODIC,  /* Wait to disable periodic sched */
+       EHCI_HRTIMER_DISABLE_ASYNC,     /* Wait to disable async sched */
+       EHCI_HRTIMER_IO_WATCHDOG,       /* Check for missing IRQs */
+       EHCI_HRTIMER_NUM_EVENTS         /* Must come last */
+};
+#define EHCI_HRTIMER_NO_EVENT  99
+
 struct ehci_hcd {                      /* one per controller */
+       /* timing support */
+       enum ehci_hrtimer_event next_hrtimer_event;
+       unsigned                enabled_hrtimer_events;
+       ktime_t                 hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
+       struct hrtimer          hrtimer;
+
+       int                     PSS_poll_count;
+       int                     ASS_poll_count;
+       int                     died_poll_count;
+
        /* glue to PCI and HCD framework */
        struct ehci_caps __iomem *caps;
        struct ehci_regs __iomem *regs;
@@ -78,30 +113,48 @@ struct ehci_hcd {                  /* one per controller */
        spinlock_t              lock;
        enum ehci_rh_state      rh_state;
 
+       /* general schedule support */
+       bool                    scanning:1;
+       bool                    need_rescan:1;
+       bool                    intr_unlinking:1;
+       bool                    async_unlinking:1;
+       bool                    shutdown:1;
+       struct ehci_qh          *qh_scan_next;
+
        /* async schedule support */
        struct ehci_qh          *async;
        struct ehci_qh          *dummy;         /* For AMD quirk use */
-       struct ehci_qh          *reclaim;
-       struct ehci_qh          *qh_scan_next;
-       unsigned                scanning : 1;
+       struct ehci_qh          *async_unlink;
+       struct ehci_qh          *async_unlink_last;
+       struct ehci_qh          *async_iaa;
+       unsigned                async_unlink_cycle;
+       unsigned                async_count;    /* async activity count */
 
        /* periodic schedule support */
 #define        DEFAULT_I_TDPS          1024            /* some HCs can do less */
        unsigned                periodic_size;
        __hc32                  *periodic;      /* hw periodic table */
        dma_addr_t              periodic_dma;
+       struct list_head        intr_qh_list;
        unsigned                i_thresh;       /* uframes HC might cache */
 
        union ehci_shadow       *pshadow;       /* mirror hw periodic table */
-       int                     next_uframe;    /* scan periodic, start here */
-       unsigned                periodic_sched; /* periodic activity count */
+       struct ehci_qh          *intr_unlink;
+       struct ehci_qh          *intr_unlink_last;
+       unsigned                intr_unlink_cycle;
+       unsigned                now_frame;      /* frame from HC hardware */
+       unsigned                next_frame;     /* scan periodic, start here */
+       unsigned                intr_count;     /* intr activity count */
+       unsigned                isoc_count;     /* isoc activity count */
+       unsigned                periodic_count; /* periodic activity count */
        unsigned                uframe_periodic_max; /* max periodic time per uframe */
 
 
-       /* list of itds & sitds completed while clock_frame was still active */
+       /* list of itds & sitds completed while now_frame was still active */
        struct list_head        cached_itd_list;
+       struct ehci_itd         *last_itd_to_free;
        struct list_head        cached_sitd_list;
-       unsigned                clock_frame;
+       struct ehci_sitd        *last_sitd_to_free;
 
        /* per root hub port */
        unsigned long           reset_done [EHCI_MAX_ROOT_PORTS];
@@ -126,10 +179,6 @@ struct ehci_hcd {                  /* one per controller */
        struct dma_pool         *itd_pool;      /* itd per iso urb */
        struct dma_pool         *sitd_pool;     /* sitd per split iso urb */
 
-       struct timer_list       iaa_watchdog;
-       struct timer_list       watchdog;
-       unsigned long           actions;
-       unsigned                periodic_stamp;
        unsigned                random_frame;
        unsigned long           next_statechange;
        ktime_t                 last_periodic_enable;
@@ -143,7 +192,6 @@ struct ehci_hcd {                   /* one per controller */
        unsigned                big_endian_capbase:1;
        unsigned                has_amcc_usb23:1;
        unsigned                need_io_watchdog:1;
-       unsigned                broken_periodic:1;
        unsigned                amd_pll_fix:1;
        unsigned                fs_i_thresh:1;  /* Intel iso scheduling */
        unsigned                use_dummy_qh:1; /* AMD Frame List table quirk*/
@@ -175,10 +223,6 @@ struct ehci_hcd {                  /* one per controller */
 #ifdef DEBUG
        struct dentry           *debug_dir;
 #endif
-       /*
-        * OTG controllers and transceivers need software interaction
-        */
-       struct usb_phy  *transceiver;
 };
 
 /* convert between an HCD pointer and the corresponding EHCI_HCD */
@@ -191,34 +235,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
        return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
 }
 
-
-static inline void
-iaa_watchdog_start(struct ehci_hcd *ehci)
-{
-       WARN_ON(timer_pending(&ehci->iaa_watchdog));
-       mod_timer(&ehci->iaa_watchdog,
-                       jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
-}
-
-static inline void iaa_watchdog_done(struct ehci_hcd *ehci)
-{
-       del_timer(&ehci->iaa_watchdog);
-}
-
-enum ehci_timer_action {
-       TIMER_IO_WATCHDOG,
-       TIMER_ASYNC_SHRINK,
-       TIMER_ASYNC_OFF,
-};
-
-static inline void
-timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
-{
-       clear_bit (action, &ehci->actions);
-}
-
-static void free_cached_lists(struct ehci_hcd *ehci);
-
 /*-------------------------------------------------------------------------*/
 
 #include <linux/usb/ehci_def.h>
@@ -328,7 +344,13 @@ union ehci_shadow {
 struct ehci_qh_hw {
        __hc32                  hw_next;        /* see EHCI 3.6.1 */
        __hc32                  hw_info1;       /* see EHCI 3.6.2 */
-#define        QH_HEAD         0x00008000
+#define        QH_CONTROL_EP   (1 << 27)       /* FS/LS control endpoint */
+#define        QH_HEAD         (1 << 15)       /* Head of async reclamation list */
+#define        QH_TOGGLE_CTL   (1 << 14)       /* Data toggle control */
+#define        QH_HIGH_SPEED   (2 << 12)       /* Endpoint speed */
+#define        QH_LOW_SPEED    (1 << 12)
+#define        QH_FULL_SPEED   (0 << 12)
+#define        QH_INACTIVATE   (1 << 7)        /* Inactivate on next transaction */
        __hc32                  hw_info2;        /* see EHCI 3.6.2 */
 #define        QH_SMASK        0x000000ff
 #define        QH_CMASK        0x0000ff00
@@ -346,32 +368,23 @@ struct ehci_qh_hw {
 } __attribute__ ((aligned(32)));
 
 struct ehci_qh {
-       struct ehci_qh_hw       *hw;
+       struct ehci_qh_hw       *hw;            /* Must come first */
        /* the rest is HCD-private */
        dma_addr_t              qh_dma;         /* address of qh */
        union ehci_shadow       qh_next;        /* ptr to qh; or periodic */
        struct list_head        qtd_list;       /* sw qtd list */
+       struct list_head        intr_node;      /* list of intr QHs */
        struct ehci_qtd         *dummy;
-       struct ehci_qh          *reclaim;       /* next to reclaim */
-
-       struct ehci_hcd         *ehci;
-       unsigned long           unlink_time;
+       struct ehci_qh          *unlink_next;   /* next on unlink list */
 
-       /*
-        * Do NOT use atomic operations for QH refcounting. On some CPUs
-        * (PPC7448 for example), atomic operations cannot be performed on
-        * memory that is cache-inhibited (i.e. being used for DMA).
-        * Spinlocks are used to protect all QH fields.
-        */
-       u32                     refcount;
-       unsigned                stamp;
+       unsigned                unlink_cycle;
 
        u8                      needs_rescan;   /* Dequeue during giveback */
        u8                      qh_state;
 #define        QH_STATE_LINKED         1               /* HC sees this */
 #define        QH_STATE_UNLINK         2               /* HC may still see this */
 #define        QH_STATE_IDLE           3               /* HC doesn't see this */
-#define        QH_STATE_UNLINK_WAIT    4               /* LINKED and on reclaim q */
+#define        QH_STATE_UNLINK_WAIT    4               /* LINKED and on unlink q */
 #define        QH_STATE_COMPLETING     5               /* don't touch token.HALT */
 
        u8                      xacterrs;       /* XactErr retry counter */
@@ -421,7 +434,6 @@ struct ehci_iso_stream {
        /* first field matches ehci_hq, but is NULL */
        struct ehci_qh_hw       *hw;
 
-       u32                     refcount;
        u8                      bEndpointAddress;
        u8                      highspeed;
        struct list_head        td_list;        /* queued itds/sitds */
index 6fe5500..f238cb3 100644 (file)
@@ -41,7 +41,7 @@ void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er)
 static int fhci_dfs_regs_show(struct seq_file *s, void *v)
 {
        struct fhci_hcd *fhci = s->private;
-       struct fhci_regs __iomem *regs = fhci->regs;
+       struct qe_usb_ctlr __iomem *regs = fhci->regs;
 
        seq_printf(s,
                "mode: 0x%x\n" "addr: 0x%x\n"
@@ -50,11 +50,11 @@ static int fhci_dfs_regs_show(struct seq_file *s, void *v)
                "status: 0x%x\n" "SOF timer: %d\n"
                "frame number: %d\n"
                "lines status: 0x%x\n",
-               in_8(&regs->usb_mod), in_8(&regs->usb_addr),
-               in_8(&regs->usb_comm), in_be16(&regs->usb_ep[0]),
-               in_be16(&regs->usb_event), in_be16(&regs->usb_mask),
-               in_8(&regs->usb_status), in_be16(&regs->usb_sof_tmr),
-               in_be16(&regs->usb_frame_num),
+               in_8(&regs->usb_usmod), in_8(&regs->usb_usadr),
+               in_8(&regs->usb_uscom), in_be16(&regs->usb_usep[0]),
+               in_be16(&regs->usb_usber), in_be16(&regs->usb_usbmr),
+               in_8(&regs->usb_usbs), in_be16(&regs->usb_ussft),
+               in_be16(&regs->usb_usfrn),
                fhci_ioports_check_bus_state(fhci));
 
        return 0;
index d262374..7da1a26 100644 (file)
@@ -40,8 +40,8 @@ void fhci_start_sof_timer(struct fhci_hcd *fhci)
        /* clear frame_n */
        out_be16(&fhci->pram->frame_num, 0);
 
-       out_be16(&fhci->regs->usb_sof_tmr, 0);
-       setbits8(&fhci->regs->usb_mod, USB_MODE_SFTE);
+       out_be16(&fhci->regs->usb_ussft, 0);
+       setbits8(&fhci->regs->usb_usmod, USB_MODE_SFTE);
 
        fhci_dbg(fhci, "<- %s\n", __func__);
 }
@@ -50,7 +50,7 @@ void fhci_stop_sof_timer(struct fhci_hcd *fhci)
 {
        fhci_dbg(fhci, "-> %s\n", __func__);
 
-       clrbits8(&fhci->regs->usb_mod, USB_MODE_SFTE);
+       clrbits8(&fhci->regs->usb_usmod, USB_MODE_SFTE);
        gtm_stop_timer16(fhci->timer);
 
        fhci_dbg(fhci, "<- %s\n", __func__);
@@ -58,7 +58,7 @@ void fhci_stop_sof_timer(struct fhci_hcd *fhci)
 
 u16 fhci_get_sof_timer_count(struct fhci_usb *usb)
 {
-       return be16_to_cpu(in_be16(&usb->fhci->regs->usb_sof_tmr) / 12);
+       return be16_to_cpu(in_be16(&usb->fhci->regs->usb_ussft) / 12);
 }
 
 /* initialize the endpoint zero */
@@ -88,8 +88,8 @@ void fhci_usb_enable_interrupt(struct fhci_usb *usb)
                enable_irq(fhci_to_hcd(fhci)->irq);
 
                /* initialize the event register and mask register */
-               out_be16(&usb->fhci->regs->usb_event, 0xffff);
-               out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+               out_be16(&usb->fhci->regs->usb_usber, 0xffff);
+               out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
 
                /* enable the timer interrupts */
                enable_irq(fhci->timer->irq);
@@ -109,7 +109,7 @@ void fhci_usb_disable_interrupt(struct fhci_usb *usb)
 
                /* disable the usb interrupt */
                disable_irq_nosync(fhci_to_hcd(fhci)->irq);
-               out_be16(&usb->fhci->regs->usb_mask, 0);
+               out_be16(&usb->fhci->regs->usb_usbmr, 0);
        }
        usb->intr_nesting_cnt++;
 }
@@ -119,9 +119,9 @@ static u32 fhci_usb_enable(struct fhci_hcd *fhci)
 {
        struct fhci_usb *usb = fhci->usb_lld;
 
-       out_be16(&usb->fhci->regs->usb_event, 0xffff);
-       out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
-       setbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
+       out_be16(&usb->fhci->regs->usb_usber, 0xffff);
+       out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
+       setbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN);
 
        mdelay(100);
 
@@ -141,7 +141,7 @@ static u32 fhci_usb_disable(struct fhci_hcd *fhci)
                        usb->port_status == FHCI_PORT_LOW)
                fhci_device_disconnected_interrupt(fhci);
 
-       clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
+       clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN);
 
        return 0;
 }
@@ -285,13 +285,13 @@ static int fhci_usb_init(struct fhci_hcd *fhci)
                          USB_E_IDLE_MASK |
                          USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK);
 
-       out_8(&usb->fhci->regs->usb_mod, USB_MODE_HOST | USB_MODE_EN);
+       out_8(&usb->fhci->regs->usb_usmod, USB_MODE_HOST | USB_MODE_EN);
 
        /* clearing the mask register */
-       out_be16(&usb->fhci->regs->usb_mask, 0);
+       out_be16(&usb->fhci->regs->usb_usbmr, 0);
 
        /* initialing the event register */
-       out_be16(&usb->fhci->regs->usb_event, 0xffff);
+       out_be16(&usb->fhci->regs->usb_usber, 0xffff);
 
        if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) {
                fhci_usb_free(usb);
@@ -745,8 +745,8 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev)
        }
 
        /* Clear and disable any pending interrupts. */
-       out_be16(&fhci->regs->usb_event, 0xffff);
-       out_be16(&fhci->regs->usb_mask, 0);
+       out_be16(&fhci->regs->usb_usber, 0xffff);
+       out_be16(&fhci->regs->usb_usbmr, 0);
 
        ret = usb_add_hcd(hcd, usb_irq, 0);
        if (ret < 0)
index 348fe62..6af2512 100644 (file)
@@ -97,7 +97,7 @@ void fhci_port_disable(struct fhci_hcd *fhci)
 
        /* Enable IDLE since we want to know if something comes along */
        usb->saved_msk |= USB_E_IDLE_MASK;
-       out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+       out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
 
        /* check if during the disconnection process attached new device */
        if (port_status == FHCI_PORT_WAITING)
@@ -158,21 +158,21 @@ void fhci_port_reset(void *lld)
 
        fhci_stop_sof_timer(fhci);
        /* disable the USB controller */
-       mode = in_8(&fhci->regs->usb_mod);
-       out_8(&fhci->regs->usb_mod, mode & (~USB_MODE_EN));
+       mode = in_8(&fhci->regs->usb_usmod);
+       out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN));
 
        /* disable idle interrupts */
-       mask = in_be16(&fhci->regs->usb_mask);
-       out_be16(&fhci->regs->usb_mask, mask & (~USB_E_IDLE_MASK));
+       mask = in_be16(&fhci->regs->usb_usbmr);
+       out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
 
        fhci_io_port_generate_reset(fhci);
 
        /* enable interrupt on this endpoint */
-       out_be16(&fhci->regs->usb_mask, mask);
+       out_be16(&fhci->regs->usb_usbmr, mask);
 
        /* enable the USB controller */
-       mode = in_8(&fhci->regs->usb_mod);
-       out_8(&fhci->regs->usb_mod, mode | USB_MODE_EN);
+       mode = in_8(&fhci->regs->usb_usmod);
+       out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN);
        fhci_start_sof_timer(fhci);
 
        fhci_dbg(fhci, "<- %s\n", __func__);
index 2df851b..2dc8a40 100644 (file)
@@ -132,8 +132,8 @@ void fhci_flush_all_transmissions(struct fhci_usb *usb)
        u8 mode;
        struct td *td;
 
-       mode = in_8(&usb->fhci->regs->usb_mod);
-       clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
+       mode = in_8(&usb->fhci->regs->usb_usmod);
+       clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN);
 
        fhci_flush_bds(usb);
 
@@ -147,9 +147,9 @@ void fhci_flush_all_transmissions(struct fhci_usb *usb)
        usb->actual_frame->frame_status = FRAME_END_TRANSMISSION;
 
        /* reset the event register */
-       out_be16(&usb->fhci->regs->usb_event, 0xffff);
+       out_be16(&usb->fhci->regs->usb_usber, 0xffff);
        /* enable the USB controller */
-       out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN);
+       out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN);
 }
 
 /*
@@ -414,7 +414,7 @@ static void sof_interrupt(struct fhci_hcd *fhci)
                        usb->port_status = FHCI_PORT_FULL;
                /* Disable IDLE */
                usb->saved_msk &= ~USB_E_IDLE_MASK;
-               out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+               out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
        }
 
        gtm_set_exact_timer16(fhci->timer, usb->max_frame_usage, false);
@@ -433,14 +433,14 @@ void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci)
        fhci_dbg(fhci, "-> %s\n", __func__);
 
        fhci_usb_disable_interrupt(usb);
-       clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+       clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS);
        usb->port_status = FHCI_PORT_DISABLED;
 
        fhci_stop_sof_timer(fhci);
 
        /* Enable IDLE since we want to know if something comes along */
        usb->saved_msk |= USB_E_IDLE_MASK;
-       out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+       out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
 
        usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION;
        usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION;
@@ -473,7 +473,7 @@ void fhci_device_connected_interrupt(struct fhci_hcd *fhci)
                }
 
                usb->port_status = FHCI_PORT_LOW;
-               setbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+               setbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS);
                usb->vroot_hub->port.wPortStatus |=
                    (USB_PORT_STAT_LOW_SPEED |
                     USB_PORT_STAT_CONNECTION);
@@ -491,7 +491,7 @@ void fhci_device_connected_interrupt(struct fhci_hcd *fhci)
                }
 
                usb->port_status = FHCI_PORT_FULL;
-               clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+               clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS);
                usb->vroot_hub->port.wPortStatus &=
                    ~USB_PORT_STAT_LOW_SPEED;
                usb->vroot_hub->port.wPortStatus |=
@@ -535,7 +535,7 @@ static void abort_transmission(struct fhci_usb *usb)
        /* issue stop Tx command */
        qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
        /* flush Tx FIFOs */
-       out_8(&usb->fhci->regs->usb_comm, USB_CMD_FLUSH_FIFO | EP_ZERO);
+       out_8(&usb->fhci->regs->usb_uscom, USB_CMD_FLUSH_FIFO | EP_ZERO);
        udelay(1000);
        /* reset Tx BDs */
        fhci_flush_bds(usb);
@@ -555,11 +555,11 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd)
 
        usb = fhci->usb_lld;
 
-       usb_er |= in_be16(&usb->fhci->regs->usb_event) &
-                 in_be16(&usb->fhci->regs->usb_mask);
+       usb_er |= in_be16(&usb->fhci->regs->usb_usber) &
+                 in_be16(&usb->fhci->regs->usb_usbmr);
 
        /* clear event bits for next time */
-       out_be16(&usb->fhci->regs->usb_event, usb_er);
+       out_be16(&usb->fhci->regs->usb_usber, usb_er);
 
        fhci_dbg_isr(fhci, usb_er);
 
@@ -573,7 +573,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd)
 
                        /* Turn on IDLE since we want to disconnect */
                        usb->saved_msk |= USB_E_IDLE_MASK;
-                       out_be16(&usb->fhci->regs->usb_event,
+                       out_be16(&usb->fhci->regs->usb_usber,
                                 usb->saved_msk);
                } else if (usb->port_status == FHCI_PORT_DISABLED) {
                        if (fhci_ioports_check_bus_state(fhci) == 1)
@@ -611,7 +611,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd)
                        /* XXX usb->port_status = FHCI_PORT_WAITING; */
                        /* Disable IDLE */
                        usb->saved_msk &= ~USB_E_IDLE_MASK;
-                       out_be16(&usb->fhci->regs->usb_mask,
+                       out_be16(&usb->fhci->regs->usb_usbmr,
                                 usb->saved_msk);
                } else {
                        fhci_dbg_isr(fhci, -1);
index c5ed881..1498061 100644 (file)
@@ -249,7 +249,7 @@ void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep,
        u8 rt;
 
        /* set the endpoint registers according to the endpoint */
-       out_be16(&usb->fhci->regs->usb_ep[0],
+       out_be16(&usb->fhci->regs->usb_usep[0],
                 USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE);
        out_be16(&usb->fhci->pram->ep_ptr[0],
                 cpm_muram_offset(ep->ep_pram_ptr));
@@ -463,7 +463,7 @@ u32 fhci_host_transaction(struct fhci_usb *usb,
        cq_put(&ep->conf_frame_Q, pkt);
 
        if (cq_howmany(&ep->conf_frame_Q) == 1)
-               out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO);
+               out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO);
 
        return 0;
 }
@@ -535,8 +535,8 @@ void fhci_flush_actual_frame(struct fhci_usb *usb)
        struct endpoint *ep = usb->ep0;
 
        /* disable the USB controller */
-       mode = in_8(&usb->fhci->regs->usb_mod);
-       out_8(&usb->fhci->regs->usb_mod, mode & ~USB_MODE_EN);
+       mode = in_8(&usb->fhci->regs->usb_usmod);
+       out_8(&usb->fhci->regs->usb_usmod, mode & ~USB_MODE_EN);
 
        tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
        td = cpm_muram_addr(tb_ptr);
@@ -571,9 +571,9 @@ void fhci_flush_actual_frame(struct fhci_usb *usb)
        usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
 
        /* reset the event register */
-       out_be16(&usb->fhci->regs->usb_event, 0xffff);
+       out_be16(&usb->fhci->regs->usb_usber, 0xffff);
        /* enable the USB controller */
-       out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN);
+       out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN);
 }
 
 /* handles Tx confirm and Tx error interrupt */
@@ -613,7 +613,7 @@ void fhci_host_transmit_actual_frame(struct fhci_usb *usb)
 
                /* start transmit only if we have something in the TDs */
                if (in_be16(&td->status) & TD_R)
-                       out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO);
+                       out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO);
 
                if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) {
                        out_be32(&old_td->buf_ptr, 0);
index dc6939a..7cc1c32 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 #include <asm/qe.h>
+#include <asm/immap_qe.h>
 
 #define USB_CLOCK      48000000
 
 #define USB_E_TXB_MASK         0x0002
 #define USB_E_RXB_MASK         0x0001
 
-/* Freescale USB Host controller registers */
-struct fhci_regs {
-       u8 usb_mod;             /* mode register */
-       u8 usb_addr;            /* address register */
-       u8 usb_comm;            /* command register */
-       u8 reserved1[1];
-       __be16 usb_ep[4];       /* endpoint register */
-       u8 reserved2[4];
-       __be16 usb_event;       /* event register */
-       u8 reserved3[2];
-       __be16 usb_mask;        /* mask register */
-       u8 reserved4[1];
-       u8 usb_status;          /* status register */
-       __be16 usb_sof_tmr;     /* Start Of Frame timer */
-       u8 reserved5[2];
-       __be16 usb_frame_num;   /* frame number register */
-       u8 reserved6[1];
-};
-
 /* Freescale USB HOST */
 struct fhci_pram {
        __be16 ep_ptr[4];       /* Endpoint porter reg */
@@ -267,7 +249,7 @@ struct fhci_hcd {
        int gpios[NUM_GPIOS];
        bool alow_gpios[NUM_GPIOS];
 
-       struct fhci_regs __iomem *regs; /* I/O memory used to communicate */
+       struct qe_usb_ctlr __iomem *regs; /* I/O memory used to communicate */
        struct fhci_pram __iomem *pram; /* Parameter RAM */
        struct gtm_timer *timer;
 
index ff471c1..f19e269 100644 (file)
@@ -1811,7 +1811,7 @@ static int imx21_remove(struct platform_device *pdev)
        usb_remove_hcd(hcd);
 
        if (res != NULL) {
-               clk_disable(imx21->clk);
+               clk_disable_unprepare(imx21->clk);
                clk_put(imx21->clk);
                iounmap(imx21->regs);
                release_mem_region(res->start, resource_size(res));
@@ -1884,7 +1884,7 @@ static int imx21_probe(struct platform_device *pdev)
        ret = clk_set_rate(imx21->clk, clk_round_rate(imx21->clk, 48000000));
        if (ret)
                goto failed_clock_set;
-       ret = clk_enable(imx21->clk);
+       ret = clk_prepare_enable(imx21->clk);
        if (ret)
                goto failed_clock_enable;
 
@@ -1900,7 +1900,7 @@ static int imx21_probe(struct platform_device *pdev)
        return 0;
 
 failed_add_hcd:
-       clk_disable(imx21->clk);
+       clk_disable_unprepare(imx21->clk);
 failed_clock_enable:
 failed_clock_set:
        clk_put(imx21->clk);
index 2909621..fc3091b 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <mach/ohci.h>
 #include <plat/usb-phy.h>
@@ -71,6 +72,8 @@ static const struct hc_driver exynos_ohci_hc_driver = {
        .start_port_reset       = ohci_start_port_reset,
 };
 
+static u64 ohci_exynos_dma_mask = DMA_BIT_MASK(32);
+
 static int __devinit exynos_ohci_probe(struct platform_device *pdev)
 {
        struct exynos4_ohci_platdata *pdata;
@@ -87,7 +90,18 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       exynos_ohci = kzalloc(sizeof(struct exynos_ohci_hcd), GFP_KERNEL);
+       /*
+        * Right now device-tree probed devices don't get dma_mask set.
+        * Since shared usb code relies on it, set it here for now.
+        * Once we move to full device tree support this will vanish off.
+        */
+       if (!pdev->dev.dma_mask)
+               pdev->dev.dma_mask = &ohci_exynos_dma_mask;
+       if (!pdev->dev.coherent_dma_mask)
+               pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+       exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd),
+                                       GFP_KERNEL);
        if (!exynos_ohci)
                return -ENOMEM;
 
@@ -97,8 +111,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev)
                                        dev_name(&pdev->dev));
        if (!hcd) {
                dev_err(&pdev->dev, "Unable to create HCD\n");
-               err = -ENOMEM;
-               goto fail_hcd;
+               return -ENOMEM;
        }
 
        exynos_ohci->hcd = hcd;
@@ -123,7 +136,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev)
 
        hcd->rsrc_start = res->start;
        hcd->rsrc_len = resource_size(res);
-       hcd->regs = ioremap(res->start, resource_size(res));
+       hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
        if (!hcd->regs) {
                dev_err(&pdev->dev, "Failed to remap I/O memory\n");
                err = -ENOMEM;
@@ -134,7 +147,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev)
        if (!irq) {
                dev_err(&pdev->dev, "Failed to get IRQ\n");
                err = -ENODEV;
-               goto fail;
+               goto fail_io;
        }
 
        if (pdata->phy_init)
@@ -146,23 +159,19 @@ static int __devinit exynos_ohci_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;
+               goto fail_io;
        }
 
        platform_set_drvdata(pdev, exynos_ohci);
 
        return 0;
 
-fail:
-       iounmap(hcd->regs);
 fail_io:
        clk_disable(exynos_ohci->clk);
 fail_clken:
        clk_put(exynos_ohci->clk);
 fail_clk:
        usb_put_hcd(hcd);
-fail_hcd:
-       kfree(exynos_ohci);
        return err;
 }
 
@@ -177,13 +186,10 @@ static int __devexit exynos_ohci_remove(struct platform_device *pdev)
        if (pdata && pdata->phy_exit)
                pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
 
-       iounmap(hcd->regs);
-
        clk_disable(exynos_ohci->clk);
        clk_put(exynos_ohci->clk);
 
        usb_put_hcd(hcd);
-       kfree(exynos_ohci);
 
        return 0;
 }
@@ -225,6 +231,9 @@ static int exynos_ohci_suspend(struct device *dev)
 
        if (pdata && pdata->phy_exit)
                pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
+
+       clk_disable(exynos_ohci->clk);
+
 fail:
        spin_unlock_irqrestore(&ohci->lock, flags);
 
@@ -238,6 +247,8 @@ static int exynos_ohci_resume(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
 
+       clk_enable(exynos_ohci->clk);
+
        if (pdata && pdata->phy_init)
                pdata->phy_init(pdev, S5P_USB_PHY_HOST);
 
@@ -258,6 +269,14 @@ static const struct dev_pm_ops exynos_ohci_pm_ops = {
        .resume         = exynos_ohci_resume,
 };
 
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_ohci_match[] = {
+       { .compatible = "samsung,exynos-ohci" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_ohci_match);
+#endif
+
 static struct platform_driver exynos_ohci_driver = {
        .probe          = exynos_ohci_probe,
        .remove         = __devexit_p(exynos_ohci_remove),
@@ -266,6 +285,7 @@ static struct platform_driver exynos_ohci_driver = {
                .name   = "exynos-ohci",
                .owner  = THIS_MODULE,
                .pm     = &exynos_ohci_pm_ops,
+               .of_match_table = of_match_ptr(exynos_ohci_match),
        }
 };
 
index 1e364ec..a446386 100644 (file)
 #define USB_HOST_NEED_CLK_EN   (1 << 21)
 #define PAD_CONTROL_LAST_DRIVEN        (1 << 19)
 
-#define USB_OTG_CLK_CTRL       IO_ADDRESS(USB_CONFIG_BASE + 0xFF4)
-#define USB_OTG_CLK_STAT       IO_ADDRESS(USB_CONFIG_BASE + 0xFF8)
-
-/* USB_OTG_CLK_CTRL bit defines */
-#define AHB_M_CLOCK_ON         (1 << 4)
-#define OTG_CLOCK_ON           (1 << 3)
-#define I2C_CLOCK_ON           (1 << 2)
-#define DEV_CLOCK_ON           (1 << 1)
-#define HOST_CLOCK_ON          (1 << 0)
-
 #define USB_OTG_STAT_CONTROL   IO_ADDRESS(USB_CONFIG_BASE + 0x110)
 
 /* USB_OTG_STAT_CONTROL bit defines */
@@ -72,7 +62,9 @@ static struct i2c_client *isp1301_i2c_client;
 
 extern int usb_disabled(void);
 
-static struct clk *usb_clk;
+static struct clk *usb_pll_clk;
+static struct clk *usb_dev_clk;
+static struct clk *usb_otg_clk;
 
 static void isp1301_configure_pnx4008(void)
 {
@@ -249,8 +241,6 @@ static const struct hc_driver ohci_nxp_hc_driver = {
        .start_port_reset = ohci_start_port_reset,
 };
 
-#define USB_CLOCK_MASK (AHB_M_CLOCK_ON| OTG_CLOCK_ON | HOST_CLOCK_ON | I2C_CLOCK_ON)
-
 static void nxp_set_usb_bits(void)
 {
        if (machine_is_pnx4008()) {
@@ -327,41 +317,63 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev)
        /* Enable AHB slave USB clock, needed for further USB clock control */
        __raw_writel(USB_SLAVE_HCLK_EN | PAD_CONTROL_LAST_DRIVEN, USB_CTRL);
 
-       isp1301_configure();
-
        /* Enable USB PLL */
-       usb_clk = clk_get(&pdev->dev, "ck_pll5");
-       if (IS_ERR(usb_clk)) {
+       usb_pll_clk = clk_get(&pdev->dev, "ck_pll5");
+       if (IS_ERR(usb_pll_clk)) {
                dev_err(&pdev->dev, "failed to acquire USB PLL\n");
-               ret = PTR_ERR(usb_clk);
+               ret = PTR_ERR(usb_pll_clk);
                goto out1;
        }
 
-       ret = clk_enable(usb_clk);
+       ret = clk_enable(usb_pll_clk);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to start USB PLL\n");
                goto out2;
        }
 
-       ret = clk_set_rate(usb_clk, 48000);
+       ret = clk_set_rate(usb_pll_clk, 48000);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to set USB clock rate\n");
                goto out3;
        }
 
+       /* Enable USB device clock */
+       usb_dev_clk = clk_get(&pdev->dev, "ck_usbd");
+       if (IS_ERR(usb_dev_clk)) {
+               dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n");
+               ret = PTR_ERR(usb_dev_clk);
+               goto out4;
+       }
+
+       ret = clk_enable(usb_dev_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to start USB DEV Clock\n");
+               goto out5;
+       }
+
+       /* Enable USB otg clocks */
+       usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
+       if (IS_ERR(usb_otg_clk)) {
+               dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n");
+               ret = PTR_ERR(usb_dev_clk);
+               goto out6;
+       }
+
        __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
 
-       /* Set to enable all needed USB clocks */
-       __raw_writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL);
+       ret = clk_enable(usb_otg_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to start USB DEV Clock\n");
+               goto out7;
+       }
 
-       while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) !=
-              USB_CLOCK_MASK) ;
+       isp1301_configure();
 
        hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
        if (!hcd) {
                dev_err(&pdev->dev, "Failed to allocate HC buffer\n");
                ret = -ENOMEM;
-               goto out3;
+               goto out8;
        }
 
        /* Set all USB bits in the Start Enable register */
@@ -371,14 +383,14 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev)
        if (!res) {
                dev_err(&pdev->dev, "Failed to get MEM resource\n");
                ret =  -ENOMEM;
-               goto out4;
+               goto out8;
        }
 
        hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
        if (!hcd->regs) {
                dev_err(&pdev->dev, "Failed to devm_request_and_ioremap\n");
                ret =  -ENOMEM;
-               goto out4;
+               goto out8;
        }
        hcd->rsrc_start = res->start;
        hcd->rsrc_len = resource_size(res);
@@ -386,7 +398,7 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev)
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                ret = -ENXIO;
-               goto out4;
+               goto out8;
        }
 
        nxp_start_hc();
@@ -400,13 +412,21 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev)
                return ret;
 
        nxp_stop_hc();
-out4:
+out8:
        nxp_unset_usb_bits();
        usb_put_hcd(hcd);
+out7:
+       clk_disable(usb_otg_clk);
+out6:
+       clk_put(usb_otg_clk);
+out5:
+       clk_disable(usb_dev_clk);
+out4:
+       clk_put(usb_dev_clk);
 out3:
-       clk_disable(usb_clk);
+       clk_disable(usb_pll_clk);
 out2:
-       clk_put(usb_clk);
+       clk_put(usb_pll_clk);
 out1:
        isp1301_i2c_client = NULL;
 out:
@@ -422,8 +442,10 @@ static int usb_hcd_nxp_remove(struct platform_device *pdev)
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
        usb_put_hcd(hcd);
        nxp_unset_usb_bits();
-       clk_disable(usb_clk);
-       clk_put(usb_clk);
+       clk_disable(usb_pll_clk);
+       clk_put(usb_pll_clk);
+       clk_disable(usb_dev_clk);
+       clk_put(usb_dev_clk);
        i2c_unregister_device(isp1301_i2c_client);
        isp1301_i2c_client = NULL;
 
index b02c344..e7d75d2 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/jiffies.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/gpio.h>
 
 #include <asm/io.h>
@@ -168,14 +169,15 @@ static int omap_1510_local_bus_init(void)
 
 static void start_hnp(struct ohci_hcd *ohci)
 {
-       const unsigned  port = ohci_to_hcd(ohci)->self.otg_port - 1;
+       struct usb_hcd *hcd = ohci_to_hcd(ohci);
+       const unsigned  port = hcd->self.otg_port - 1;
        unsigned long   flags;
        u32 l;
 
-       otg_start_hnp(ohci->transceiver->otg);
+       otg_start_hnp(hcd->phy->otg);
 
        local_irq_save(flags);
-       ohci->transceiver->state = OTG_STATE_A_SUSPEND;
+       hcd->phy->state = OTG_STATE_A_SUSPEND;
        writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]);
        l = omap_readl(OTG_CTRL);
        l &= ~OTG_A_BUSREQ;
@@ -212,18 +214,18 @@ static int ohci_omap_init(struct usb_hcd *hcd)
 
 #ifdef CONFIG_USB_OTG
        if (need_transceiver) {
-               ohci->transceiver = usb_get_transceiver();
-               if (ohci->transceiver) {
-                       int     status = otg_set_host(ohci->transceiver->otg,
+               hcd->phy = usb_get_phy(USB_PHY_TYPE_USB2);
+               if (!IS_ERR_OR_NULL(hcd->phy)) {
+                       int     status = otg_set_host(hcd->phy->otg,
                                                &ohci_to_hcd(ohci)->self);
-                       dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n",
-                                       ohci->transceiver->label, status);
+                       dev_dbg(hcd->self.controller, "init %s phy, status %d\n",
+                                       hcd->phy->label, status);
                        if (status) {
-                               usb_put_transceiver(ohci->transceiver);
+                               usb_put_phy(hcd->phy);
                                return status;
                        }
                } else {
-                       dev_err(hcd->self.controller, "can't find transceiver\n");
+                       dev_err(hcd->self.controller, "can't find phy\n");
                        return -ENODEV;
                }
                ohci->start_hnp = start_hnp;
@@ -404,9 +406,9 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
 
        usb_remove_hcd(hcd);
-       if (ohci->transceiver) {
-               (void) otg_set_host(ohci->transceiver->otg, 0);
-               usb_put_transceiver(ohci->transceiver);
+       if (!IS_ERR_OR_NULL(hcd->phy)) {
+               (void) otg_set_host(hcd->phy->otg, 0);
+               usb_put_phy(hcd->phy);
        }
        if (machine_is_omap_osk())
                gpio_free(9);
index 1b19aea..d329914 100644 (file)
@@ -372,11 +372,6 @@ struct ohci_hcd {
        struct ed               *ed_controltail;        /* last in ctrl list */
        struct ed               *periodic [NUM_INTS];   /* shadow int_table */
 
-       /*
-        * OTG controllers and transceivers need software interaction;
-        * other external transceivers should be software-transparent
-        */
-       struct usb_phy  *transceiver;
        void (*start_hnp)(struct ohci_hcd *ohci);
 
        /*
index 7b01094..74bfc86 100644 (file)
@@ -544,12 +544,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                if (hcd->speed != HCD_USB3)
                        goto error;
 
+               /* Set the U1 and U2 exit latencies. */
                memcpy(buf, &usb_bos_descriptor,
                                USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
                temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
                buf[12] = HCS_U1_LATENCY(temp);
                put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
 
+               /* Indicate whether the host has LTM support. */
+               temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+               if (HCC_LTC(temp))
+                       buf[8] |= USB_LTM_SUPPORT;
+
                spin_unlock_irqrestore(&xhci->lock, flags);
                return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
        case GetPortStatus:
index a979cd0..7648b2d 100644 (file)
@@ -4450,6 +4450,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 
        /* Accept arbitrarily long scatter-gather lists */
        hcd->self.sg_tablesize = ~0;
+       /* XHCI controllers don't stop the ep queue on short packets :| */
+       hcd->self.no_stop_on_short = 1;
 
        if (usb_hcd_is_primary_hcd(hcd)) {
                xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
index 9f3eda9..7a95ab8 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
@@ -364,8 +365,8 @@ static int am35x_musb_init(struct musb *musb)
                return -ENODEV;
 
        usb_nop_xceiv_register();
-       musb->xceiv = usb_get_transceiver();
-       if (!musb->xceiv)
+       musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(musb->xceiv))
                return -ENODEV;
 
        if (is_host_enabled(musb))
@@ -406,7 +407,7 @@ static int am35x_musb_exit(struct musb *musb)
        if (data->set_phy_power)
                data->set_phy_power(0);
 
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
        usb_nop_xceiv_unregister();
 
        return 0;
index a087ed6..428e6aa 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/list.h>
 #include <linux/gpio.h>
 #include <linux/io.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/prefetch.h>
@@ -415,8 +416,8 @@ static int bfin_musb_init(struct musb *musb)
        gpio_direction_output(musb->config->gpio_vrsel, 0);
 
        usb_nop_xceiv_register();
-       musb->xceiv = usb_get_transceiver();
-       if (!musb->xceiv) {
+       musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(musb->xceiv)) {
                gpio_free(musb->config->gpio_vrsel);
                return -ENODEV;
        }
@@ -440,7 +441,7 @@ static int bfin_musb_exit(struct musb *musb)
 {
        gpio_free(musb->config->gpio_vrsel);
 
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
        usb_nop_xceiv_unregister();
        return 0;
 }
index 8bd9566..0f9fcec 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
@@ -425,8 +426,8 @@ static int da8xx_musb_init(struct musb *musb)
                goto fail;
 
        usb_nop_xceiv_register();
-       musb->xceiv = usb_get_transceiver();
-       if (!musb->xceiv)
+       musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(musb->xceiv))
                goto fail;
 
        if (is_host_enabled(musb))
@@ -458,7 +459,7 @@ static int da8xx_musb_exit(struct musb *musb)
 
        phy_off();
 
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
        usb_nop_xceiv_unregister();
 
        return 0;
index 9d63ba4..472c8b4 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/list.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
@@ -385,8 +386,8 @@ static int davinci_musb_init(struct musb *musb)
        u32             revision;
 
        usb_nop_xceiv_register();
-       musb->xceiv = usb_get_transceiver();
-       if (!musb->xceiv)
+       musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(musb->xceiv))
                goto unregister;
 
        musb->mregs += DAVINCI_BASE_OFFSET;
@@ -444,7 +445,7 @@ static int davinci_musb_init(struct musb *musb)
        return 0;
 
 fail:
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
 unregister:
        usb_nop_xceiv_unregister();
        return -ENODEV;
@@ -494,7 +495,7 @@ static int davinci_musb_exit(struct musb *musb)
 
        phy_off();
 
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
        usb_nop_xceiv_unregister();
 
        return 0;
index db3dff8..26f1bef 100644 (file)
@@ -1909,7 +1909,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
        /* The musb_platform_init() call:
         *   - adjusts musb->mregs and musb->isr if needed,
         *   - may initialize an integrated tranceiver
-        *   - initializes musb->xceiv, usually by otg_get_transceiver()
+        *   - initializes musb->xceiv, usually by otg_get_phy()
         *   - stops powering VBUS
         *
         * There are various transceiver configurations.  Blackfin,
index f4a40f0..dbcdeea 100644 (file)
@@ -327,7 +327,6 @@ struct musb {
 
        irqreturn_t             (*isr)(int, void *);
        struct work_struct      irq_work;
-       struct work_struct      otg_notifier_work;
        u16                     hwvers;
 
 /* this hub status bit is reserved by USB 2.0 and not seen by usbcore */
@@ -373,7 +372,6 @@ struct musb {
        u16                     int_tx;
 
        struct usb_phy          *xceiv;
-       u8                      xceiv_event;
 
        int nIrq;
        unsigned                irq_wake:1;
index 23db42d..217808d 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
@@ -376,8 +377,8 @@ static int dsps_musb_init(struct musb *musb)
 
        /* NOP driver needs change if supporting dual instance */
        usb_nop_xceiv_register();
-       musb->xceiv = usb_get_transceiver();
-       if (!musb->xceiv)
+       musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(musb->xceiv))
                return -ENODEV;
 
        /* Returns zero if e.g. not clocked */
@@ -409,7 +410,7 @@ static int dsps_musb_init(struct musb *musb)
 
        return 0;
 err0:
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
        usb_nop_xceiv_unregister();
        return status;
 }
@@ -430,7 +431,7 @@ static int dsps_musb_exit(struct musb *musb)
                data->set_phy_power(0);
 
        /* NOP driver needs change if supporting dual instance */
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
        usb_nop_xceiv_unregister();
 
        return 0;
index 95918da..f7194cf 100644 (file)
@@ -328,6 +328,13 @@ static void txstate(struct musb *musb, struct musb_request *req)
 
        musb_ep = req->ep;
 
+       /* Check if EP is disabled */
+       if (!musb_ep->desc) {
+               dev_dbg(musb->controller, "ep:%s disabled - ignore request\n",
+                                               musb_ep->end_point.name);
+               return;
+       }
+
        /* we shouldn't get here while DMA is active ... but we do ... */
        if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) {
                dev_dbg(musb->controller, "dma pending...\n");
@@ -650,6 +657,13 @@ static void rxstate(struct musb *musb, struct musb_request *req)
 
        len = musb_ep->packet_sz;
 
+       /* Check if EP is disabled */
+       if (!musb_ep->desc) {
+               dev_dbg(musb->controller, "ep:%s disabled - ignore request\n",
+                                               musb_ep->end_point.name);
+               return;
+       }
+
        /* We shouldn't get here while DMA is active, but we do... */
        if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) {
                dev_dbg(musb->controller, "DMA pending...\n");
index e090c79..4bb717d 100644 (file)
@@ -1746,7 +1746,11 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                                c->channel_release(dma);
                                hw_ep->rx_channel = NULL;
                                dma = NULL;
-                               /* REVISIT reset CSR */
+                               val = musb_readw(epio, MUSB_RXCSR);
+                               val &= ~(MUSB_RXCSR_DMAENAB
+                                       | MUSB_RXCSR_H_AUTOREQ
+                                       | MUSB_RXCSR_AUTOCLEAR);
+                               musb_writew(epio, MUSB_RXCSR, val);
                        }
                }
 #endif /* Mentor DMA */
index c7785e8..5fdb9da 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <linux/err.h>
+#include <linux/usb/musb-omap.h>
 
 #include "musb_core.h"
 #include "omap2430.h"
 struct omap2430_glue {
        struct device           *dev;
        struct platform_device  *musb;
+       enum omap_musb_vbus_id_status status;
+       struct work_struct      omap_musb_mailbox_work;
 };
 #define glue_to_musb(g)                platform_get_drvdata(g->musb)
 
+struct omap2430_glue           *_glue;
+
 static struct timer_list musb_idle_timer;
 
 static void musb_do_idle(unsigned long _musb)
@@ -223,50 +228,63 @@ static inline void omap2430_low_level_init(struct musb *musb)
        musb_writel(musb->mregs, OTG_FORCESTDBY, l);
 }
 
-static int musb_otg_notifications(struct notifier_block *nb,
-               unsigned long event, void *unused)
+void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
 {
-       struct musb     *musb = container_of(nb, struct musb, nb);
+       struct omap2430_glue    *glue = _glue;
+       struct musb             *musb = glue_to_musb(glue);
 
-       musb->xceiv_event = event;
-       schedule_work(&musb->otg_notifier_work);
+       glue->status = status;
+       if (!musb) {
+               dev_err(glue->dev, "musb core is not yet ready\n");
+               return;
+       }
 
-       return NOTIFY_OK;
+       schedule_work(&glue->omap_musb_mailbox_work);
 }
+EXPORT_SYMBOL_GPL(omap_musb_mailbox);
 
-static void musb_otg_notifier_work(struct work_struct *data_notifier_work)
+static void omap_musb_set_mailbox(struct omap2430_glue *glue)
 {
-       struct musb *musb = container_of(data_notifier_work, struct musb, otg_notifier_work);
+       struct musb *musb = glue_to_musb(glue);
        struct device *dev = musb->controller;
        struct musb_hdrc_platform_data *pdata = dev->platform_data;
        struct omap_musb_board_data *data = pdata->board_data;
+       struct usb_otg *otg = musb->xceiv->otg;
 
-       switch (musb->xceiv_event) {
-       case USB_EVENT_ID:
-               dev_dbg(musb->controller, "ID GND\n");
+       switch (glue->status) {
+       case OMAP_MUSB_ID_GROUND:
+               dev_dbg(dev, "ID GND\n");
 
+               otg->default_a = true;
+               musb->xceiv->state = OTG_STATE_A_IDLE;
+               musb->xceiv->last_event = USB_EVENT_ID;
                if (!is_otg_enabled(musb) || musb->gadget_driver) {
-                       pm_runtime_get_sync(musb->controller);
+                       pm_runtime_get_sync(dev);
                        usb_phy_init(musb->xceiv);
                        omap2430_musb_set_vbus(musb, 1);
                }
                break;
 
-       case USB_EVENT_VBUS:
-               dev_dbg(musb->controller, "VBUS Connect\n");
+       case OMAP_MUSB_VBUS_VALID:
+               dev_dbg(dev, "VBUS Connect\n");
 
+               otg->default_a = false;
+               musb->xceiv->state = OTG_STATE_B_IDLE;
+               musb->xceiv->last_event = USB_EVENT_VBUS;
                if (musb->gadget_driver)
-                       pm_runtime_get_sync(musb->controller);
+                       pm_runtime_get_sync(dev);
                usb_phy_init(musb->xceiv);
                break;
 
-       case USB_EVENT_NONE:
-               dev_dbg(musb->controller, "VBUS Disconnect\n");
+       case OMAP_MUSB_ID_FLOAT:
+       case OMAP_MUSB_VBUS_OFF:
+               dev_dbg(dev, "VBUS Disconnect\n");
 
+               musb->xceiv->last_event = USB_EVENT_NONE;
                if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
                        if (musb->gadget_driver) {
-                               pm_runtime_mark_last_busy(musb->controller);
-                               pm_runtime_put_autosuspend(musb->controller);
+                               pm_runtime_mark_last_busy(dev);
+                               pm_runtime_put_autosuspend(dev);
                        }
 
                if (data->interface_type == MUSB_INTERFACE_UTMI) {
@@ -276,15 +294,24 @@ static void musb_otg_notifier_work(struct work_struct *data_notifier_work)
                usb_phy_shutdown(musb->xceiv);
                break;
        default:
-               dev_dbg(musb->controller, "ID float\n");
+               dev_dbg(dev, "ID float\n");
        }
 }
 
+
+static void omap_musb_mailbox_work(struct work_struct *mailbox_work)
+{
+       struct omap2430_glue *glue = container_of(mailbox_work,
+                               struct omap2430_glue, omap_musb_mailbox_work);
+       omap_musb_set_mailbox(glue);
+}
+
 static int omap2430_musb_init(struct musb *musb)
 {
        u32 l;
        int status = 0;
        struct device *dev = musb->controller;
+       struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
        struct musb_hdrc_platform_data *plat = dev->platform_data;
        struct omap_musb_board_data *data = plat->board_data;
 
@@ -292,14 +319,12 @@ static int omap2430_musb_init(struct musb *musb)
         * up through ULPI.  TWL4030-family PMICs include one,
         * which needs a driver, drivers aren't always needed.
         */
-       musb->xceiv = usb_get_transceiver();
-       if (!musb->xceiv) {
+       musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(musb->xceiv)) {
                pr_err("HS USB OTG: no transceiver configured\n");
                return -ENODEV;
        }
 
-       INIT_WORK(&musb->otg_notifier_work, musb_otg_notifier_work);
-
        status = pm_runtime_get_sync(dev);
        if (status < 0) {
                dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status);
@@ -326,14 +351,11 @@ static int omap2430_musb_init(struct musb *musb)
                        musb_readl(musb->mregs, OTG_INTERFSEL),
                        musb_readl(musb->mregs, OTG_SIMENABLE));
 
-       musb->nb.notifier_call = musb_otg_notifications;
-       status = usb_register_notifier(musb->xceiv, &musb->nb);
-
-       if (status)
-               dev_dbg(musb->controller, "notification register failed\n");
-
        setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
 
+       if (glue->status != OMAP_MUSB_UNKNOWN)
+               omap_musb_set_mailbox(glue);
+
        pm_runtime_put_noidle(musb->controller);
        return 0;
 
@@ -346,12 +368,13 @@ static void omap2430_musb_enable(struct musb *musb)
        u8              devctl;
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
        struct device *dev = musb->controller;
+       struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
        struct musb_hdrc_platform_data *pdata = dev->platform_data;
        struct omap_musb_board_data *data = pdata->board_data;
 
-       switch (musb->xceiv->last_event) {
+       switch (glue->status) {
 
-       case USB_EVENT_ID:
+       case OMAP_MUSB_ID_GROUND:
                usb_phy_init(musb->xceiv);
                if (data->interface_type != MUSB_INTERFACE_UTMI)
                        break;
@@ -370,7 +393,7 @@ static void omap2430_musb_enable(struct musb *musb)
                }
                break;
 
-       case USB_EVENT_VBUS:
+       case OMAP_MUSB_VBUS_VALID:
                usb_phy_init(musb->xceiv);
                break;
 
@@ -381,17 +404,18 @@ static void omap2430_musb_enable(struct musb *musb)
 
 static void omap2430_musb_disable(struct musb *musb)
 {
-       if (musb->xceiv->last_event)
+       struct device *dev = musb->controller;
+       struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
+
+       if (glue->status != OMAP_MUSB_UNKNOWN)
                usb_phy_shutdown(musb->xceiv);
 }
 
 static int omap2430_musb_exit(struct musb *musb)
 {
        del_timer_sync(&musb_idle_timer);
-       cancel_work_sync(&musb->otg_notifier_work);
 
        omap2430_low_level_exit(musb);
-       usb_put_transceiver(musb->xceiv);
 
        return 0;
 }
@@ -418,7 +442,7 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
        struct omap2430_glue            *glue;
        int                             ret = -ENOMEM;
 
-       glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+       glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
        if (!glue) {
                dev_err(&pdev->dev, "failed to allocate glue context\n");
                goto err0;
@@ -427,7 +451,7 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
        musb = platform_device_alloc("musb-hdrc", -1);
        if (!musb) {
                dev_err(&pdev->dev, "failed to allocate musb device\n");
-               goto err1;
+               goto err0;
        }
 
        musb->dev.parent                = &pdev->dev;
@@ -436,22 +460,31 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
 
        glue->dev                       = &pdev->dev;
        glue->musb                      = musb;
+       glue->status                    = OMAP_MUSB_UNKNOWN;
 
        pdata->platform_ops             = &omap2430_ops;
 
        platform_set_drvdata(pdev, glue);
 
+       /*
+        * REVISIT if we ever have two instances of the wrapper, we will be
+        * in big trouble
+        */
+       _glue   = glue;
+
+       INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work);
+
        ret = platform_device_add_resources(musb, pdev->resource,
                        pdev->num_resources);
        if (ret) {
                dev_err(&pdev->dev, "failed to add resources\n");
-               goto err2;
+               goto err1;
        }
 
        ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
        if (ret) {
                dev_err(&pdev->dev, "failed to add platform_data\n");
-               goto err2;
+               goto err1;
        }
 
        pm_runtime_enable(&pdev->dev);
@@ -459,16 +492,13 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
        ret = platform_device_add(musb);
        if (ret) {
                dev_err(&pdev->dev, "failed to register musb device\n");
-               goto err2;
+               goto err1;
        }
 
        return 0;
 
-err2:
-       platform_device_put(musb);
-
 err1:
-       kfree(glue);
+       platform_device_put(musb);
 
 err0:
        return ret;
@@ -478,9 +508,9 @@ static int __devexit omap2430_remove(struct platform_device *pdev)
 {
        struct omap2430_glue            *glue = platform_get_drvdata(pdev);
 
+       cancel_work_sync(&glue->omap_musb_mailbox_work);
        platform_device_del(glue->musb);
        platform_device_put(glue->musb);
-       kfree(glue);
 
        return 0;
 }
@@ -546,7 +576,7 @@ static int __init omap2430_init(void)
 {
        return platform_driver_register(&omap2430_driver);
 }
-module_init(omap2430_init);
+subsys_initcall(omap2430_init);
 
 static void __exit omap2430_exit(void)
 {
index de13559..1a1bd9c 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/prefetch.h>
 #include <linux/usb.h>
@@ -1078,8 +1079,8 @@ static int tusb_musb_init(struct musb *musb)
        int                     ret;
 
        usb_nop_xceiv_register();
-       musb->xceiv = usb_get_transceiver();
-       if (!musb->xceiv)
+       musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(musb->xceiv))
                return -ENODEV;
 
        pdev = to_platform_device(musb->controller);
@@ -1130,7 +1131,7 @@ done:
                if (sync)
                        iounmap(sync);
 
-               usb_put_transceiver(musb->xceiv);
+               usb_put_phy(musb->xceiv);
                usb_nop_xceiv_unregister();
        }
        return ret;
@@ -1146,7 +1147,7 @@ static int tusb_musb_exit(struct musb *musb)
 
        iounmap(musb->sync_va);
 
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
        usb_nop_xceiv_unregister();
        return 0;
 }
index aa09dd4..a8c0fad 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 
@@ -37,8 +38,8 @@ struct ux500_glue {
 
 static int ux500_musb_init(struct musb *musb)
 {
-       musb->xceiv = usb_get_transceiver();
-       if (!musb->xceiv) {
+       musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(musb->xceiv)) {
                pr_err("HS USB OTG: no transceiver configured\n");
                return -ENODEV;
        }
@@ -48,7 +49,7 @@ static int ux500_musb_init(struct musb *musb)
 
 static int ux500_musb_exit(struct musb *musb)
 {
-       usb_put_transceiver(musb->xceiv);
+       usb_put_phy(musb->xceiv);
 
        return 0;
 }
index 5c87db0..13fd1dd 100644 (file)
@@ -116,6 +116,16 @@ config FSL_USB2_OTG
        help
          Enable this to support Freescale USB OTG transceiver.
 
+config USB_MXS_PHY
+       tristate "Freescale MXS USB PHY support"
+       depends on ARCH_MXC || ARCH_MXS
+       select STMP_DEVICE
+       select USB_OTG_UTILS
+       help
+         Enable this to support the Freescale MXS USB PHY.
+
+         MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
+
 config USB_MV_OTG
        tristate "Marvell USB OTG support"
        depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND
index 41aa509..a844b8d 100644 (file)
@@ -20,4 +20,5 @@ obj-$(CONFIG_USB_MSM_OTG)     += msm_otg.o
 obj-$(CONFIG_AB8500_USB)       += ab8500-usb.o
 fsl_usb2_otg-objs              := fsl_otg.o otg_fsm.o
 obj-$(CONFIG_FSL_USB2_OTG)     += fsl_usb2_otg.o
+obj-$(CONFIG_USB_MXS_PHY)      += mxs-phy.o
 obj-$(CONFIG_USB_MV_OTG)       += mv_otg.o
index a84af67..ae8ad56 100644 (file)
@@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev)
        if (err < 0)
                goto fail0;
 
-       err = usb_set_transceiver(&ab->phy);
+       err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2);
        if (err) {
                dev_err(&pdev->dev, "Can't register transceiver\n");
                goto fail1;
@@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev)
 
        cancel_work_sync(&ab->phy_dis_work);
 
-       usb_set_transceiver(NULL);
+       usb_remove_phy(&ab->phy);
 
        ab8500_usb_host_phy_dis(ab);
        ab8500_usb_peri_phy_dis(ab);
index be4a63e..23c798c 100644 (file)
@@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev)
        fsl_otg_dev = fsl_otg_tc;
 
        /* Store the otg transceiver */
-       status = usb_set_transceiver(&fsl_otg_tc->phy);
+       status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2);
        if (status) {
                pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n");
                goto err;
@@ -824,7 +824,7 @@ err:
 int usb_otg_start(struct platform_device *pdev)
 {
        struct fsl_otg *p_otg;
-       struct usb_phy *otg_trans = usb_get_transceiver();
+       struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2);
        struct otg_fsm *fsm;
        int status;
        struct resource *res;
@@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev)
 {
        struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
 
-       usb_set_transceiver(NULL);
+       usb_remove_phy(&fsl_otg_dev->phy);
        free_irq(fsl_otg_dev->irq, fsl_otg_dev);
 
        iounmap((void *)usb_dr_regs);
index bde6298..a67ffe2 100644 (file)
@@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev)
        }
 
        /* only active when a gadget is registered */
-       err = usb_set_transceiver(&gpio_vbus->phy);
+       err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2);
        if (err) {
                dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
                        err);
@@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev)
        cancel_delayed_work_sync(&gpio_vbus->work);
        regulator_put(gpio_vbus->vbus_draw);
 
-       usb_set_transceiver(NULL);
+       usb_remove_phy(&gpio_vbus->phy);
 
        free_irq(gpio_vbus->irq, pdev);
        if (gpio_is_valid(pdata->gpio_pullup))
index e0558df..575fc81 100644 (file)
@@ -1336,9 +1336,6 @@ static int
 isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
 {
        struct isp1301  *isp = container_of(otg->phy, struct isp1301, phy);
-#ifndef        CONFIG_USB_OTG
-       u32 l;
-#endif
 
        if (!otg || isp != the_transceiver)
                return -ENODEV;
@@ -1365,10 +1362,14 @@ isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
        otg->gadget = gadget;
        // FIXME update its refcount
 
-       l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
-       l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS);
-       l |= OTG_ID;
-       omap_writel(l, OTG_CTRL);
+       {
+               u32 l;
+
+               l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+               l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS);
+               l |= OTG_ID;
+               omap_writel(l, OTG_CTRL);
+       }
 
        power_up(isp);
        isp->phy.state = OTG_STATE_B_IDLE;
@@ -1610,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
        dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES);
 #endif
 
-       status = usb_set_transceiver(&isp->phy);
+       status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2);
        if (status < 0)
                dev_err(&i2c->dev, "can't register transceiver, %d\n",
                        status);
@@ -1649,7 +1650,7 @@ subsys_initcall(isp_init);
 static void __exit isp_exit(void)
 {
        if (the_transceiver)
-               usb_set_transceiver(NULL);
+               usb_remove_phy(&the_transceiver->phy);
        i2c_del_driver(&isp1301_driver);
 }
 module_exit(isp_exit);
index 1d0347c..9f5fc90 100644 (file)
@@ -1555,9 +1555,9 @@ static int __init msm_otg_probe(struct platform_device *pdev)
        phy->otg->set_host = msm_otg_set_host;
        phy->otg->set_peripheral = msm_otg_set_peripheral;
 
-       ret = usb_set_transceiver(&motg->phy);
+       ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2);
        if (ret) {
-               dev_err(&pdev->dev, "usb_set_transceiver failed\n");
+               dev_err(&pdev->dev, "usb_add_phy failed\n");
                goto free_irq;
        }
 
@@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev)
        device_init_wakeup(&pdev->dev, 0);
        pm_runtime_disable(&pdev->dev);
 
-       usb_set_transceiver(NULL);
+       usb_remove_phy(phy);
        free_irq(motg->irq, motg);
 
        /*
index 6cc6c3f..3f124e8 100644 (file)
@@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev)
        for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++)
                clk_put(mvotg->clk[clk_i]);
 
-       usb_set_transceiver(NULL);
+       usb_remove_phy(&mvotg->phy);
        platform_set_drvdata(pdev, NULL);
 
        kfree(mvotg->phy.otg);
@@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev)
                goto err_disable_clk;
        }
 
-       retval = usb_set_transceiver(&mvotg->phy);
+       retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2);
        if (retval < 0) {
                dev_err(&pdev->dev, "can't register transceiver, %d\n",
                        retval);
@@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev)
        return 0;
 
 err_set_transceiver:
-       usb_set_transceiver(NULL);
+       usb_remove_phy(&mvotg->phy);
 err_free_irq:
        free_irq(mvotg->irq, mvotg);
 err_disable_clk:
diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c
new file mode 100644 (file)
index 0000000..c1a67cb
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright (C) 2012 Marek Vasut <marex@denx.de>
+ * on behalf of DENX Software Engineering GmbH
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/usb/otg.h>
+#include <linux/stmp_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#define DRIVER_NAME "mxs_phy"
+
+#define HW_USBPHY_PWD                          0x00
+#define HW_USBPHY_CTRL                         0x30
+#define HW_USBPHY_CTRL_SET                     0x34
+#define HW_USBPHY_CTRL_CLR                     0x38
+
+#define BM_USBPHY_CTRL_SFTRST                  BIT(31)
+#define BM_USBPHY_CTRL_CLKGATE                 BIT(30)
+#define BM_USBPHY_CTRL_ENUTMILEVEL3            BIT(15)
+#define BM_USBPHY_CTRL_ENUTMILEVEL2            BIT(14)
+#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT      BIT(1)
+
+struct mxs_phy {
+       struct usb_phy phy;
+       struct clk *clk;
+};
+
+#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
+
+static void mxs_phy_hw_init(struct mxs_phy *mxs_phy)
+{
+       void __iomem *base = mxs_phy->phy.io_priv;
+
+       stmp_reset_block(base + HW_USBPHY_CTRL);
+
+       /* Power up the PHY */
+       writel_relaxed(0, base + HW_USBPHY_PWD);
+
+       /* enable FS/LS device */
+       writel_relaxed(BM_USBPHY_CTRL_ENUTMILEVEL2 |
+                       BM_USBPHY_CTRL_ENUTMILEVEL3,
+                       base + HW_USBPHY_CTRL_SET);
+}
+
+static int mxs_phy_init(struct usb_phy *phy)
+{
+       struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
+       clk_prepare_enable(mxs_phy->clk);
+       mxs_phy_hw_init(mxs_phy);
+
+       return 0;
+}
+
+static void mxs_phy_shutdown(struct usb_phy *phy)
+{
+       struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
+       writel_relaxed(BM_USBPHY_CTRL_CLKGATE,
+                       phy->io_priv + HW_USBPHY_CTRL_SET);
+
+       clk_disable_unprepare(mxs_phy->clk);
+}
+
+static int mxs_phy_on_connect(struct usb_phy *phy, int port)
+{
+       dev_dbg(phy->dev, "Connect on port %d\n", port);
+
+       mxs_phy_hw_init(to_mxs_phy(phy));
+       writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+                       phy->io_priv + HW_USBPHY_CTRL_SET);
+
+       return 0;
+}
+
+static int mxs_phy_on_disconnect(struct usb_phy *phy, int port)
+{
+       dev_dbg(phy->dev, "Disconnect on port %d\n", port);
+
+       writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+                       phy->io_priv + HW_USBPHY_CTRL_CLR);
+
+       return 0;
+}
+
+static int mxs_phy_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       void __iomem *base;
+       struct clk *clk;
+       struct mxs_phy *mxs_phy;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "can't get device resources\n");
+               return -ENOENT;
+       }
+
+       base = devm_request_and_ioremap(&pdev->dev, res);
+       if (!base)
+               return -EBUSY;
+
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev,
+                       "can't get the clock, err=%ld", PTR_ERR(clk));
+               return PTR_ERR(clk);
+       }
+
+       mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
+       if (!mxs_phy) {
+               dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n");
+               return -ENOMEM;
+       }
+
+       mxs_phy->phy.io_priv            = base;
+       mxs_phy->phy.dev                = &pdev->dev;
+       mxs_phy->phy.label              = DRIVER_NAME;
+       mxs_phy->phy.init               = mxs_phy_init;
+       mxs_phy->phy.shutdown           = mxs_phy_shutdown;
+       mxs_phy->phy.notify_connect     = mxs_phy_on_connect;
+       mxs_phy->phy.notify_disconnect  = mxs_phy_on_disconnect;
+
+       ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier);
+
+       mxs_phy->clk = clk;
+
+       platform_set_drvdata(pdev, &mxs_phy->phy);
+
+       return 0;
+}
+
+static int __devexit mxs_phy_remove(struct platform_device *pdev)
+{
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static const struct of_device_id mxs_phy_dt_ids[] = {
+       { .compatible = "fsl,imx23-usbphy", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
+
+static struct platform_driver mxs_phy_driver = {
+       .probe = mxs_phy_probe,
+       .remove = __devexit_p(mxs_phy_remove),
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = mxs_phy_dt_ids,
+        },
+};
+
+static int __init mxs_phy_module_init(void)
+{
+       return platform_driver_register(&mxs_phy_driver);
+}
+postcore_initcall(mxs_phy_module_init);
+
+static void __exit mxs_phy_module_exit(void)
+{
+       platform_driver_unregister(&mxs_phy_driver);
+}
+module_exit(mxs_phy_module_exit);
+
+MODULE_ALIAS("platform:mxs-usb-phy");
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
+MODULE_DESCRIPTION("Freescale MXS USB PHY driver");
+MODULE_LICENSE("GPL");
index 58b26df..803f958 100644 (file)
@@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
        nop->phy.otg->set_host          = nop_set_host;
        nop->phy.otg->set_peripheral    = nop_set_peripheral;
 
-       err = usb_set_transceiver(&nop->phy);
+       err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2);
        if (err) {
                dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
                        err);
@@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev)
 {
        struct nop_usb_xceiv *nop = platform_get_drvdata(pdev);
 
-       usb_set_transceiver(NULL);
+       usb_remove_phy(&nop->phy);
 
        platform_set_drvdata(pdev, NULL);
        kfree(nop->phy.otg);
index 801e597..1bf60a2 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/export.h>
+#include <linux/err.h>
 #include <linux/device.h>
+#include <linux/slab.h>
 
 #include <linux/usb/otg.h>
 
-static struct usb_phy *phy;
+static LIST_HEAD(phy_list);
+static DEFINE_SPINLOCK(phy_lock);
+
+static struct usb_phy *__usb_find_phy(struct list_head *list,
+       enum usb_phy_type type)
+{
+       struct usb_phy  *phy = NULL;
+
+       list_for_each_entry(phy, list, head) {
+               if (phy->type != type)
+                       continue;
+
+               return phy;
+       }
+
+       return ERR_PTR(-ENODEV);
+}
+
+static void devm_usb_phy_release(struct device *dev, void *res)
+{
+       struct usb_phy *phy = *(struct usb_phy **)res;
+
+       usb_put_phy(phy);
+}
+
+static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
+{
+       return res == match_data;
+}
 
 /**
- * usb_get_transceiver - find the (single) USB transceiver
+ * devm_usb_get_phy - find the USB PHY
+ * @dev - device that requests this phy
+ * @type - the type of the phy the controller requires
  *
- * Returns the transceiver driver, after getting a refcount to it; or
- * null if there is no such transceiver.  The caller is responsible for
- * calling usb_put_transceiver() to release that count.
+ * Gets the phy using usb_get_phy(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
  *
  * For use by USB host and peripheral drivers.
  */
-struct usb_phy *usb_get_transceiver(void)
+struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type)
 {
-       if (phy)
-               get_device(phy->dev);
+       struct usb_phy **ptr, *phy;
+
+       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;
+               devres_add(dev, ptr);
+       } else
+               devres_free(ptr);
+
+       return phy;
+}
+EXPORT_SYMBOL(devm_usb_get_phy);
+
+/**
+ * usb_get_phy - find the USB PHY
+ * @type - the type of the phy the controller requires
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy.  The caller is responsible for
+ * calling usb_put_phy() to release that count.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *usb_get_phy(enum usb_phy_type type)
+{
+       struct usb_phy  *phy = NULL;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&phy_lock, flags);
+
+       phy = __usb_find_phy(&phy_list, type);
+       if (IS_ERR(phy)) {
+               pr_err("unable to find transceiver of type %s\n",
+                       usb_phy_type_string(type));
+               goto err0;
+       }
+
+       get_device(phy->dev);
+
+err0:
+       spin_unlock_irqrestore(&phy_lock, flags);
+
        return phy;
 }
-EXPORT_SYMBOL(usb_get_transceiver);
+EXPORT_SYMBOL(usb_get_phy);
+
+/**
+ * devm_usb_put_phy - release the USB PHY
+ * @dev - device that wants to release this phy
+ * @phy - the phy returned by devm_usb_get_phy()
+ *
+ * destroys the devres associated with this phy and invokes usb_put_phy
+ * to release the phy.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+void devm_usb_put_phy(struct device *dev, struct usb_phy *phy)
+{
+       int r;
+
+       r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL(devm_usb_put_phy);
 
 /**
- * usb_put_transceiver - release the (single) USB transceiver
- * @x: the transceiver returned by usb_get_transceiver()
+ * usb_put_phy - release the USB PHY
+ * @x: the phy returned by usb_get_phy()
  *
- * Releases a refcount the caller received from usb_get_transceiver().
+ * Releases a refcount the caller received from usb_get_phy().
  *
  * For use by USB host and peripheral drivers.
  */
-void usb_put_transceiver(struct usb_phy *x)
+void usb_put_phy(struct usb_phy *x)
 {
        if (x)
                put_device(x->dev);
 }
-EXPORT_SYMBOL(usb_put_transceiver);
+EXPORT_SYMBOL(usb_put_phy);
 
 /**
- * usb_set_transceiver - declare the (single) USB transceiver
- * @x: the USB transceiver to be used; or NULL
+ * usb_add_phy - declare the USB PHY
+ * @x: the USB phy to be used; or NULL
+ * @type - the type of this PHY
  *
- * This call is exclusively for use by transceiver drivers, which
+ * This call is exclusively for use by phy drivers, which
  * coordinate the activities of drivers for host and peripheral
  * controllers, and in some cases for VBUS current regulation.
  */
-int usb_set_transceiver(struct usb_phy *x)
+int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
+{
+       int             ret = 0;
+       unsigned long   flags;
+       struct usb_phy  *phy;
+
+       if (x && x->type != USB_PHY_TYPE_UNDEFINED) {
+               dev_err(x->dev, "not accepting initialized PHY %s\n", x->label);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&phy_lock, flags);
+
+       list_for_each_entry(phy, &phy_list, head) {
+               if (phy->type == type) {
+                       ret = -EBUSY;
+                       dev_err(x->dev, "transceiver type %s already exists\n",
+                                               usb_phy_type_string(type));
+                       goto out;
+               }
+       }
+
+       x->type = type;
+       list_add_tail(&x->head, &phy_list);
+
+out:
+       spin_unlock_irqrestore(&phy_lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL(usb_add_phy);
+
+/**
+ * usb_remove_phy - remove the OTG PHY
+ * @x: the USB OTG PHY to be removed;
+ *
+ * This reverts the effects of usb_add_phy
+ */
+void usb_remove_phy(struct usb_phy *x)
 {
-       if (phy && x)
-               return -EBUSY;
-       phy = x;
-       return 0;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&phy_lock, flags);
+       if (x)
+               list_del(&x->head);
+       spin_unlock_irqrestore(&phy_lock, flags);
 }
-EXPORT_SYMBOL(usb_set_transceiver);
+EXPORT_SYMBOL(usb_remove_phy);
 
 const char *otg_state_string(enum usb_otg_state state)
 {
index c4a86da..523cad5 100644 (file)
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/usb/otg.h>
+#include <linux/usb/musb-omap.h>
 #include <linux/usb/ulpi.h>
 #include <linux/i2c/twl.h>
 #include <linux/regulator/consumer.h>
 #include <linux/err.h>
-#include <linux/notifier.h>
 #include <linux/slab.h>
 
 /* Register defines */
@@ -159,7 +159,7 @@ struct twl4030_usb {
        enum twl4030_usb_mode   usb_mode;
 
        int                     irq;
-       u8                      linkstat;
+       enum omap_musb_vbus_id_status linkstat;
        bool                    vbus_supplied;
        u8                      asleep;
        bool                    irq_enabled;
@@ -246,11 +246,11 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
 
 /*-------------------------------------------------------------------------*/
 
-static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl)
+static enum omap_musb_vbus_id_status
+       twl4030_usb_linkstat(struct twl4030_usb *twl)
 {
        int     status;
-       int     linkstat = USB_EVENT_NONE;
-       struct usb_otg *otg = twl->phy.otg;
+       enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
 
        twl->vbus_supplied = false;
 
@@ -273,30 +273,23 @@ static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl)
                         twl->vbus_supplied = true;
 
                if (status & BIT(2))
-                       linkstat = USB_EVENT_ID;
+                       linkstat = OMAP_MUSB_ID_GROUND;
                else
-                       linkstat = USB_EVENT_VBUS;
-       } else
-               linkstat = USB_EVENT_NONE;
+                       linkstat = OMAP_MUSB_VBUS_VALID;
+       } else {
+               if (twl->linkstat != OMAP_MUSB_UNKNOWN)
+                       linkstat = OMAP_MUSB_VBUS_OFF;
+       }
 
        dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
                        status, status, linkstat);
 
-       twl->phy.last_event = linkstat;
-
        /* REVISIT this assumes host and peripheral controllers
         * are registered, and that both are active...
         */
 
        spin_lock_irq(&twl->lock);
        twl->linkstat = linkstat;
-       if (linkstat == USB_EVENT_ID) {
-               otg->default_a = true;
-               twl->phy.state = OTG_STATE_A_IDLE;
-       } else {
-               otg->default_a = false;
-               twl->phy.state = OTG_STATE_B_IDLE;
-       }
        spin_unlock_irq(&twl->lock);
 
        return linkstat;
@@ -501,10 +494,10 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
 static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
 {
        struct twl4030_usb *twl = _twl;
-       int status;
+       enum omap_musb_vbus_id_status status;
 
        status = twl4030_usb_linkstat(twl);
-       if (status >= 0) {
+       if (status > 0) {
                /* FIXME add a set_power() method so that B-devices can
                 * configure the charger appropriately.  It's not always
                 * correct to consume VBUS power, and how much current to
@@ -516,13 +509,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
                 * USB_LINK_VBUS state.  musb_hdrc won't care until it
                 * starts to handle softconnect right.
                 */
-               if (status == USB_EVENT_NONE)
+               if (status == OMAP_MUSB_VBUS_OFF ||
+                               status == OMAP_MUSB_ID_FLOAT)
                        twl4030_phy_suspend(twl, 0);
                else
                        twl4030_phy_resume(twl);
 
-               atomic_notifier_call_chain(&twl->phy.notifier, status,
-                               twl->phy.otg->gadget);
+               omap_musb_mailbox(twl->linkstat);
        }
        sysfs_notify(&twl->dev->kobj, NULL, "vbus");
 
@@ -531,11 +524,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
 
 static void twl4030_usb_phy_init(struct twl4030_usb *twl)
 {
-       int status;
+       enum omap_musb_vbus_id_status status;
 
        status = twl4030_usb_linkstat(twl);
-       if (status >= 0) {
-               if (status == USB_EVENT_NONE) {
+       if (status > 0) {
+               if (status == OMAP_MUSB_VBUS_OFF ||
+                               status == OMAP_MUSB_ID_FLOAT) {
                        __twl4030_phy_power(twl, 0);
                        twl->asleep = 1;
                } else {
@@ -543,8 +537,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl)
                        twl->asleep = 0;
                }
 
-               atomic_notifier_call_chain(&twl->phy.notifier, status,
-                               twl->phy.otg->gadget);
+               omap_musb_mailbox(twl->linkstat);
        }
        sysfs_notify(&twl->dev->kobj, NULL, "vbus");
 }
@@ -598,21 +591,20 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       twl = kzalloc(sizeof *twl, GFP_KERNEL);
+       twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
        if (!twl)
                return -ENOMEM;
 
-       otg = kzalloc(sizeof *otg, GFP_KERNEL);
-       if (!otg) {
-               kfree(twl);
+       otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
+       if (!otg)
                return -ENOMEM;
-       }
 
        twl->dev                = &pdev->dev;
        twl->irq                = platform_get_irq(pdev, 0);
        twl->usb_mode           = pdata->usb_mode;
        twl->vbus_supplied      = false;
        twl->asleep             = 1;
+       twl->linkstat           = OMAP_MUSB_UNKNOWN;
 
        twl->phy.dev            = twl->dev;
        twl->phy.label          = "twl4030";
@@ -629,18 +621,14 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
        err = twl4030_usb_ldo_init(twl);
        if (err) {
                dev_err(&pdev->dev, "ldo init failed\n");
-               kfree(otg);
-               kfree(twl);
                return err;
        }
-       usb_set_transceiver(&twl->phy);
+       usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2);
 
        platform_set_drvdata(pdev, twl);
        if (device_create_file(&pdev->dev, &dev_attr_vbus))
                dev_warn(&pdev->dev, "could not create sysfs file\n");
 
-       ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
-
        /* Our job is to use irqs and status from the power module
         * to keep the transceiver disabled when nothing's connected.
         *
@@ -651,13 +639,11 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
         */
        twl->irq_enabled = true;
        status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq,
-                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                       "twl4030_usb", twl);
+                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+                       IRQF_ONESHOT, "twl4030_usb", twl);
        if (status < 0) {
                dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
                        twl->irq, status);
-               kfree(otg);
-               kfree(twl);
                return status;
        }
 
@@ -701,9 +687,6 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
        regulator_put(twl->usb1v8);
        regulator_put(twl->usb3v1);
 
-       kfree(twl->phy.otg);
-       kfree(twl);
-
        return 0;
 }
 
index 0eabb04..6907d8d 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/usb/otg.h>
+#include <linux/usb/musb-omap.h>
 #include <linux/i2c/twl.h>
 #include <linux/regulator/consumer.h>
 #include <linux/err.h>
-#include <linux/notifier.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 
@@ -100,7 +100,7 @@ struct twl6030_usb {
 
        int                     irq1;
        int                     irq2;
-       u8                      linkstat;
+       enum omap_musb_vbus_id_status linkstat;
        u8                      asleep;
        bool                    irq_enabled;
        bool                    vbus_enable;
@@ -147,7 +147,7 @@ static int twl6030_phy_init(struct usb_phy *x)
        dev  = twl->dev;
        pdata = dev->platform_data;
 
-       if (twl->linkstat == USB_EVENT_ID)
+       if (twl->linkstat == OMAP_MUSB_ID_GROUND)
                pdata->phy_power(twl->dev, 1, 1);
        else
                pdata->phy_power(twl->dev, 0, 1);
@@ -235,13 +235,13 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev,
        spin_lock_irqsave(&twl->lock, flags);
 
        switch (twl->linkstat) {
-       case USB_EVENT_VBUS:
+       case OMAP_MUSB_VBUS_VALID:
               ret = snprintf(buf, PAGE_SIZE, "vbus\n");
               break;
-       case USB_EVENT_ID:
+       case OMAP_MUSB_ID_GROUND:
               ret = snprintf(buf, PAGE_SIZE, "id\n");
               break;
-       case USB_EVENT_NONE:
+       case OMAP_MUSB_VBUS_OFF:
               ret = snprintf(buf, PAGE_SIZE, "none\n");
               break;
        default:
@@ -256,8 +256,7 @@ static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
 static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
 {
        struct twl6030_usb *twl = _twl;
-       struct usb_otg *otg = twl->phy.otg;
-       int status;
+       enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
        u8 vbus_state, hw_state;
 
        hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
@@ -268,22 +267,18 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
                if (vbus_state & VBUS_DET) {
                        regulator_enable(twl->usb3v3);
                        twl->asleep = 1;
-                       status = USB_EVENT_VBUS;
-                       otg->default_a = false;
-                       twl->phy.state = OTG_STATE_B_IDLE;
+                       status = OMAP_MUSB_VBUS_VALID;
                        twl->linkstat = status;
-                       twl->phy.last_event = status;
-                       atomic_notifier_call_chain(&twl->phy.notifier,
-                                               status, otg->gadget);
+                       omap_musb_mailbox(status);
                } else {
-                       status = USB_EVENT_NONE;
-                       twl->linkstat = status;
-                       twl->phy.last_event = status;
-                       atomic_notifier_call_chain(&twl->phy.notifier,
-                                               status, otg->gadget);
-                       if (twl->asleep) {
-                               regulator_disable(twl->usb3v3);
-                               twl->asleep = 0;
+                       if (twl->linkstat != OMAP_MUSB_UNKNOWN) {
+                               status = OMAP_MUSB_VBUS_OFF;
+                               twl->linkstat = status;
+                               omap_musb_mailbox(status);
+                               if (twl->asleep) {
+                                       regulator_disable(twl->usb3v3);
+                                       twl->asleep = 0;
+                               }
                        }
                }
        }
@@ -295,8 +290,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
 static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
 {
        struct twl6030_usb *twl = _twl;
-       struct usb_otg *otg = twl->phy.otg;
-       int status = USB_EVENT_NONE;
+       enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
        u8 hw_state;
 
        hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
@@ -307,13 +301,9 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
                twl->asleep = 1;
                twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR);
                twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET);
-               status = USB_EVENT_ID;
-               otg->default_a = true;
-               twl->phy.state = OTG_STATE_A_IDLE;
+               status = OMAP_MUSB_ID_GROUND;
                twl->linkstat = status;
-               twl->phy.last_event = status;
-               atomic_notifier_call_chain(&twl->phy.notifier, status,
-                                                       otg->gadget);
+               omap_musb_mailbox(status);
        } else  {
                twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR);
                twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET);
@@ -402,20 +392,19 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        pdata = dev->platform_data;
 
-       twl = kzalloc(sizeof *twl, GFP_KERNEL);
+       twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL);
        if (!twl)
                return -ENOMEM;
 
-       otg = kzalloc(sizeof *otg, GFP_KERNEL);
-       if (!otg) {
-               kfree(twl);
+       otg = devm_kzalloc(dev, sizeof *otg, GFP_KERNEL);
+       if (!otg)
                return -ENOMEM;
-       }
 
        twl->dev                = &pdev->dev;
        twl->irq1               = platform_get_irq(pdev, 0);
        twl->irq2               = platform_get_irq(pdev, 1);
        twl->features           = pdata->features;
+       twl->linkstat           = OMAP_MUSB_UNKNOWN;
 
        twl->phy.dev            = twl->dev;
        twl->phy.label          = "twl6030";
@@ -436,18 +425,14 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
        err = twl6030_usb_ldo_init(twl);
        if (err) {
                dev_err(&pdev->dev, "ldo init failed\n");
-               kfree(otg);
-               kfree(twl);
                return err;
        }
-       usb_set_transceiver(&twl->phy);
+       usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2);
 
        platform_set_drvdata(pdev, twl);
        if (device_create_file(&pdev->dev, &dev_attr_vbus))
                dev_warn(&pdev->dev, "could not create sysfs file\n");
 
-       ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
-
        INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);
 
        twl->irq_enabled = true;
@@ -458,8 +443,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
                        twl->irq1, status);
                device_remove_file(twl->dev, &dev_attr_vbus);
-               kfree(otg);
-               kfree(twl);
                return status;
        }
 
@@ -471,8 +454,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
                        twl->irq2, status);
                free_irq(twl->irq1, twl);
                device_remove_file(twl->dev, &dev_attr_vbus);
-               kfree(otg);
-               kfree(twl);
                return status;
        }
 
@@ -503,8 +484,6 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
        pdata->phy_exit(twl->dev);
        device_remove_file(twl->dev, &dev_attr_vbus);
        cancel_work_sync(&twl->set_vbus_work);
-       kfree(twl->phy.otg);
-       kfree(twl);
 
        return 0;
 }
index 30b757a..ecd1730 100644 (file)
@@ -771,22 +771,15 @@ static void xfer_work(struct work_struct *work)
        struct usbhs_pipe *pipe = pkt->pipe;
        struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
        struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
-       struct scatterlist sg;
        struct dma_async_tx_descriptor *desc;
        struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
        struct device *dev = usbhs_priv_to_dev(priv);
        enum dma_transfer_direction dir;
-       dma_cookie_t cookie;
 
        dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
 
-       sg_init_table(&sg, 1);
-       sg_set_page(&sg, virt_to_page(pkt->dma),
-                   pkt->length, offset_in_page(pkt->dma));
-       sg_dma_address(&sg) = pkt->dma + pkt->actual;
-       sg_dma_len(&sg) = pkt->trans;
-
-       desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir,
+       desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual,
+                                       pkt->trans, dir,
                                        DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc)
                return;
@@ -794,8 +787,7 @@ static void xfer_work(struct work_struct *work)
        desc->callback          = usbhsf_dma_complete;
        desc->callback_param    = pipe;
 
-       cookie = desc->tx_submit(desc);
-       if (cookie < 0) {
+       if (dmaengine_submit(desc) < 0) {
                dev_err(dev, "Failed to submit dma descriptor\n");
                return;
        }
index a1b9924..af0b70e 100644 (file)
@@ -176,7 +176,7 @@ static void keyspan_set_termios(struct tty_struct *tty,
        tty_encode_baud_rate(tty, baud_rate, baud_rate);
        /* set CTS/RTS handshake etc. */
        p_priv->cflag = cflag;
-       p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none;
+       p_priv->flow_control = (cflag & CRTSCTS) ? flow_cts : flow_none;
 
        /* Mark/Space not supported */
        tty->termios->c_cflag &= ~CMSPAR;
@@ -474,7 +474,7 @@ static void usa28_indat_callback(struct urb *urb)
                p_priv = usb_get_serial_port_data(port);
                data = urb->transfer_buffer;
 
-               tty =tty_port_tty_get(&port->port);
+               tty = tty_port_tty_get(&port->port);
                if (tty && urb->actual_length) {
                        tty_insert_flip_string(tty, data, urb->actual_length);
                        tty_flip_buffer_push(tty);
@@ -557,9 +557,9 @@ static void usa28_instat_callback(struct urb *urb)
        p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
        p_priv->ri_state = ((msg->ri) ? 1 : 0);
 
-       ifold_dcd_state != p_priv->dcd_state && old_dcd_state) {
+       if (old_dcd_state != p_priv->dcd_state && old_dcd_state) {
                tty = tty_port_tty_get(&port->port);
-               if (tty && !C_CLOCAL(tty)) 
+               if (tty && !C_CLOCAL(tty))
                        tty_hangup(tty);
                tty_kref_put(tty);
        }
@@ -1036,15 +1036,12 @@ static int keyspan_write_room(struct tty_struct *tty)
 static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port)
 {
        struct keyspan_port_private     *p_priv;
-       struct keyspan_serial_private   *s_priv;
-       struct usb_serial               *serial = port->serial;
        const struct keyspan_device_details     *d_details;
        int                             i, err;
        int                             baud_rate, device_port;
        struct urb                      *urb;
        unsigned int                    cflag = 0;
 
-       s_priv = usb_get_serial_data(serial);
        p_priv = usb_get_serial_port_data(port);
        d_details = p_priv->device_details;
 
@@ -1102,7 +1099,7 @@ static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port)
        }
        /* set CTS/RTS handshake etc. */
        p_priv->cflag = cflag;
-       p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none;
+       p_priv->flow_control = (cflag & CRTSCTS) ? flow_cts : flow_none;
 
        keyspan_send_setup(port, 1);
        /* mdelay(100); */
@@ -1130,10 +1127,8 @@ static void keyspan_close(struct usb_serial_port *port)
 {
        int                     i;
        struct usb_serial       *serial = port->serial;
-       struct keyspan_serial_private   *s_priv;
        struct keyspan_port_private     *p_priv;
 
-       s_priv = usb_get_serial_data(serial);
        p_priv = usb_get_serial_port_data(port);
 
        p_priv->rts_state = 0;
@@ -1240,7 +1235,7 @@ static int keyspan_fake_startup(struct usb_serial *serial)
 
        if (request_ihex_firmware(&fw, fw_name, &serial->dev->dev)) {
                dev_err(&serial->dev->dev, "Required keyspan firmware image (%s) unavailable.\n", fw_name);
-               return(1);
+               return 1;
        }
 
        dbg("Uploading Keyspan %s firmware.", fw_name);
@@ -1709,7 +1704,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
                msg.setPrescaler = 0xff;
        }
 
-       msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1;
+       msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1;
        switch (p_priv->cflag & CSIZE) {
        case CS5:
                msg.lcr |= USA_DATABITS_5;
@@ -1726,7 +1721,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
        }
        if (p_priv->cflag & PARENB) {
                /* note USA_PARITY_NONE == 0 */
-               msg.lcr |= (p_priv->cflag & PARODD)?
+               msg.lcr |= (p_priv->cflag & PARODD) ?
                        USA_PARITY_ODD : USA_PARITY_EVEN;
        }
        msg.setLcr = 0xff;
@@ -1994,7 +1989,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
                /* msg.setPrescaler = 0xff; */
        }
 
-       msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1;
+       msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1;
        switch (p_priv->cflag & CSIZE) {
        case CS5:
                msg.lcr |= USA_DATABITS_5;
@@ -2011,7 +2006,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
        }
        if (p_priv->cflag & PARENB) {
                /* note USA_PARITY_NONE == 0 */
-               msg.lcr |= (p_priv->cflag & PARODD)?
+               msg.lcr |= (p_priv->cflag & PARODD) ?
                        USA_PARITY_ODD : USA_PARITY_EVEN;
        }
        msg.setLcr = 0xff;
@@ -2178,7 +2173,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial,
                msg.txMode = TXMODE_BYHAND;
        }
 
-       msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1;
+       msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1;
        switch (p_priv->cflag & CSIZE) {
        case CS5:
                msg.lcr |= USA_DATABITS_5;
@@ -2195,7 +2190,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial,
        }
        if (p_priv->cflag & PARENB) {
                /* note USA_PARITY_NONE == 0 */
-               msg.lcr |= (p_priv->cflag & PARODD)?
+               msg.lcr |= (p_priv->cflag & PARODD) ?
                        USA_PARITY_ODD : USA_PARITY_EVEN;
        }
        if (p_priv->old_cflag != p_priv->cflag) {
@@ -2322,7 +2317,7 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial,
        }
        if (p_priv->cflag & PARENB) {
                /* note USA_PARITY_NONE == 0 */
-               msg.lcr |= (p_priv->cflag & PARODD)?
+               msg.lcr |= (p_priv->cflag & PARODD) ?
                                        USA_PARITY_ODD : USA_PARITY_EVEN;
        }
        msg.setLcr = 0xff;
index 417ab1b..08ff9b8 100644 (file)
@@ -936,6 +936,8 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff),
+         .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
@@ -1310,6 +1312,10 @@ static struct usb_serial_driver * const serial_drivers[] = {
 
 static bool debug;
 
+struct option_private {
+       u8 bInterfaceNumber;
+};
+
 module_usb_serial_driver(serial_drivers, option_ids);
 
 static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason,
@@ -1340,51 +1346,78 @@ static int option_probe(struct usb_serial *serial,
                        const struct usb_device_id *id)
 {
        struct usb_wwan_intf_private *data;
-
-       /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
-       if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID &&
-               serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 &&
-               serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8)
+       struct option_private *priv;
+       struct usb_interface_descriptor *iface_desc =
+                               &serial->interface->cur_altsetting->desc;
+       struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;
+
+       /*
+        * D-Link DWM 652 still exposes CD-Rom emulation interface in modem
+        * mode.
+        */
+       if (dev_desc->idVendor == DLINK_VENDOR_ID &&
+               dev_desc->idProduct == DLINK_PRODUCT_DWM_652 &&
+               iface_desc->bInterfaceClass == 0x08)
                return -ENODEV;
 
        /* Bandrich modem and AT command interface is 0xff */
-       if ((serial->dev->descriptor.idVendor == BANDRICH_VENDOR_ID ||
-               serial->dev->descriptor.idVendor == PIRELLI_VENDOR_ID) &&
-               serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff)
+       if ((dev_desc->idVendor == BANDRICH_VENDOR_ID ||
+               dev_desc->idVendor == PIRELLI_VENDOR_ID) &&
+               iface_desc->bInterfaceClass != 0xff)
                return -ENODEV;
-
-       /* Don't bind reserved interfaces (like network ones) which often have
+       /*
+        * Don't bind reserved interfaces (like network ones) which often have
         * the same class/subclass/protocol as the serial interfaces.  Look at
         * the Windows driver .INF files for reserved interface numbers.
         */
        if (is_blacklisted(
-               serial->interface->cur_altsetting->desc.bInterfaceNumber,
+               iface_desc->bInterfaceNumber,
                OPTION_BLACKLIST_RESERVED_IF,
                (const struct option_blacklist_info *) id->driver_info))
                return -ENODEV;
-
-       /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */
-       if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID &&
-               serial->dev->descriptor.idProduct == SAMSUNG_PRODUCT_GT_B3730 &&
-               serial->interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA)
+       /*
+        * Don't bind network interface on Samsung GT-B3730, it is handled by
+        * a separate module.
+        */
+       if (dev_desc->idVendor == SAMSUNG_VENDOR_ID &&
+               dev_desc->idProduct == SAMSUNG_PRODUCT_GT_B3730 &&
+               iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)
                return -ENODEV;
 
-       data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+       data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
-       data->send_setup = option_send_setup;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               kfree(data);
+               return -ENOMEM;
+       }
+
+       priv->bInterfaceNumber = iface_desc->bInterfaceNumber;
+       data->private = priv;
+
+       if (!is_blacklisted(iface_desc->bInterfaceNumber,
+                       OPTION_BLACKLIST_SENDSETUP,
+                       (struct option_blacklist_info *)id->driver_info)) {
+               data->send_setup = option_send_setup;
+       }
        spin_lock_init(&data->susp_lock);
-       data->private = (void *)id->driver_info;
+
+       usb_set_serial_data(serial, data);
+
        return 0;
 }
 
 static void option_release(struct usb_serial *serial)
 {
-       struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+       struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
+       struct option_private *priv = intfdata->private;
 
        usb_wwan_release(serial);
 
        kfree(priv);
+       kfree(intfdata);
 }
 
 static void option_instat_callback(struct urb *urb)
@@ -1451,18 +1484,11 @@ static void option_instat_callback(struct urb *urb)
 static int option_send_setup(struct usb_serial_port *port)
 {
        struct usb_serial *serial = port->serial;
-       struct usb_wwan_intf_private *intfdata =
-               (struct usb_wwan_intf_private *) serial->private;
+       struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
+       struct option_private *priv = intfdata->private;
        struct usb_wwan_port_private *portdata;
-       int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
        int val = 0;
 
-       if (is_blacklisted(ifNum, OPTION_BLACKLIST_SENDSETUP,
-                       (struct option_blacklist_info *) intfdata->private)) {
-               dbg("No send_setup on blacklisted interface #%d\n", ifNum);
-               return -EIO;
-       }
-
        portdata = usb_get_serial_port_data(port);
 
        if (portdata->dtr_state)
@@ -1470,9 +1496,9 @@ static int option_send_setup(struct usb_serial_port *port)
        if (portdata->rts_state)
                val |= 0x02;
 
-       return usb_control_msg(serial->dev,
-               usb_rcvctrlpipe(serial->dev, 0),
-               0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT);
+       return usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+                               0x22, 0x21, val, priv->bInterfaceNumber, NULL,
+                               0, USB_CTRL_SET_TIMEOUT);
 }
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
index 996015c..8d10301 100644 (file)
@@ -105,6 +105,10 @@ static const struct usb_device_id id_table[] = {
        {USB_DEVICE(0x1410, 0xa021)},   /* Novatel Gobi 3000 Composite */
        {USB_DEVICE(0x413c, 0x8193)},   /* Dell Gobi 3000 QDL */
        {USB_DEVICE(0x413c, 0x8194)},   /* Dell Gobi 3000 Composite */
+       {USB_DEVICE(0x1199, 0x68a4)},   /* Sierra Wireless QDL */
+       {USB_DEVICE(0x1199, 0x68a5)},   /* Sierra Wireless Modem */
+       {USB_DEVICE(0x1199, 0x68a8)},   /* Sierra Wireless QDL */
+       {USB_DEVICE(0x1199, 0x68a9)},   /* Sierra Wireless Modem */
        {USB_DEVICE(0x1199, 0x9010)},   /* Sierra Wireless Gobi 3000 QDL */
        {USB_DEVICE(0x1199, 0x9012)},   /* Sierra Wireless Gobi 3000 QDL */
        {USB_DEVICE(0x1199, 0x9013)},   /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
@@ -112,8 +116,24 @@ static const struct usb_device_id id_table[] = {
        {USB_DEVICE(0x1199, 0x9015)},   /* Sierra Wireless Gobi 3000 Modem device */
        {USB_DEVICE(0x1199, 0x9018)},   /* Sierra Wireless Gobi 3000 QDL */
        {USB_DEVICE(0x1199, 0x9019)},   /* Sierra Wireless Gobi 3000 Modem device */
+       {USB_DEVICE(0x1199, 0x901b)},   /* Sierra Wireless MC7770 */
        {USB_DEVICE(0x12D1, 0x14F0)},   /* Sony Gobi 3000 QDL */
        {USB_DEVICE(0x12D1, 0x14F1)},   /* Sony Gobi 3000 Composite */
+
+       /* non Gobi Qualcomm serial devices */
+       {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 0)},       /* Sierra Wireless MC7700 Device Management */
+       {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 2)},       /* Sierra Wireless MC7700 NMEA */
+       {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 3)},       /* Sierra Wireless MC7700 Modem */
+       {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 0)},       /* Sierra Wireless MC7750 Device Management */
+       {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 2)},       /* Sierra Wireless MC7750 NMEA */
+       {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 3)},       /* Sierra Wireless MC7750 Modem */
+       {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 0)},       /* Sierra Wireless MC7710 Device Management */
+       {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 2)},       /* Sierra Wireless MC7710 NMEA */
+       {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 3)},       /* Sierra Wireless MC7710 Modem */
+       {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 0)},       /* Sierra Wireless EM7700 Device Management */
+       {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 2)},       /* Sierra Wireless EM7700 NMEA */
+       {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 3)},       /* Sierra Wireless EM7700 Modem */
+
        { }                             /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, id_table);
@@ -127,6 +147,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
        __u8 nintf;
        __u8 ifnum;
        bool is_gobi1k = id->driver_info ? true : false;
+       int altsetting = -1;
 
        dev_dbg(dev, "Is Gobi 1000 = %d\n", is_gobi1k);
 
@@ -142,38 +163,43 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
 
        spin_lock_init(&data->susp_lock);
 
-       switch (nintf) {
-       case 1:
+       if (nintf == 1) {
                /* QDL mode */
                /* Gobi 2000 has a single altsetting, older ones have two */
                if (serial->interface->num_altsetting == 2)
                        intf = &serial->interface->altsetting[1];
                else if (serial->interface->num_altsetting > 2)
-                       break;
+                       goto done;
 
                if (intf->desc.bNumEndpoints == 2 &&
                    usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
                    usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
                        dev_dbg(dev, "QDL port found\n");
 
-                       if (serial->interface->num_altsetting == 1) {
+                       if (serial->interface->num_altsetting == 1)
                                retval = 0; /* Success */
-                               break;
-                       }
-
-                       retval = usb_set_interface(serial->dev, ifnum, 1);
-                       if (retval < 0) {
-                               dev_err(dev,
-                                       "Could not set interface, error %d\n",
-                                       retval);
-                               retval = -ENODEV;
-                               kfree(data);
-                       }
+                       else
+                               altsetting = 1;
                }
-               break;
+               goto done;
 
-       case 3:
-       case 4:
+       }
+
+       /* allow any number of interfaces when doing direct interface match */
+       if (id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) {
+               dev_dbg(dev, "Generic Qualcomm serial interface found\n");
+               altsetting = 0;
+               goto done;
+       }
+
+       if (nintf < 3 || nintf > 4) {
+               dev_err(dev, "unknown number of interfaces: %d\n", nintf);
+               goto done;
+       }
+
+       /* default to enabling interface */
+       altsetting = 0;
+       switch (ifnum) {
                /* Composite mode; don't bind to the QMI/net interface as that
                 * gets handled by other drivers.
                 */
@@ -191,53 +217,44 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
                 * 3: NMEA
                 */
 
-               if (ifnum == 1 && !is_gobi1k) {
+       case 1:
+               if (is_gobi1k)
+                       altsetting = -1;
+               else
                        dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n");
-                       retval = usb_set_interface(serial->dev, ifnum, 0);
-                       if (retval < 0) {
-                               dev_err(dev,
-                                       "Could not set interface, error %d\n",
-                                       retval);
-                               retval = -ENODEV;
-                               kfree(data);
-                       }
-               } else if (ifnum == 2) {
-                       dev_dbg(dev, "Modem port found\n");
-                       retval = usb_set_interface(serial->dev, ifnum, 0);
-                       if (retval < 0) {
-                               dev_err(dev,
-                                       "Could not set interface, error %d\n",
-                                       retval);
-                               retval = -ENODEV;
-                               kfree(data);
-                       }
-               } else if (ifnum==3 && !is_gobi1k) {
+               break;
+       case 2:
+               dev_dbg(dev, "Modem port found\n");
+               break;
+       case 3:
+               if (is_gobi1k)
+                       altsetting = -1;
+               else
                        /*
                         * NMEA (serial line 9600 8N1)
                         * # echo "\$GPS_START" > /dev/ttyUSBx
                         * # echo "\$GPS_STOP"  > /dev/ttyUSBx
                         */
                        dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n");
-                       retval = usb_set_interface(serial->dev, ifnum, 0);
-                       if (retval < 0) {
-                               dev_err(dev,
-                                       "Could not set interface, error %d\n",
-                                       retval);
-                               retval = -ENODEV;
-                               kfree(data);
-                       }
-               }
-               break;
+       }
 
-       default:
-               dev_err(dev, "unknown number of interfaces: %d\n", nintf);
-               kfree(data);
-               retval = -ENODEV;
+done:
+       if (altsetting >= 0) {
+               retval = usb_set_interface(serial->dev, ifnum, altsetting);
+               if (retval < 0) {
+                       dev_err(dev,
+                               "Could not set interface, error %d\n",
+                               retval);
+                       retval = -ENODEV;
+               }
        }
 
-       /* Set serial->private if not returning -ENODEV */
-       if (retval != -ENODEV)
+       /* Set serial->private if not returning error */
+       if (retval == 0)
                usb_set_serial_data(serial, data);
+       else
+               kfree(data);
+
        return retval;
 }
 
index 8dd88eb..151670b 100644 (file)
@@ -345,7 +345,6 @@ static void qt2_set_termios(struct tty_struct *tty,
 static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
 {
        struct usb_serial *serial;
-       struct qt2_serial_private *serial_priv;
        struct qt2_port_private *port_priv;
        u8 *data;
        u16 device_port;
@@ -357,7 +356,6 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
        serial = port->serial;
 
        port_priv = usb_get_serial_port_data(port);
-       serial_priv = usb_get_serial_data(serial);
 
        /* set the port to RS232 mode */
        status = qt2_control_msg(serial->dev, QT2_GET_SET_QMCR,
@@ -417,13 +415,11 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
 static void qt2_close(struct usb_serial_port *port)
 {
        struct usb_serial *serial;
-       struct qt2_serial_private *serial_priv;
        struct qt2_port_private *port_priv;
        unsigned long flags;
        int i;
 
        serial = port->serial;
-       serial_priv = usb_get_serial_data(serial);
        port_priv = usb_get_serial_port_data(port);
 
        port_priv->is_open = false;
index d423d36..0274710 100644 (file)
@@ -288,9 +288,6 @@ static const struct usb_device_id id_table[] = {
        /* Sierra Wireless HSPA Non-Composite Device */
        { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)},
        { USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */
-       { USB_DEVICE(0x1199, 0x68A2),   /* Sierra Wireless MC77xx in QMI mode */
-         .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
-       },
        { USB_DEVICE(0x1199, 0x68A3),   /* Sierra Wireless Direct IP modems */
          .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
        },
index 82dd834..5dfb4c3 100644 (file)
@@ -66,7 +66,7 @@ void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us)
         * NOTE: This only works because a scsi_cmnd struct field contains
         * a unsigned char cmnd[16], so we know we have storage available
         */
-       for (; srb->cmd_len<12; srb->cmd_len++)
+       for (; srb->cmd_len < 12; srb->cmd_len++)
                srb->cmnd[srb->cmd_len] = 0;
 
        /* send the command to the transport layer */
@@ -76,14 +76,14 @@ void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us)
 void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us)
 {
        /* fix some commands -- this is a form of mode translation
-        * UFI devices only accept 12 byte long commands 
+        * UFI devices only accept 12 byte long commands
         *
         * NOTE: This only works because a scsi_cmnd struct field contains
         * a unsigned char cmnd[16], so we know we have storage available
         */
 
        /* Pad the ATAPI command with zeros */
-       for (; srb->cmd_len<12; srb->cmd_len++)
+       for (; srb->cmd_len < 12; srb->cmd_len++)
                srb->cmnd[srb->cmd_len] = 0;
 
        /* set command length to 12 bytes (this affects the transport layer) */
index 8ec8a6e..638cd64 100644 (file)
@@ -41,16 +41,17 @@ struct sense_iu_old {
 struct uas_dev_info {
        struct usb_interface *intf;
        struct usb_device *udev;
-       int qdepth;
+       struct usb_anchor sense_urbs;
+       struct usb_anchor data_urbs;
+       int qdepth, resetting;
+       struct response_ui response;
        unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
        unsigned use_streams:1;
        unsigned uas_sense_old:1;
        struct scsi_cmnd *cmnd;
-       struct urb *status_urb; /* used only if stream support is available */
 };
 
 enum {
-       ALLOC_STATUS_URB        = (1 << 0),
        SUBMIT_STATUS_URB       = (1 << 1),
        ALLOC_DATA_IN_URB       = (1 << 2),
        SUBMIT_DATA_IN_URB      = (1 << 3),
@@ -58,18 +59,18 @@ enum {
        SUBMIT_DATA_OUT_URB     = (1 << 5),
        ALLOC_CMD_URB           = (1 << 6),
        SUBMIT_CMD_URB          = (1 << 7),
-       COMPLETED_DATA_IN       = (1 << 8),
-       COMPLETED_DATA_OUT      = (1 << 9),
-       DATA_COMPLETES_CMD      = (1 << 10),
+       COMMAND_INFLIGHT        = (1 << 8),
+       DATA_IN_URB_INFLIGHT    = (1 << 9),
+       DATA_OUT_URB_INFLIGHT   = (1 << 10),
+       COMMAND_COMPLETED       = (1 << 11),
 };
 
 /* Overrides scsi_pointer */
 struct uas_cmd_info {
        unsigned int state;
        unsigned int stream;
+       unsigned int aborted;
        struct urb *cmd_urb;
-       /* status_urb is used only if stream support isn't available */
-       struct urb *status_urb;
        struct urb *data_in_urb;
        struct urb *data_out_urb;
        struct list_head list;
@@ -114,7 +115,6 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
 {
        struct sense_iu *sense_iu = urb->transfer_buffer;
        struct scsi_device *sdev = cmnd->device;
-       struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
 
        if (urb->actual_length > 16) {
                unsigned len = be16_to_cpup(&sense_iu->len);
@@ -132,15 +132,12 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
        }
 
        cmnd->result = sense_iu->status;
-       if (!(cmdinfo->state & DATA_COMPLETES_CMD))
-               cmnd->scsi_done(cmnd);
 }
 
 static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
 {
        struct sense_iu_old *sense_iu = urb->transfer_buffer;
        struct scsi_device *sdev = cmnd->device;
-       struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
 
        if (urb->actual_length > 8) {
                unsigned len = be16_to_cpup(&sense_iu->len) - 2;
@@ -158,17 +155,51 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
        }
 
        cmnd->result = sense_iu->status;
-       if (!(cmdinfo->state & DATA_COMPLETES_CMD))
-               cmnd->scsi_done(cmnd);
+}
+
+static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller)
+{
+       struct uas_cmd_info *ci = (void *)&cmnd->SCp;
+
+       scmd_printk(KERN_INFO, cmnd, "%s %p tag %d, inflight:"
+                   "%s%s%s%s%s%s%s%s%s%s%s\n",
+                   caller, cmnd, cmnd->request->tag,
+                   (ci->state & SUBMIT_STATUS_URB)     ? " s-st"  : "",
+                   (ci->state & ALLOC_DATA_IN_URB)     ? " a-in"  : "",
+                   (ci->state & SUBMIT_DATA_IN_URB)    ? " s-in"  : "",
+                   (ci->state & ALLOC_DATA_OUT_URB)    ? " a-out" : "",
+                   (ci->state & SUBMIT_DATA_OUT_URB)   ? " s-out" : "",
+                   (ci->state & ALLOC_CMD_URB)         ? " a-cmd" : "",
+                   (ci->state & SUBMIT_CMD_URB)        ? " s-cmd" : "",
+                   (ci->state & COMMAND_INFLIGHT)      ? " CMD"   : "",
+                   (ci->state & DATA_IN_URB_INFLIGHT)  ? " IN"    : "",
+                   (ci->state & DATA_OUT_URB_INFLIGHT) ? " OUT"   : "",
+                   (ci->state & COMMAND_COMPLETED)     ? " done"  : "");
+}
+
+static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
+{
+       struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+
+       if (cmdinfo->state & (COMMAND_INFLIGHT |
+                             DATA_IN_URB_INFLIGHT |
+                             DATA_OUT_URB_INFLIGHT))
+               return -EBUSY;
+       BUG_ON(cmdinfo->state & COMMAND_COMPLETED);
+       cmdinfo->state |= COMMAND_COMPLETED;
+       usb_free_urb(cmdinfo->data_in_urb);
+       usb_free_urb(cmdinfo->data_out_urb);
+       cmnd->scsi_done(cmnd);
+       return 0;
 }
 
 static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
-                                                       unsigned direction)
+                         unsigned direction)
 {
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
        int err;
 
-       cmdinfo->state = direction;
+       cmdinfo->state |= direction | SUBMIT_STATUS_URB;
        err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
        if (err) {
                spin_lock(&uas_work_lock);
@@ -186,12 +217,15 @@ static void uas_stat_cmplt(struct urb *urb)
        struct scsi_cmnd *cmnd;
        struct uas_cmd_info *cmdinfo;
        u16 tag;
-       int ret;
 
        if (urb->status) {
                dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status);
-               if (devinfo->use_streams)
-                       usb_free_urb(urb);
+               usb_free_urb(urb);
+               return;
+       }
+
+       if (devinfo->resetting) {
+               usb_free_urb(urb);
                return;
        }
 
@@ -201,47 +235,34 @@ static void uas_stat_cmplt(struct urb *urb)
        else
                cmnd = scsi_host_find_tag(shost, tag - 1);
        if (!cmnd) {
-               if (devinfo->use_streams) {
+               if (iu->iu_id != IU_ID_RESPONSE) {
                        usb_free_urb(urb);
                        return;
                }
-               ret = usb_submit_urb(urb, GFP_ATOMIC);
-               if (ret)
-                       dev_err(&urb->dev->dev, "failed submit status urb\n");
-               return;
+       } else {
+               cmdinfo = (void *)&cmnd->SCp;
        }
-       cmdinfo = (void *)&cmnd->SCp;
 
        switch (iu->iu_id) {
        case IU_ID_STATUS:
                if (devinfo->cmnd == cmnd)
                        devinfo->cmnd = NULL;
 
-               if (!(cmdinfo->state & COMPLETED_DATA_IN) &&
-                               cmdinfo->data_in_urb) {
-                      if (devinfo->use_streams) {
-                              cmdinfo->state |= DATA_COMPLETES_CMD;
-                              usb_unlink_urb(cmdinfo->data_in_urb);
-                      } else {
-                              usb_free_urb(cmdinfo->data_in_urb);
-                      }
-               }
-               if (!(cmdinfo->state & COMPLETED_DATA_OUT) &&
-                               cmdinfo->data_out_urb) {
-                       if (devinfo->use_streams) {
-                               cmdinfo->state |= DATA_COMPLETES_CMD;
-                               usb_unlink_urb(cmdinfo->data_in_urb);
-                       } else {
-                               usb_free_urb(cmdinfo->data_out_urb);
-                       }
-               }
-
                if (urb->actual_length < 16)
                        devinfo->uas_sense_old = 1;
                if (devinfo->uas_sense_old)
                        uas_sense_old(urb, cmnd);
                else
                        uas_sense(urb, cmnd);
+               if (cmnd->result != 0) {
+                       /* cancel data transfers on error */
+                       if (cmdinfo->state & DATA_IN_URB_INFLIGHT)
+                               usb_unlink_urb(cmdinfo->data_in_urb);
+                       if (cmdinfo->state & DATA_OUT_URB_INFLIGHT)
+                               usb_unlink_urb(cmdinfo->data_out_urb);
+               }
+               cmdinfo->state &= ~COMMAND_INFLIGHT;
+               uas_try_complete(cmnd, __func__);
                break;
        case IU_ID_READ_READY:
                uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB);
@@ -249,74 +270,59 @@ static void uas_stat_cmplt(struct urb *urb)
        case IU_ID_WRITE_READY:
                uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);
                break;
+       case IU_ID_RESPONSE:
+               /* store results for uas_eh_task_mgmt() */
+               memcpy(&devinfo->response, iu, sizeof(devinfo->response));
+               break;
        default:
                scmd_printk(KERN_ERR, cmnd,
                        "Bogus IU (%d) received on status pipe\n", iu->iu_id);
        }
-
-       if (devinfo->use_streams) {
-               usb_free_urb(urb);
-               return;
-       }
-
-       ret = usb_submit_urb(urb, GFP_ATOMIC);
-       if (ret)
-               dev_err(&urb->dev->dev, "failed submit status urb\n");
-}
-
-static void uas_data_out_cmplt(struct urb *urb)
-{
-       struct scsi_cmnd *cmnd = urb->context;
-       struct scsi_data_buffer *sdb = scsi_out(cmnd);
-       struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
-
-       cmdinfo->state |= COMPLETED_DATA_OUT;
-
-       sdb->resid = sdb->length - urb->actual_length;
        usb_free_urb(urb);
-
-       if (cmdinfo->state & DATA_COMPLETES_CMD)
-               cmnd->scsi_done(cmnd);
 }
 
-static void uas_data_in_cmplt(struct urb *urb)
+static void uas_data_cmplt(struct urb *urb)
 {
        struct scsi_cmnd *cmnd = urb->context;
-       struct scsi_data_buffer *sdb = scsi_in(cmnd);
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+       struct scsi_data_buffer *sdb = NULL;
 
-       cmdinfo->state |= COMPLETED_DATA_IN;
-
-       sdb->resid = sdb->length - urb->actual_length;
-       usb_free_urb(urb);
-
-       if (cmdinfo->state & DATA_COMPLETES_CMD)
-               cmnd->scsi_done(cmnd);
+       if (cmdinfo->data_in_urb == urb) {
+               sdb = scsi_in(cmnd);
+               cmdinfo->state &= ~DATA_IN_URB_INFLIGHT;
+       } else if (cmdinfo->data_out_urb == urb) {
+               sdb = scsi_out(cmnd);
+               cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT;
+       }
+       BUG_ON(sdb == NULL);
+       if (urb->status) {
+               /* error: no data transfered */
+               sdb->resid = sdb->length;
+       } else {
+               sdb->resid = sdb->length - urb->actual_length;
+       }
+       if (cmdinfo->aborted) {
+               return;
+       }
+       uas_try_complete(cmnd, __func__);
 }
 
 static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
-               unsigned int pipe, struct scsi_cmnd *cmnd,
-               enum dma_data_direction dir)
+                                     unsigned int pipe, u16 stream_id,
+                                     struct scsi_cmnd *cmnd,
+                                     enum dma_data_direction dir)
 {
-       struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
        struct usb_device *udev = devinfo->udev;
        struct urb *urb = usb_alloc_urb(0, gfp);
-       struct scsi_data_buffer *sdb;
-       usb_complete_t complete_fn;
-       u16 stream_id = cmdinfo->stream;
+       struct scsi_data_buffer *sdb = (dir == DMA_FROM_DEVICE)
+               ? scsi_in(cmnd) : scsi_out(cmnd);
 
        if (!urb)
                goto out;
-       if (dir == DMA_FROM_DEVICE) {
-               sdb = scsi_in(cmnd);
-               complete_fn = uas_data_in_cmplt;
-       } else {
-               sdb = scsi_out(cmnd);
-               complete_fn = uas_data_out_cmplt;
-       }
        usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length,
-                       complete_fn, cmnd);
-       urb->stream_id = stream_id;
+                         uas_data_cmplt, cmnd);
+       if (devinfo->use_streams)
+               urb->stream_id = stream_id;
        urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;
        urb->sg = sdb->table.sgl;
  out:
@@ -324,7 +330,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
 }
 
 static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
-               struct Scsi_Host *shost, u16 stream_id)
+                                      struct Scsi_Host *shost, u16 stream_id)
 {
        struct usb_device *udev = devinfo->udev;
        struct urb *urb = usb_alloc_urb(0, gfp);
@@ -388,38 +394,95 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
        return NULL;
 }
 
+static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp,
+                              u8 function, u16 stream_id)
+{
+       struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
+       struct usb_device *udev = devinfo->udev;
+       struct urb *urb = usb_alloc_urb(0, gfp);
+       struct task_mgmt_iu *iu;
+       int err = -ENOMEM;
+
+       if (!urb)
+               goto err;
+
+       iu = kzalloc(sizeof(*iu), gfp);
+       if (!iu)
+               goto err;
+
+       iu->iu_id = IU_ID_TASK_MGMT;
+       iu->tag = cpu_to_be16(stream_id);
+       int_to_scsilun(cmnd->device->lun, &iu->lun);
+
+       iu->function = function;
+       switch (function) {
+       case TMF_ABORT_TASK:
+               if (blk_rq_tagged(cmnd->request))
+                       iu->task_tag = cpu_to_be16(cmnd->request->tag + 2);
+               else
+                       iu->task_tag = cpu_to_be16(1);
+               break;
+       }
+
+       usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu),
+                         usb_free_urb, NULL);
+       urb->transfer_flags |= URB_FREE_BUFFER;
+
+       err = usb_submit_urb(urb, gfp);
+       if (err)
+               goto err;
+
+       return 0;
+
+err:
+       usb_free_urb(urb);
+       return err;
+}
+
 /*
  * Why should I request the Status IU before sending the Command IU?  Spec
  * says to, but also says the device may receive them in any order.  Seems
  * daft to me.
  */
 
-static int uas_submit_urbs(struct scsi_cmnd *cmnd,
-                                       struct uas_dev_info *devinfo, gfp_t gfp)
+static int uas_submit_sense_urb(struct Scsi_Host *shost,
+                               gfp_t gfp, unsigned int stream)
 {
-       struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+       struct urb *urb;
 
-       if (cmdinfo->state & ALLOC_STATUS_URB) {
-               cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp,
-                               cmnd->device->host, cmdinfo->stream);
-               if (!cmdinfo->status_urb)
-                       return SCSI_MLQUEUE_DEVICE_BUSY;
-               cmdinfo->state &= ~ALLOC_STATUS_URB;
+       urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream);
+       if (!urb)
+               return SCSI_MLQUEUE_DEVICE_BUSY;
+       if (usb_submit_urb(urb, gfp)) {
+               shost_printk(KERN_INFO, shost,
+                            "sense urb submission failure\n");
+               usb_free_urb(urb);
+               return SCSI_MLQUEUE_DEVICE_BUSY;
        }
+       usb_anchor_urb(urb, &devinfo->sense_urbs);
+       return 0;
+}
+
+static int uas_submit_urbs(struct scsi_cmnd *cmnd,
+                          struct uas_dev_info *devinfo, gfp_t gfp)
+{
+       struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+       int err;
 
        if (cmdinfo->state & SUBMIT_STATUS_URB) {
-               if (usb_submit_urb(cmdinfo->status_urb, gfp)) {
-                       scmd_printk(KERN_INFO, cmnd,
-                                       "sense urb submission failure\n");
-                       return SCSI_MLQUEUE_DEVICE_BUSY;
+               err = uas_submit_sense_urb(cmnd->device->host, gfp,
+                                          cmdinfo->stream);
+               if (err) {
+                       return err;
                }
                cmdinfo->state &= ~SUBMIT_STATUS_URB;
        }
 
        if (cmdinfo->state & ALLOC_DATA_IN_URB) {
                cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
-                                       devinfo->data_in_pipe, cmnd,
-                                       DMA_FROM_DEVICE);
+                                       devinfo->data_in_pipe, cmdinfo->stream,
+                                       cmnd, DMA_FROM_DEVICE);
                if (!cmdinfo->data_in_urb)
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                cmdinfo->state &= ~ALLOC_DATA_IN_URB;
@@ -432,12 +495,14 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
                cmdinfo->state &= ~SUBMIT_DATA_IN_URB;
+               cmdinfo->state |= DATA_IN_URB_INFLIGHT;
+               usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs);
        }
 
        if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
                cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
-                                       devinfo->data_out_pipe, cmnd,
-                                       DMA_TO_DEVICE);
+                                       devinfo->data_out_pipe, cmdinfo->stream,
+                                       cmnd, DMA_TO_DEVICE);
                if (!cmdinfo->data_out_urb)
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                cmdinfo->state &= ~ALLOC_DATA_OUT_URB;
@@ -450,6 +515,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
                cmdinfo->state &= ~SUBMIT_DATA_OUT_URB;
+               cmdinfo->state |= DATA_OUT_URB_INFLIGHT;
+               usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs);
        }
 
        if (cmdinfo->state & ALLOC_CMD_URB) {
@@ -467,6 +534,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
                cmdinfo->state &= ~SUBMIT_CMD_URB;
+               cmdinfo->state |= COMMAND_INFLIGHT;
        }
 
        return 0;
@@ -494,8 +562,9 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
 
        cmnd->scsi_done = done;
 
-       cmdinfo->state = ALLOC_STATUS_URB | SUBMIT_STATUS_URB |
+       cmdinfo->state = SUBMIT_STATUS_URB |
                        ALLOC_CMD_URB | SUBMIT_CMD_URB;
+       cmdinfo->aborted = 0;
 
        switch (cmnd->sc_data_direction) {
        case DMA_FROM_DEVICE:
@@ -510,8 +579,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
        }
 
        if (!devinfo->use_streams) {
-               cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB |
-                               ALLOC_STATUS_URB | SUBMIT_STATUS_URB);
+               cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB);
                cmdinfo->stream = 0;
        }
 
@@ -519,7 +587,6 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
        if (err) {
                /* If we did nothing, give up now */
                if (cmdinfo->state & SUBMIT_STATUS_URB) {
-                       usb_free_urb(cmdinfo->status_urb);
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
                spin_lock(&uas_work_lock);
@@ -533,36 +600,66 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
 
 static DEF_SCSI_QCMD(uas_queuecommand)
 
-static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
+static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
+                           const char *fname, u8 function)
 {
-       struct scsi_device *sdev = cmnd->device;
-       sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
-                                                       cmnd->request->tag);
-
-/* XXX: Send ABORT TASK Task Management command */
-       return FAILED;
+       struct Scsi_Host *shost = cmnd->device->host;
+       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+       u16 tag = 9999; /* FIXME */
+
+       memset(&devinfo->response, 0, sizeof(devinfo->response));
+       if (uas_submit_sense_urb(shost, GFP_NOIO, tag)) {
+               shost_printk(KERN_INFO, shost,
+                            "%s: %s: submit sense urb failed\n",
+                            __func__, fname);
+               return FAILED;
+       }
+       if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) {
+               shost_printk(KERN_INFO, shost,
+                            "%s: %s: submit task mgmt urb failed\n",
+                            __func__, fname);
+               return FAILED;
+       }
+       if (0 == usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000)) {
+               shost_printk(KERN_INFO, shost,
+                            "%s: %s timed out\n", __func__, fname);
+               return FAILED;
+       }
+       if (be16_to_cpu(devinfo->response.tag) != tag) {
+               shost_printk(KERN_INFO, shost,
+                            "%s: %s failed (wrong tag %d/%d)\n", __func__,
+                            fname, be16_to_cpu(devinfo->response.tag), tag);
+               return FAILED;
+       }
+       if (devinfo->response.response_code != RC_TMF_COMPLETE) {
+               shost_printk(KERN_INFO, shost,
+                            "%s: %s failed (rc 0x%x)\n", __func__,
+                            fname, devinfo->response.response_code);
+               return FAILED;
+       }
+       return SUCCESS;
 }
 
-static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd)
+static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
 {
-       struct scsi_device *sdev = cmnd->device;
-       sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
-                                                       cmnd->request->tag);
+       struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+       int ret;
 
-/* XXX: Send LOGICAL UNIT RESET Task Management command */
-       return FAILED;
+       uas_log_cmd_state(cmnd, __func__);
+       cmdinfo->aborted = 1;
+       ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK);
+       if (cmdinfo->state & DATA_IN_URB_INFLIGHT)
+               usb_kill_urb(cmdinfo->data_in_urb);
+       if (cmdinfo->state & DATA_OUT_URB_INFLIGHT)
+               usb_kill_urb(cmdinfo->data_out_urb);
+       return ret;
 }
 
-static int uas_eh_target_reset_handler(struct scsi_cmnd *cmnd)
+static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd)
 {
-       struct scsi_device *sdev = cmnd->device;
-       sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
-                                                       cmnd->request->tag);
-
-/* XXX: Can we reset just the one USB interface?
- * Would calling usb_set_interface() have the right effect?
- */
-       return FAILED;
+       sdev_printk(KERN_INFO, cmnd->device, "%s\n", __func__);
+       return uas_eh_task_mgmt(cmnd, "LOGICAL UNIT RESET",
+                               TMF_LOGICAL_UNIT_RESET);
 }
 
 static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
@@ -570,14 +667,21 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
        struct scsi_device *sdev = cmnd->device;
        struct uas_dev_info *devinfo = sdev->hostdata;
        struct usb_device *udev = devinfo->udev;
+       int err;
 
-       sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
-                                                       cmnd->request->tag);
+       devinfo->resetting = 1;
+       usb_kill_anchored_urbs(&devinfo->sense_urbs);
+       usb_kill_anchored_urbs(&devinfo->data_urbs);
+       err = usb_reset_device(udev);
+       devinfo->resetting = 0;
 
-       if (usb_reset_device(udev))
-               return SUCCESS;
+       if (err) {
+               shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__);
+               return FAILED;
+       }
 
-       return FAILED;
+       shost_printk(KERN_INFO, sdev->host, "%s success\n", __func__);
+       return SUCCESS;
 }
 
 static int uas_slave_alloc(struct scsi_device *sdev)
@@ -602,7 +706,6 @@ static struct scsi_host_template uas_host_template = {
        .slave_configure = uas_slave_configure,
        .eh_abort_handler = uas_eh_abort_handler,
        .eh_device_reset_handler = uas_eh_device_reset_handler,
-       .eh_target_reset_handler = uas_eh_target_reset_handler,
        .eh_bus_reset_handler = uas_eh_bus_reset_handler,
        .can_queue = 65536,     /* Is there a limit on the _host_ ? */
        .this_id = -1,
@@ -722,29 +825,6 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo)
        }
 }
 
-static int uas_alloc_status_urb(struct uas_dev_info *devinfo,
-               struct Scsi_Host *shost)
-{
-       if (devinfo->use_streams) {
-               devinfo->status_urb = NULL;
-               return 0;
-       }
-
-       devinfo->status_urb = uas_alloc_sense_urb(devinfo, GFP_KERNEL,
-                       shost, 0);
-       if (!devinfo->status_urb)
-               goto err_s_urb;
-
-       if (usb_submit_urb(devinfo->status_urb, GFP_KERNEL))
-               goto err_submit_urb;
-
-       return 0;
-err_submit_urb:
-       usb_free_urb(devinfo->status_urb);
-err_s_urb:
-       return -ENOMEM;
-}
-
 static void uas_free_streams(struct uas_dev_info *devinfo)
 {
        struct usb_device *udev = devinfo->udev;
@@ -787,6 +867,9 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
 
        devinfo->intf = intf;
        devinfo->udev = udev;
+       devinfo->resetting = 0;
+       init_usb_anchor(&devinfo->sense_urbs);
+       init_usb_anchor(&devinfo->data_urbs);
        uas_configure_endpoints(devinfo);
 
        result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2);
@@ -799,17 +882,10 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
 
        shost->hostdata[0] = (unsigned long)devinfo;
 
-       result = uas_alloc_status_urb(devinfo, shost);
-       if (result)
-               goto err_alloc_status;
-
        scsi_scan_host(shost);
        usb_set_intfdata(intf, shost);
        return result;
 
-err_alloc_status:
-       scsi_remove_host(shost);
-       shost = NULL;
 deconfig_eps:
        uas_free_streams(devinfo);
  free:
@@ -837,8 +913,8 @@ static void uas_disconnect(struct usb_interface *intf)
        struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
 
        scsi_remove_host(shost);
-       usb_kill_urb(devinfo->status_urb);
-       usb_free_urb(devinfo->status_urb);
+       usb_kill_anchored_urbs(&devinfo->sense_urbs);
+       usb_kill_anchored_urbs(&devinfo->data_urbs);
        uas_free_streams(devinfo);
        kfree(devinfo);
 }
index 5db9382..6955045 100644 (file)
@@ -78,6 +78,9 @@ struct ieee1394_device_id {
  *     of a given interface; other interfaces may support other classes.
  * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass.
  * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass.
+ * @bInterfaceNumber: Number of interface; composite devices may use
+ *     fixed interface numbers to differentiate between vendor-specific
+ *     interfaces.
  * @driver_info: Holds information used by the driver.  Usually it holds
  *     a pointer to a descriptor understood by the driver, or perhaps
  *     device flags.
@@ -115,8 +118,12 @@ struct usb_device_id {
        __u8            bInterfaceSubClass;
        __u8            bInterfaceProtocol;
 
+       /* Used for vendor-specific interface matches */
+       __u8            bInterfaceNumber;
+
        /* not matched against */
-       kernel_ulong_t  driver_info;
+       kernel_ulong_t  driver_info
+               __attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
 /* Some useful macros to use to create struct usb_device_id */
@@ -130,6 +137,7 @@ struct usb_device_id {
 #define USB_DEVICE_ID_MATCH_INT_CLASS          0x0080
 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS       0x0100
 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL       0x0200
+#define USB_DEVICE_ID_MATCH_INT_NUMBER         0x0400
 
 #define HID_ANY_ID                             (~0)
 #define HID_BUS_ANY                            0xffff
index 97ec12c..8b79e09 100644 (file)
@@ -12,6 +12,9 @@
  * published by the Free Software Foundation.
 */
 
+#ifndef __LINUX_USB_S3C_HSOTG_H
+#define __LINUX_USB_S3C_HSOTG_H
+
 enum s3c_hsotg_dmamode {
        S3C_HSOTG_DMA_NONE,     /* do not use DMA at-all */
        S3C_HSOTG_DMA_ONLY,     /* always use DMA */
@@ -33,3 +36,5 @@ struct s3c_hsotg_plat {
 };
 
 extern void s3c_hsotg_set_platdata(struct s3c_hsotg_plat *pd);
+
+#endif /* __LINUX_USB_S3C_HSOTG_H */
index dea39dc..30d1ae3 100644 (file)
@@ -77,14 +77,15 @@ struct usb_host_endpoint {
 struct usb_host_interface {
        struct usb_interface_descriptor desc;
 
+       int extralen;
+       unsigned char *extra;   /* Extra descriptors */
+
        /* array of desc.bNumEndpoint endpoints associated with this
         * interface setting.  these will be in no particular order.
         */
        struct usb_host_endpoint *endpoint;
 
        char *string;           /* iInterface string, if present */
-       unsigned char *extra;   /* Extra descriptors */
-       int extralen;
 };
 
 enum usb_interface_condition {
@@ -331,6 +332,11 @@ 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 no_stop_on_short:1;    /*
+                                        * Quirk: some controllers don't stop
+                                        * the ep queue on a short transfer
+                                        * with the URB_SHORT_NOT_OK flag set.
+                                        */
        unsigned sg_tablesize;          /* 0 or largest number of sg list entries */
 
        int devnum_next;                /* Next open device number in
@@ -556,7 +562,6 @@ struct usb_device {
        struct usb3_lpm_parameters u1_params;
        struct usb3_lpm_parameters u2_params;
        unsigned lpm_disable_count;
-       unsigned hub_initiated_lpm_disable_count;
 };
 #define        to_usb_device(d) container_of(d, struct usb_device, dev)
 
@@ -629,6 +634,17 @@ extern void usb_enable_lpm(struct usb_device *udev);
 extern int usb_unlocked_disable_lpm(struct usb_device *udev);
 extern void usb_unlocked_enable_lpm(struct usb_device *udev);
 
+extern int usb_disable_ltm(struct usb_device *udev);
+extern void usb_enable_ltm(struct usb_device *udev);
+
+static inline bool usb_device_supports_ltm(struct usb_device *udev)
+{
+       if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap)
+               return false;
+       return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT;
+}
+
+
 /*-------------------------------------------------------------------------*/
 
 /* for drivers using iso endpoints */
@@ -777,6 +793,22 @@ static inline int usb_make_path(struct usb_device *dev, char *buf, size_t size)
        .bInterfaceProtocol = (pr)
 
 /**
+ * USB_DEVICE_INTERFACE_NUMBER - describe a usb device with a specific interface number
+ * @vend: the 16 bit USB Vendor ID
+ * @prod: the 16 bit USB Product ID
+ * @num: bInterfaceNumber value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific interface number of devices.
+ */
+#define USB_DEVICE_INTERFACE_NUMBER(vend, prod, num) \
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+                      USB_DEVICE_ID_MATCH_INT_NUMBER, \
+       .idVendor = (vend), \
+       .idProduct = (prod), \
+       .bInterfaceNumber = (num)
+
+/**
  * USB_DEVICE_INFO - macro used to describe a class of usb devices
  * @cl: bDeviceClass value
  * @sc: bDeviceSubClass value
@@ -829,6 +861,27 @@ static inline int usb_make_path(struct usb_device *dev, char *buf, size_t size)
        .bInterfaceSubClass = (sc), \
        .bInterfaceProtocol = (pr)
 
+/**
+ * USB_VENDOR_AND_INTERFACE_INFO - describe a specific usb vendor with a class of usb interfaces
+ * @vend: the 16 bit USB Vendor ID
+ * @cl: bInterfaceClass value
+ * @sc: bInterfaceSubClass value
+ * @pr: bInterfaceProtocol value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific vendor with a specific class of interfaces.
+ *
+ * This is especially useful when explicitly matching devices that have
+ * vendor specific bDeviceClass values, but standards-compliant interfaces.
+ */
+#define USB_VENDOR_AND_INTERFACE_INFO(vend, cl, sc, pr) \
+       .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \
+               | USB_DEVICE_ID_MATCH_VENDOR, \
+       .idVendor = (vend), \
+       .bInterfaceClass = (cl), \
+       .bInterfaceSubClass = (sc), \
+       .bInterfaceProtocol = (pr)
+
 /* ----------------------------------------------------------------------- */
 
 /* Stuff for dynamic usb ids */
index edb90d6..544825d 100644 (file)
@@ -5,12 +5,15 @@
 #ifndef __LINUX_USB_CHIPIDEA_H
 #define __LINUX_USB_CHIPIDEA_H
 
+#include <linux/usb/otg.h>
+
 struct ci13xxx;
-struct ci13xxx_udc_driver {
+struct ci13xxx_platform_data {
        const char      *name;
        /* offset of the capability registers */
        uintptr_t        capoffset;
        unsigned         power_budget;
+       struct usb_phy  *phy;
        unsigned long    flags;
 #define CI13XXX_REGS_SHARED            BIT(0)
 #define CI13XXX_REQUIRE_TRANSCEIVER    BIT(1)
@@ -19,10 +22,17 @@ struct ci13xxx_udc_driver {
 
 #define CI13XXX_CONTROLLER_RESET_EVENT         0
 #define CI13XXX_CONTROLLER_STOPPED_EVENT       1
-       void    (*notify_event) (struct ci13xxx *udc, unsigned event);
+       void    (*notify_event) (struct ci13xxx *ci, unsigned event);
 };
 
 /* Default offset of capability registers */
 #define DEF_CAPOFFSET          0x100
 
+/* Add ci13xxx device */
+struct platform_device *ci13xxx_add_device(struct device *dev,
+                       struct resource *res, int nres,
+                       struct ci13xxx_platform_data *platdata);
+/* Remove ci13xxx device */
+void ci13xxx_remove_device(struct platform_device *pdev);
+
 #endif
index 7cc95ee..de4b9ed 100644 (file)
@@ -111,7 +111,13 @@ struct ehci_regs {
        /* ASYNCLISTADDR: offset 0x18 */
        u32             async_next;     /* address of next async queue head */
 
-       u32             reserved[9];
+       u32             reserved1[2];
+
+       /* TXFILLTUNING: offset 0x24 */
+       u32             txfill_tuning;  /* TX FIFO Tuning register */
+#define TXFIFO_DEFAULT (8<<16)         /* FIFO burst threshold 8 */
+
+       u32             reserved2[6];
 
        /* CONFIGFLAG: offset 0x40 */
        u32             configured_flag;
@@ -155,26 +161,34 @@ struct ehci_regs {
 #define PORT_CSC       (1<<1)          /* connect status change */
 #define PORT_CONNECT   (1<<0)          /* device connected */
 #define PORT_RWC_BITS   (PORT_CSC | PORT_PEC | PORT_OCC)
-};
 
-#define USBMODE                0x68            /* USB Device mode */
+       u32             reserved3[9];
+
+       /* USBMODE: offset 0x68 */
+       u32             usbmode;        /* USB Device mode */
 #define USBMODE_SDIS   (1<<3)          /* Stream disable */
 #define USBMODE_BE     (1<<2)          /* BE/LE endianness select */
 #define USBMODE_CM_HC  (3<<0)          /* host controller mode */
 #define USBMODE_CM_IDLE        (0<<0)          /* idle state */
 
+       u32             reserved4[7];
+
 /* Moorestown has some non-standard registers, partially due to the fact that
  * its EHCI controller has both TT and LPM support. HOSTPCx are extensions to
  * PORTSCx
  */
-#define HOSTPC0                0x84            /* HOSTPC extension */
+       /* HOSTPC: offset 0x84 */
+       u32             hostpc[0];      /* HOSTPC extension */
 #define HOSTPC_PHCD    (1<<22)         /* Phy clock disable */
 #define HOSTPC_PSPD    (3<<25)         /* Port speed detection */
-#define USBMODE_EX     0xc8            /* USB Device mode extension */
+
+       u32             reserved5[17];
+
+       /* USBMODE_EX: offset 0xc8 */
+       u32             usbmode_ex;     /* USB Device mode extension */
 #define USBMODE_EX_VBPS        (1<<5)          /* VBus Power Select On */
 #define USBMODE_EX_HC  (3<<0)          /* host controller mode */
-#define TXFILLTUNING   0x24            /* TX FIFO Tuning register */
-#define TXFIFO_DEFAULT (8<<16)         /* FIFO burst threshold 8 */
+};
 
 /* Appendix C, Debug port ... intended for use with special "debug devices"
  * that can help if there's no serial console.  (nonstandard enumeration.)
index 49b3ac2..c5fdb14 100644 (file)
@@ -93,6 +93,12 @@ struct usb_hcd {
         */
        const struct hc_driver  *driver;        /* hw-specific hooks */
 
+       /*
+        * OTG and some Host controllers need software interaction with phys;
+        * other external phys should be software-transparent
+        */
+       struct usb_phy  *phy;
+
        /* Flags that need to be manipulated atomically because they can
         * change while the host controller is running.  Always use
         * set_bit() or clear_bit() to change their values.
diff --git a/include/linux/usb/musb-omap.h b/include/linux/usb/musb-omap.h
new file mode 100644 (file)
index 0000000..7774c59
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011-2012 by Texas Instruments
+ *
+ * The Inventra Controller Driver for Linux 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.
+ */
+
+#ifndef __MUSB_OMAP_H__
+#define __MUSB_OMAP_H__
+
+enum omap_musb_vbus_id_status {
+       OMAP_MUSB_UNKNOWN = 0,
+       OMAP_MUSB_ID_GROUND,
+       OMAP_MUSB_ID_FLOAT,
+       OMAP_MUSB_VBUS_VALID,
+       OMAP_MUSB_VBUS_OFF,
+};
+
+#if (defined(CONFIG_USB_MUSB_OMAP2PLUS) || \
+                               defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE))
+void omap_musb_mailbox(enum omap_musb_vbus_id_status status);
+#else
+static inline void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
+{
+}
+#endif
+
+#endif /* __MUSB_OMAP_H__ */
index 38ab3f4..45824be 100644 (file)
@@ -43,6 +43,13 @@ enum usb_phy_events {
        USB_EVENT_ENUMERATED,   /* gadget driver enumerated */
 };
 
+/* associate a type with PHY */
+enum usb_phy_type {
+       USB_PHY_TYPE_UNDEFINED,
+       USB_PHY_TYPE_USB2,
+       USB_PHY_TYPE_USB3,
+};
+
 struct usb_phy;
 
 /* for transceivers connected thru an ULPI interface, the user must
@@ -89,6 +96,7 @@ struct usb_phy {
        const char              *label;
        unsigned int             flags;
 
+       enum usb_phy_type       type;
        enum usb_otg_state      state;
        enum usb_phy_events     last_event;
 
@@ -105,6 +113,9 @@ struct usb_phy {
        u16                     port_status;
        u16                     port_change;
 
+       /* to support controllers that have multiple transceivers */
+       struct list_head        head;
+
        /* initialize/shutdown the OTG controller */
        int     (*init)(struct usb_phy *x);
        void    (*shutdown)(struct usb_phy *x);
@@ -117,11 +128,15 @@ struct usb_phy {
        int     (*set_suspend)(struct usb_phy *x,
                                int suspend);
 
+       /* notify phy connect status change */
+       int     (*notify_connect)(struct usb_phy *x, int port);
+       int     (*notify_disconnect)(struct usb_phy *x, int port);
 };
 
 
 /* for board-specific init logic */
-extern int usb_set_transceiver(struct usb_phy *);
+extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type);
+extern void usb_remove_phy(struct usb_phy *);
 
 #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE))
 /* sometimes transceivers are accessed only through e.g. ULPI */
@@ -172,16 +187,29 @@ usb_phy_shutdown(struct usb_phy *x)
 
 /* for usb host and peripheral controller drivers */
 #ifdef CONFIG_USB_OTG_UTILS
-extern struct usb_phy *usb_get_transceiver(void);
-extern void usb_put_transceiver(struct usb_phy *);
+extern struct usb_phy *usb_get_phy(enum usb_phy_type type);
+extern struct usb_phy *devm_usb_get_phy(struct device *dev,
+       enum usb_phy_type type);
+extern void usb_put_phy(struct usb_phy *);
+extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x);
 extern const char *otg_state_string(enum usb_otg_state state);
 #else
-static inline struct usb_phy *usb_get_transceiver(void)
+static inline struct usb_phy *usb_get_phy(enum usb_phy_type type)
+{
+       return NULL;
+}
+
+static inline struct usb_phy *devm_usb_get_phy(struct device *dev,
+       enum usb_phy_type type)
 {
        return NULL;
 }
 
-static inline void usb_put_transceiver(struct usb_phy *x)
+static inline void usb_put_phy(struct usb_phy *x)
+{
+}
+
+static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x)
 {
 }
 
@@ -252,6 +280,24 @@ usb_phy_set_suspend(struct usb_phy *x, int suspend)
 }
 
 static inline int
+usb_phy_notify_connect(struct usb_phy *x, int port)
+{
+       if (x->notify_connect)
+               return x->notify_connect(x, port);
+       else
+               return 0;
+}
+
+static inline int
+usb_phy_notify_disconnect(struct usb_phy *x, int port)
+{
+       if (x->notify_disconnect)
+               return x->notify_disconnect(x, port);
+       else
+               return 0;
+}
+
+static inline int
 otg_start_srp(struct usb_otg *otg)
 {
        if (otg && otg->start_srp)
@@ -276,4 +322,15 @@ usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb)
 /* for OTG controller drivers (and maybe other stuff) */
 extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
 
+static inline const char *usb_phy_type_string(enum usb_phy_type type)
+{
+       switch (type) {
+       case USB_PHY_TYPE_USB2:
+               return "USB2 PHY";
+       case USB_PHY_TYPE_USB3:
+               return "USB3 PHY";
+       default:
+               return "UNKNOWN PHY TYPE";
+       }
+}
 #endif /* __LINUX_USB_OTG_H */
index 547e59c..c5d36c6 100644 (file)
@@ -132,6 +132,14 @@ struct renesas_usbhs_driver_param {
         * option:
         *
         * dma id for dmaengine
+        * The data transfer direction on D0FIFO/D1FIFO should be
+        * fixed for keeping consistency.
+        * So, the platform id settings will be..
+        *      .d0_tx_id = xx_TX,
+        *      .d1_rx_id = xx_RX,
+        * or
+        *      .d1_tx_id = xx_TX,
+        *      .d0_rx_id = xx_RX,
         */
        int d0_tx_id;
        int d0_rx_id;
index 9a988e4..5499ab5 100644 (file)
@@ -20,6 +20,28 @@ enum {
        IU_ID_WRITE_READY       = 0x07,
 };
 
+enum {
+       TMF_ABORT_TASK          = 0x01,
+       TMF_ABORT_TASK_SET      = 0x02,
+       TMF_CLEAR_TASK_SET      = 0x04,
+       TMF_LOGICAL_UNIT_RESET  = 0x08,
+       TMF_I_T_NEXUS_RESET     = 0x10,
+       TMF_CLEAR_ACA           = 0x40,
+       TMF_QUERY_TASK          = 0x80,
+       TMF_QUERY_TASK_SET      = 0x81,
+       TMF_QUERY_ASYNC_EVENT   = 0x82,
+};
+
+enum {
+       RC_TMF_COMPLETE         = 0x00,
+       RC_INVALID_INFO_UNIT    = 0x02,
+       RC_TMF_NOT_SUPPORTED    = 0x04,
+       RC_TMF_FAILED           = 0x05,
+       RC_TMF_SUCCEEDED        = 0x08,
+       RC_INCORRECT_LUN        = 0x09,
+       RC_OVERLAPPED_TAG       = 0x0a,
+};
+
 struct command_iu {
        __u8 iu_id;
        __u8 rsvd1;
@@ -32,6 +54,16 @@ struct command_iu {
        __u8 cdb[16];   /* XXX: Overflow-checking tools may misunderstand */
 };
 
+struct task_mgmt_iu {
+       __u8 iu_id;
+       __u8 rsvd1;
+       __be16 tag;
+       __u8 function;
+       __u8 rsvd2;
+       __be16 task_tag;
+       struct scsi_lun lun;
+};
+
 /*
  * Also used for the Read Ready and Write Ready IUs since they have the
  * same first four bytes
@@ -47,6 +79,14 @@ struct sense_iu {
        __u8 sense[SCSI_SENSE_BUFFERSIZE];
 };
 
+struct response_ui {
+       __u8 iu_id;
+       __u8 rsvd1;
+       __be16 tag;
+       __be16 add_response_info;
+       __u8 response_code;
+};
+
 struct usb_pipe_usage_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;
index 15591d2..3b74666 100644 (file)
@@ -125,6 +125,12 @@ struct usbdevfs_hub_portinfo {
        char port [127];        /* e.g. port 3 connects to device 27 */
 };
 
+/* Device capability flags */
+#define USBDEVFS_CAP_ZERO_PACKET               0x01
+#define USBDEVFS_CAP_BULK_CONTINUATION         0x02
+#define USBDEVFS_CAP_NO_PACKET_SIZE_LIM                0x04
+#define USBDEVFS_CAP_BULK_SCATTER_GATHER       0x08
+
 #ifdef __KERNEL__
 #ifdef CONFIG_COMPAT
 #include <linux/compat.h>
@@ -204,4 +210,6 @@ struct usbdevfs_ioctl32 {
 #define USBDEVFS_CONNECT           _IO('U', 23)
 #define USBDEVFS_CLAIM_PORT        _IOR('U', 24, unsigned int)
 #define USBDEVFS_RELEASE_PORT      _IOR('U', 25, unsigned int)
+#define USBDEVFS_GET_CAPABILITIES  _IOR('U', 26, __u32)
+
 #endif /* _LINUX_USBDEVICE_FS_H */
index 5759751..7ed6864 100644 (file)
@@ -156,7 +156,7 @@ static void device_id_check(const char *modname, const char *device_id,
 }
 
 /* USB is special because the bcdDevice can be matched against a numeric range */
-/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipN" */
+/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipNinN" */
 static void do_usb_entry(struct usb_device_id *id,
                         unsigned int bcdDevice_initial, int bcdDevice_initial_digits,
                         unsigned char range_lo, unsigned char range_hi,
@@ -210,6 +210,9 @@ static void do_usb_entry(struct usb_device_id *id,
        ADD(alias, "ip",
            id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL,
            id->bInterfaceProtocol);
+       ADD(alias, "in",
+           id->match_flags&USB_DEVICE_ID_MATCH_INT_NUMBER,
+           id->bInterfaceNumber);
 
        add_wildcard(alias);
        buf_printf(&mod->dev_table_buf,
index 82d7c59..b0adb27 100644 (file)
@@ -425,7 +425,7 @@ int main (int argc, char **argv)
        /* for easy use when hotplugging */
        device = getenv ("DEVICE");
 
-       while ((c = getopt (argc, argv, "D:aA:c:g:hns:t:v:")) != EOF)
+       while ((c = getopt (argc, argv, "D:aA:c:g:hlns:t:v:")) != EOF)
        switch (c) {
        case 'D':       /* device, if only one */
                device = optarg;
@@ -468,10 +468,21 @@ int main (int argc, char **argv)
        case 'h':
        default:
 usage:
-               fprintf (stderr, "usage: %s [-n] [-D dev | -a | -A usbfs-dir]\n"
-                       "\t[-c iterations]  [-t testnum]\n"
-                       "\t[-s packetsize] [-g sglen] [-v vary]\n",
-                       argv [0]);
+               fprintf (stderr,
+                       "usage: %s [options]\n"
+                       "Options:\n"
+                       "\t-D dev               only test specific device\n"
+                       "\t-A usbfs-dir\n"
+                       "\t-a           test all recognized devices\n"
+                       "\t-l           loop forever(for stress test)\n"
+                       "\t-t testnum   only run specified case\n"
+                       "\t-n           no test running, show devices to be tested\n"
+                       "Case arguments:\n"
+                       "\t-c iterations        default 1000\n"
+                       "\t-s packetsize        default 512\n"
+                       "\t-g sglen     default 32\n"
+                       "\t-v vary              default 512\n",
+                       argv[0]);
                return 1;
        }
        if (optind != argc)