Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Jun 2009 20:06:10 +0000 (13:06 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Jun 2009 20:06:10 +0000 (13:06 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (143 commits)
  USB: xhci depends on PCI.
  USB: xhci: Add Makefile, MAINTAINERS, and Kconfig entries.
  USB: xhci: Respect critical sections.
  USB: xHCI: Fix interrupt moderation.
  USB: xhci: Remove packed attribute from structures.
  usb; xhci: Fix TRB offset calculations.
  USB: xhci: replace if-elseif-else with switch-case
  USB: xhci: Make xhci-mem.c include linux/dmapool.h
  USB: xhci: drop spinlock in xhci_urb_enqueue() error path.
  USB: Change names of SuperSpeed ep companion descriptor structs.
  USB: xhci: Avoid compiler reordering in Link TRB giveback.
  USB: xhci: Clean up xhci_irq() function.
  USB: xhci: Avoid global namespace pollution.
  USB: xhci: Fix Link TRB handoff bit twiddling.
  USB: xhci: Fix register write order.
  USB: xhci: fix some compiler warnings in xhci.h
  USB: xhci: fix lots of compiler warnings.
  USB: xhci: use xhci_handle_event instead of handle_event
  USB: xhci: URB cancellation support.
  USB: xhci: Scatter gather list support for bulk transfers.
  ...

164 files changed:
MAINTAINERS
arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h [new file with mode: 0644]
arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h [new file with mode: 0644]
drivers/pci/pci.c
drivers/staging/uc2322/aten2011.c
drivers/usb/Kconfig
drivers/usb/Makefile
drivers/usb/class/cdc-acm.c
drivers/usb/class/cdc-acm.h
drivers/usb/class/usbtmc.c
drivers/usb/core/Kconfig
drivers/usb/core/Makefile
drivers/usb/core/config.c
drivers/usb/core/driver.c
drivers/usb/core/endpoint.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.c
drivers/usb/core/hcd.h
drivers/usb/core/hub.c
drivers/usb/core/hub.h
drivers/usb/core/message.c
drivers/usb/core/sysfs.c
drivers/usb/core/urb.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/at91_udc.c
drivers/usb/gadget/atmel_usba_udc.c
drivers/usb/gadget/audio.c [new file with mode: 0644]
drivers/usb/gadget/ci13xxx_udc.c
drivers/usb/gadget/f_audio.c [new file with mode: 0644]
drivers/usb/gadget/f_rndis.c
drivers/usb/gadget/file_storage.c
drivers/usb/gadget/fsl_mx3_udc.c [new file with mode: 0644]
drivers/usb/gadget/fsl_udc_core.c [moved from drivers/usb/gadget/fsl_usb2_udc.c with 99% similarity]
drivers/usb/gadget/fsl_usb2_udc.h
drivers/usb/gadget/gadget_chips.h
drivers/usb/gadget/goku_udc.c
drivers/usb/gadget/imx_udc.c
drivers/usb/gadget/inode.c
drivers/usb/gadget/langwell_udc.c [new file with mode: 0644]
drivers/usb/gadget/langwell_udc.h [new file with mode: 0644]
drivers/usb/gadget/pxa27x_udc.c
drivers/usb/gadget/pxa27x_udc.h
drivers/usb/gadget/s3c-hsotg.c [new file with mode: 0644]
drivers/usb/gadget/u_audio.c [new file with mode: 0644]
drivers/usb/gadget/u_audio.h [new file with mode: 0644]
drivers/usb/gadget/u_serial.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-ixp4xx.c
drivers/usb/host/ehci-orion.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-ppc-of.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci.h
drivers/usb/host/fhci-dbg.c
drivers/usb/host/hwa-hc.c
drivers/usb/host/ohci-dbg.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/r8a66597-hcd.c
drivers/usb/host/r8a66597.h
drivers/usb/host/uhci-hcd.c
drivers/usb/host/uhci-q.c
drivers/usb/host/xhci-dbg.c [new file with mode: 0644]
drivers/usb/host/xhci-ext-caps.h [new file with mode: 0644]
drivers/usb/host/xhci-hcd.c [new file with mode: 0644]
drivers/usb/host/xhci-hub.c [new file with mode: 0644]
drivers/usb/host/xhci-mem.c [new file with mode: 0644]
drivers/usb/host/xhci-pci.c [new file with mode: 0644]
drivers/usb/host/xhci-ring.c [new file with mode: 0644]
drivers/usb/host/xhci.h [new file with mode: 0644]
drivers/usb/misc/sisusbvga/Kconfig
drivers/usb/misc/usbtest.c
drivers/usb/mon/mon_text.c
drivers/usb/musb/Kconfig
drivers/usb/musb/blackfin.c
drivers/usb/musb/cppi_dma.c
drivers/usb/musb/cppi_dma.h
drivers/usb/musb/davinci.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_gadget_ep0.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/musb_host.h
drivers/usb/musb/musb_virthub.c
drivers/usb/musb/omap2430.c
drivers/usb/musb/tusb6010.c
drivers/usb/otg/Kconfig
drivers/usb/otg/Makefile
drivers/usb/otg/langwell_otg.c [new file with mode: 0644]
drivers/usb/otg/nop-usb-xceiv.c
drivers/usb/otg/twl4030-usb.c
drivers/usb/serial/aircable.c
drivers/usb/serial/belkin_sa.c
drivers/usb/serial/bus.c
drivers/usb/serial/cp210x.c
drivers/usb/serial/cyberjack.c
drivers/usb/serial/cypress_m8.c
drivers/usb/serial/digi_acceleport.c
drivers/usb/serial/empeg.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio.h
drivers/usb/serial/garmin_gps.c
drivers/usb/serial/generic.c
drivers/usb/serial/io_edgeport.c
drivers/usb/serial/io_tables.h
drivers/usb/serial/io_ti.c
drivers/usb/serial/ipaq.c
drivers/usb/serial/iuu_phoenix.c
drivers/usb/serial/keyspan.c
drivers/usb/serial/keyspan.h
drivers/usb/serial/keyspan_pda.c
drivers/usb/serial/kl5kusb105.c
drivers/usb/serial/kobil_sct.c
drivers/usb/serial/mct_u232.c
drivers/usb/serial/mos7720.c
drivers/usb/serial/mos7840.c
drivers/usb/serial/omninet.c
drivers/usb/serial/opticon.c
drivers/usb/serial/option.c
drivers/usb/serial/oti6858.c
drivers/usb/serial/pl2303.c
drivers/usb/serial/sierra.c
drivers/usb/serial/spcp8x5.c
drivers/usb/serial/symbolserial.c
drivers/usb/serial/ti_usb_3410_5052.c
drivers/usb/serial/usb-serial.c
drivers/usb/serial/usb_debug.c
drivers/usb/serial/visor.c
drivers/usb/serial/whiteheat.c
drivers/usb/storage/initializers.c
drivers/usb/storage/option_ms.c
drivers/usb/storage/sierra_ms.c
drivers/usb/storage/unusual_devs.h
fs/befs/linuxvfs.c
fs/fat/dir.c
fs/fat/namei_vfat.c
fs/isofs/joliet.c
fs/ncpfs/ncplib_kernel.c
fs/nls/nls_base.c
fs/nls/nls_utf8.c
include/linux/nls.h
include/linux/pci.h
include/linux/pci_ids.h
include/linux/usb.h
include/linux/usb/audio.h
include/linux/usb/ch9.h
include/linux/usb/composite.h
include/linux/usb/langwell_otg.h [new file with mode: 0644]
include/linux/usb/langwell_udc.h [new file with mode: 0644]
include/linux/usb/otg.h
include/linux/usb/r8a66597.h [new file with mode: 0644]
include/linux/usb/serial.h

index 09f6b3e..685784c 100644 (file)
@@ -6165,6 +6165,12 @@ L:       linux-wireless@vger.kernel.org
 S:     Maintained
 F:     drivers/net/wireless/rndis_wlan.c
 
+USB XHCI DRIVER
+P:     Sarah Sharp
+M:     sarah.a.sharp@intel.com
+L:     linux-usb@vger.kernel.org
+S:     Supported
+
 USB ZC0301 DRIVER
 P:     Luca Risolia
 M:     luca.risolia@studio.unibo.it
diff --git a/arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h b/arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h
new file mode 100644 (file)
index 0000000..36a85f5
--- /dev/null
@@ -0,0 +1,50 @@
+/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ *      http://armlinux.simtec.co.uk/
+ *      Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C - USB2.0 Highspeed/OtG device PHY registers
+ *
+ * This program 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.
+*/
+
+/* Note, this is a seperate header file as some of the clock framework
+ * needs to touch this if the clk_48m is used as the USB OHCI or other
+ * peripheral source.
+*/
+
+#ifndef __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H
+#define __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H __FILE__
+
+/* S3C64XX_PA_USB_HSPHY */
+
+#define S3C_HSOTG_PHYREG(x)    ((x) + S3C_VA_USB_HSPHY)
+
+#define S3C_PHYPWR                             S3C_HSOTG_PHYREG(0x00)
+#define SRC_PHYPWR_OTG_DISABLE                 (1 << 4)
+#define SRC_PHYPWR_ANALOG_POWERDOWN            (1 << 3)
+#define SRC_PHYPWR_FORCE_SUSPEND               (1 << 1)
+
+#define S3C_PHYCLK                             S3C_HSOTG_PHYREG(0x04)
+#define S3C_PHYCLK_MODE_USB11                  (1 << 6)
+#define S3C_PHYCLK_EXT_OSC                     (1 << 5)
+#define S3C_PHYCLK_CLK_FORCE                   (1 << 4)
+#define S3C_PHYCLK_ID_PULL                     (1 << 2)
+#define S3C_PHYCLK_CLKSEL_MASK                 (0x3 << 0)
+#define S3C_PHYCLK_CLKSEL_SHIFT                        (0)
+#define S3C_PHYCLK_CLKSEL_48M                  (0x0 << 0)
+#define S3C_PHYCLK_CLKSEL_12M                  (0x2 << 0)
+#define S3C_PHYCLK_CLKSEL_24M                  (0x3 << 0)
+
+#define S3C_RSTCON                             S3C_HSOTG_PHYREG(0x08)
+#define S3C_RSTCON_PHYCLK                      (1 << 2)
+#define S3C_RSTCON_HCLK                                (1 << 2)
+#define S3C_RSTCON_PHY                         (1 << 0)
+
+#define S3C_PHYTUNE                            S3C_HSOTG_PHYREG(0x20)
+
+#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H */
diff --git a/arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h b/arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h
new file mode 100644 (file)
index 0000000..8d18d9d
--- /dev/null
@@ -0,0 +1,377 @@
+/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ *      http://armlinux.simtec.co.uk/
+ *      Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C - USB2.0 Highspeed/OtG device block registers
+ *
+ * This program 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 __PLAT_S3C64XX_REGS_USB_HSOTG_H
+#define __PLAT_S3C64XX_REGS_USB_HSOTG_H __FILE__
+
+#define S3C_HSOTG_REG(x) (x)
+
+#define S3C_GOTGCTL                            S3C_HSOTG_REG(0x000)
+#define S3C_GOTGCTL_BSESVLD                    (1 << 19)
+#define S3C_GOTGCTL_ASESVLD                    (1 << 18)
+#define S3C_GOTGCTL_DBNC_SHORT                 (1 << 17)
+#define S3C_GOTGCTL_CONID_B                    (1 << 16)
+#define S3C_GOTGCTL_DEVHNPEN                   (1 << 11)
+#define S3C_GOTGCTL_HSSETHNPEN                 (1 << 10)
+#define S3C_GOTGCTL_HNPREQ                     (1 << 9)
+#define S3C_GOTGCTL_HSTNEGSCS                  (1 << 8)
+#define S3C_GOTGCTL_SESREQ                     (1 << 1)
+#define S3C_GOTGCTL_SESREQSCS                  (1 << 0)
+
+#define S3C_GOTGINT                            S3C_HSOTG_REG(0x004)
+#define S3C_GOTGINT_DbnceDone                  (1 << 19)
+#define S3C_GOTGINT_ADevTOUTChg                        (1 << 18)
+#define S3C_GOTGINT_HstNegDet                  (1 << 17)
+#define S3C_GOTGINT_HstnegSucStsChng           (1 << 9)
+#define S3C_GOTGINT_SesReqSucStsChng           (1 << 8)
+#define S3C_GOTGINT_SesEndDet                  (1 << 2)
+
+#define S3C_GAHBCFG                            S3C_HSOTG_REG(0x008)
+#define S3C_GAHBCFG_PTxFEmpLvl                 (1 << 8)
+#define S3C_GAHBCFG_NPTxFEmpLvl                        (1 << 7)
+#define S3C_GAHBCFG_DMAEn                      (1 << 5)
+#define S3C_GAHBCFG_HBstLen_MASK               (0xf << 1)
+#define S3C_GAHBCFG_HBstLen_SHIFT              (1)
+#define S3C_GAHBCFG_HBstLen_Single             (0x0 << 1)
+#define S3C_GAHBCFG_HBstLen_Incr               (0x1 << 1)
+#define S3C_GAHBCFG_HBstLen_Incr4              (0x3 << 1)
+#define S3C_GAHBCFG_HBstLen_Incr8              (0x5 << 1)
+#define S3C_GAHBCFG_HBstLen_Incr16             (0x7 << 1)
+#define S3C_GAHBCFG_GlblIntrEn                 (1 << 0)
+
+#define S3C_GUSBCFG                            S3C_HSOTG_REG(0x00C)
+#define S3C_GUSBCFG_PHYLPClkSel                        (1 << 15)
+#define S3C_GUSBCFG_HNPCap                     (1 << 9)
+#define S3C_GUSBCFG_SRPCap                     (1 << 8)
+#define S3C_GUSBCFG_PHYIf16                    (1 << 3)
+#define S3C_GUSBCFG_TOutCal_MASK               (0x7 << 0)
+#define S3C_GUSBCFG_TOutCal_SHIFT              (0)
+#define S3C_GUSBCFG_TOutCal_LIMIT              (0x7)
+#define S3C_GUSBCFG_TOutCal(_x)                        ((_x) << 0)
+
+#define S3C_GRSTCTL                            S3C_HSOTG_REG(0x010)
+
+#define S3C_GRSTCTL_AHBIdle                    (1 << 31)
+#define S3C_GRSTCTL_DMAReq                     (1 << 30)
+#define S3C_GRSTCTL_TxFNum_MASK                        (0x1f << 6)
+#define S3C_GRSTCTL_TxFNum_SHIFT               (6)
+#define S3C_GRSTCTL_TxFNum_LIMIT               (0x1f)
+#define S3C_GRSTCTL_TxFNum(_x)                 ((_x) << 6)
+#define S3C_GRSTCTL_TxFFlsh                    (1 << 5)
+#define S3C_GRSTCTL_RxFFlsh                    (1 << 4)
+#define S3C_GRSTCTL_INTknQFlsh                 (1 << 3)
+#define S3C_GRSTCTL_FrmCntrRst                 (1 << 2)
+#define S3C_GRSTCTL_HSftRst                    (1 << 1)
+#define S3C_GRSTCTL_CSftRst                    (1 << 0)
+
+#define S3C_GINTSTS                            S3C_HSOTG_REG(0x014)
+#define S3C_GINTMSK                            S3C_HSOTG_REG(0x018)
+
+#define S3C_GINTSTS_WkUpInt                    (1 << 31)
+#define S3C_GINTSTS_SessReqInt                 (1 << 30)
+#define S3C_GINTSTS_DisconnInt                 (1 << 29)
+#define S3C_GINTSTS_ConIDStsChng               (1 << 28)
+#define S3C_GINTSTS_PTxFEmp                    (1 << 26)
+#define S3C_GINTSTS_HChInt                     (1 << 25)
+#define S3C_GINTSTS_PrtInt                     (1 << 24)
+#define S3C_GINTSTS_FetSusp                    (1 << 22)
+#define S3C_GINTSTS_incompIP                   (1 << 21)
+#define S3C_GINTSTS_IncomplSOIN                        (1 << 20)
+#define S3C_GINTSTS_OEPInt                     (1 << 19)
+#define S3C_GINTSTS_IEPInt                     (1 << 18)
+#define S3C_GINTSTS_EPMis                      (1 << 17)
+#define S3C_GINTSTS_EOPF                       (1 << 15)
+#define S3C_GINTSTS_ISOutDrop                  (1 << 14)
+#define S3C_GINTSTS_EnumDone                   (1 << 13)
+#define S3C_GINTSTS_USBRst                     (1 << 12)
+#define S3C_GINTSTS_USBSusp                    (1 << 11)
+#define S3C_GINTSTS_ErlySusp                   (1 << 10)
+#define S3C_GINTSTS_GOUTNakEff                 (1 << 7)
+#define S3C_GINTSTS_GINNakEff                  (1 << 6)
+#define S3C_GINTSTS_NPTxFEmp                   (1 << 5)
+#define S3C_GINTSTS_RxFLvl                     (1 << 4)
+#define S3C_GINTSTS_SOF                                (1 << 3)
+#define S3C_GINTSTS_OTGInt                     (1 << 2)
+#define S3C_GINTSTS_ModeMis                    (1 << 1)
+#define S3C_GINTSTS_CurMod_Host                        (1 << 0)
+
+#define S3C_GRXSTSR                            S3C_HSOTG_REG(0x01C)
+#define S3C_GRXSTSP                            S3C_HSOTG_REG(0x020)
+
+#define S3C_GRXSTS_FN_MASK                     (0x7f << 25)
+#define S3C_GRXSTS_FN_SHIFT                    (25)
+
+#define S3C_GRXSTS_PktSts_MASK                 (0xf << 17)
+#define S3C_GRXSTS_PktSts_SHIFT                        (17)
+#define S3C_GRXSTS_PktSts_GlobalOutNAK         (0x1 << 17)
+#define S3C_GRXSTS_PktSts_OutRX                        (0x2 << 17)
+#define S3C_GRXSTS_PktSts_OutDone              (0x3 << 17)
+#define S3C_GRXSTS_PktSts_SetupDone            (0x4 << 17)
+#define S3C_GRXSTS_PktSts_SetupRX              (0x6 << 17)
+
+#define S3C_GRXSTS_DPID_MASK                   (0x3 << 15)
+#define S3C_GRXSTS_DPID_SHIFT                  (15)
+#define S3C_GRXSTS_ByteCnt_MASK                        (0x7ff << 4)
+#define S3C_GRXSTS_ByteCnt_SHIFT               (4)
+#define S3C_GRXSTS_EPNum_MASK                  (0xf << 0)
+#define S3C_GRXSTS_EPNum_SHIFT                 (0)
+
+#define S3C_GRXFSIZ                            S3C_HSOTG_REG(0x024)
+
+#define S3C_GNPTXFSIZ                          S3C_HSOTG_REG(0x028)
+
+#define S3C_GNPTXFSIZ_NPTxFDep_MASK            (0xffff << 16)
+#define S3C_GNPTXFSIZ_NPTxFDep_SHIFT           (16)
+#define S3C_GNPTXFSIZ_NPTxFDep_LIMIT           (0xffff)
+#define S3C_GNPTXFSIZ_NPTxFDep(_x)             ((_x) << 16)
+#define S3C_GNPTXFSIZ_NPTxFStAddr_MASK         (0xffff << 0)
+#define S3C_GNPTXFSIZ_NPTxFStAddr_SHIFT                (0)
+#define S3C_GNPTXFSIZ_NPTxFStAddr_LIMIT                (0xffff)
+#define S3C_GNPTXFSIZ_NPTxFStAddr(_x)          ((_x) << 0)
+
+#define S3C_GNPTXSTS                           S3C_HSOTG_REG(0x02C)
+
+#define S3C_GNPTXSTS_NPtxQTop_MASK             (0x7f << 24)
+#define S3C_GNPTXSTS_NPtxQTop_SHIFT            (24)
+
+#define S3C_GNPTXSTS_NPTxQSpcAvail_MASK                (0xff << 16)
+#define S3C_GNPTXSTS_NPTxQSpcAvail_SHIFT       (16)
+#define S3C_GNPTXSTS_NPTxQSpcAvail_GET(_v)     (((_v) >> 16) & 0xff)
+
+#define S3C_GNPTXSTS_NPTxFSpcAvail_MASK                (0xffff << 0)
+#define S3C_GNPTXSTS_NPTxFSpcAvail_SHIFT       (0)
+#define S3C_GNPTXSTS_NPTxFSpcAvail_GET(_v)     (((_v) >> 0) & 0xffff)
+
+
+#define S3C_HPTXFSIZ                           S3C_HSOTG_REG(0x100)
+
+#define S3C_DPTXFSIZn(_a)                      S3C_HSOTG_REG(0x104 + (((_a) - 1) * 4))
+
+#define S3C_DPTXFSIZn_DPTxFSize_MASK           (0xffff << 16)
+#define S3C_DPTXFSIZn_DPTxFSize_SHIFT          (16)
+#define S3C_DPTXFSIZn_DPTxFSize_GET(_v)                (((_v) >> 16) & 0xffff)
+#define S3C_DPTXFSIZn_DPTxFSize_LIMIT          (0xffff)
+#define S3C_DPTXFSIZn_DPTxFSize(_x)            ((_x) << 16)
+
+#define S3C_DPTXFSIZn_DPTxFStAddr_MASK         (0xffff << 0)
+#define S3C_DPTXFSIZn_DPTxFStAddr_SHIFT                (0)
+
+/* Device mode registers */
+#define S3C_DCFG                               S3C_HSOTG_REG(0x800)
+
+#define S3C_DCFG_EPMisCnt_MASK                 (0x1f << 18)
+#define S3C_DCFG_EPMisCnt_SHIFT                        (18)
+#define S3C_DCFG_EPMisCnt_LIMIT                        (0x1f)
+#define S3C_DCFG_EPMisCnt(_x)                  ((_x) << 18)
+
+#define S3C_DCFG_PerFrInt_MASK                 (0x3 << 11)
+#define S3C_DCFG_PerFrInt_SHIFT                        (11)
+#define S3C_DCFG_PerFrInt_LIMIT                        (0x3)
+#define S3C_DCFG_PerFrInt(_x)                  ((_x) << 11)
+
+#define S3C_DCFG_DevAddr_MASK                  (0x7f << 4)
+#define S3C_DCFG_DevAddr_SHIFT                 (4)
+#define S3C_DCFG_DevAddr_LIMIT                 (0x7f)
+#define S3C_DCFG_DevAddr(_x)                   ((_x) << 4)
+
+#define S3C_DCFG_NZStsOUTHShk                  (1 << 2)
+
+#define S3C_DCFG_DevSpd_MASK                   (0x3 << 0)
+#define S3C_DCFG_DevSpd_SHIFT                  (0)
+#define S3C_DCFG_DevSpd_HS                     (0x0 << 0)
+#define S3C_DCFG_DevSpd_FS                     (0x1 << 0)
+#define S3C_DCFG_DevSpd_LS                     (0x2 << 0)
+#define S3C_DCFG_DevSpd_FS48                   (0x3 << 0)
+
+#define S3C_DCTL                               S3C_HSOTG_REG(0x804)
+
+#define S3C_DCTL_PWROnPrgDone                  (1 << 11)
+#define S3C_DCTL_CGOUTNak                      (1 << 10)
+#define S3C_DCTL_SGOUTNak                      (1 << 9)
+#define S3C_DCTL_CGNPInNAK                     (1 << 8)
+#define S3C_DCTL_SGNPInNAK                     (1 << 7)
+#define S3C_DCTL_TstCtl_MASK                   (0x7 << 4)
+#define S3C_DCTL_TstCtl_SHIFT                  (4)
+#define S3C_DCTL_GOUTNakSts                    (1 << 3)
+#define S3C_DCTL_GNPINNakSts                   (1 << 2)
+#define S3C_DCTL_SftDiscon                     (1 << 1)
+#define S3C_DCTL_RmtWkUpSig                    (1 << 0)
+
+#define S3C_DSTS                               S3C_HSOTG_REG(0x808)
+
+#define S3C_DSTS_SOFFN_MASK                    (0x3fff << 8)
+#define S3C_DSTS_SOFFN_SHIFT                   (8)
+#define S3C_DSTS_SOFFN_LIMIT                   (0x3fff)
+#define S3C_DSTS_SOFFN(_x)                     ((_x) << 8)
+#define S3C_DSTS_ErraticErr                    (1 << 3)
+#define S3C_DSTS_EnumSpd_MASK                  (0x3 << 1)
+#define S3C_DSTS_EnumSpd_SHIFT                 (1)
+#define S3C_DSTS_EnumSpd_HS                    (0x0 << 1)
+#define S3C_DSTS_EnumSpd_FS                    (0x1 << 1)
+#define S3C_DSTS_EnumSpd_LS                    (0x2 << 1)
+#define S3C_DSTS_EnumSpd_FS48                  (0x3 << 1)
+
+#define S3C_DSTS_SuspSts                       (1 << 0)
+
+#define S3C_DIEPMSK                            S3C_HSOTG_REG(0x810)
+
+#define S3C_DIEPMSK_INEPNakEffMsk              (1 << 6)
+#define S3C_DIEPMSK_INTknEPMisMsk              (1 << 5)
+#define S3C_DIEPMSK_INTknTXFEmpMsk             (1 << 4)
+#define S3C_DIEPMSK_TimeOUTMsk                 (1 << 3)
+#define S3C_DIEPMSK_AHBErrMsk                  (1 << 2)
+#define S3C_DIEPMSK_EPDisbldMsk                        (1 << 1)
+#define S3C_DIEPMSK_XferComplMsk               (1 << 0)
+
+#define S3C_DOEPMSK                            S3C_HSOTG_REG(0x814)
+
+#define S3C_DOEPMSK_Back2BackSetup             (1 << 6)
+#define S3C_DOEPMSK_OUTTknEPdisMsk             (1 << 4)
+#define S3C_DOEPMSK_SetupMsk                   (1 << 3)
+#define S3C_DOEPMSK_AHBErrMsk                  (1 << 2)
+#define S3C_DOEPMSK_EPDisbldMsk                        (1 << 1)
+#define S3C_DOEPMSK_XferComplMsk               (1 << 0)
+
+#define S3C_DAINT                              S3C_HSOTG_REG(0x818)
+#define S3C_DAINTMSK                           S3C_HSOTG_REG(0x81C)
+
+#define S3C_DAINT_OutEP_SHIFT                  (16)
+#define S3C_DAINT_OutEP(x)                     (1 << ((x) + 16))
+#define S3C_DAINT_InEP(x)                      (1 << (x))
+
+#define S3C_DTKNQR1                            S3C_HSOTG_REG(0x820)
+#define S3C_DTKNQR2                            S3C_HSOTG_REG(0x824)
+#define S3C_DTKNQR3                            S3C_HSOTG_REG(0x830)
+#define S3C_DTKNQR4                            S3C_HSOTG_REG(0x834)
+
+#define S3C_DVBUSDIS                           S3C_HSOTG_REG(0x828)
+#define S3C_DVBUSPULSE                         S3C_HSOTG_REG(0x82C)
+
+#define S3C_DIEPCTL0                           S3C_HSOTG_REG(0x900)
+#define S3C_DOEPCTL0                           S3C_HSOTG_REG(0xB00)
+#define S3C_DIEPCTL(_a)                                S3C_HSOTG_REG(0x900 + ((_a) * 0x20))
+#define S3C_DOEPCTL(_a)                                S3C_HSOTG_REG(0xB00 + ((_a) * 0x20))
+
+/* EP0 specialness:
+ * bits[29..28] - reserved (no SetD0PID, SetD1PID)
+ * bits[25..22] - should always be zero, this isn't a periodic endpoint
+ * bits[10..0] - MPS setting differenct for EP0
+*/
+#define S3C_D0EPCTL_MPS_MASK                   (0x3 << 0)
+#define S3C_D0EPCTL_MPS_SHIFT                  (0)
+#define S3C_D0EPCTL_MPS_64                     (0x0 << 0)
+#define S3C_D0EPCTL_MPS_32                     (0x1 << 0)
+#define S3C_D0EPCTL_MPS_16                     (0x2 << 0)
+#define S3C_D0EPCTL_MPS_8                      (0x3 << 0)
+
+#define S3C_DxEPCTL_EPEna                      (1 << 31)
+#define S3C_DxEPCTL_EPDis                      (1 << 30)
+#define S3C_DxEPCTL_SetD1PID                   (1 << 29)
+#define S3C_DxEPCTL_SetOddFr                   (1 << 29)
+#define S3C_DxEPCTL_SetD0PID                   (1 << 28)
+#define S3C_DxEPCTL_SetEvenFr                  (1 << 28)
+#define S3C_DxEPCTL_SNAK                       (1 << 27)
+#define S3C_DxEPCTL_CNAK                       (1 << 26)
+#define S3C_DxEPCTL_TxFNum_MASK                        (0xf << 22)
+#define S3C_DxEPCTL_TxFNum_SHIFT               (22)
+#define S3C_DxEPCTL_TxFNum_LIMIT               (0xf)
+#define S3C_DxEPCTL_TxFNum(_x)                 ((_x) << 22)
+
+#define S3C_DxEPCTL_Stall                      (1 << 21)
+#define S3C_DxEPCTL_Snp                                (1 << 20)
+#define S3C_DxEPCTL_EPType_MASK                        (0x3 << 18)
+#define S3C_DxEPCTL_EPType_SHIFT               (18)
+#define S3C_DxEPCTL_EPType_Control             (0x0 << 18)
+#define S3C_DxEPCTL_EPType_Iso                 (0x1 << 18)
+#define S3C_DxEPCTL_EPType_Bulk                        (0x2 << 18)
+#define S3C_DxEPCTL_EPType_Intterupt           (0x3 << 18)
+
+#define S3C_DxEPCTL_NAKsts                     (1 << 17)
+#define S3C_DxEPCTL_DPID                       (1 << 16)
+#define S3C_DxEPCTL_EOFrNum                    (1 << 16)
+#define S3C_DxEPCTL_USBActEp                   (1 << 15)
+#define S3C_DxEPCTL_NextEp_MASK                        (0xf << 11)
+#define S3C_DxEPCTL_NextEp_SHIFT               (11)
+#define S3C_DxEPCTL_NextEp_LIMIT               (0xf)
+#define S3C_DxEPCTL_NextEp(_x)                 ((_x) << 11)
+
+#define S3C_DxEPCTL_MPS_MASK                   (0x7ff << 0)
+#define S3C_DxEPCTL_MPS_SHIFT                  (0)
+#define S3C_DxEPCTL_MPS_LIMIT                  (0x7ff)
+#define S3C_DxEPCTL_MPS(_x)                    ((_x) << 0)
+
+#define S3C_DIEPINT(_a)                                S3C_HSOTG_REG(0x908 + ((_a) * 0x20))
+#define S3C_DOEPINT(_a)                                S3C_HSOTG_REG(0xB08 + ((_a) * 0x20))
+
+#define S3C_DxEPINT_INEPNakEff                 (1 << 6)
+#define S3C_DxEPINT_Back2BackSetup             (1 << 6)
+#define S3C_DxEPINT_INTknEPMis                 (1 << 5)
+#define S3C_DxEPINT_INTknTXFEmp                        (1 << 4)
+#define S3C_DxEPINT_OUTTknEPdis                        (1 << 4)
+#define S3C_DxEPINT_Timeout                    (1 << 3)
+#define S3C_DxEPINT_Setup                      (1 << 3)
+#define S3C_DxEPINT_AHBErr                     (1 << 2)
+#define S3C_DxEPINT_EPDisbld                   (1 << 1)
+#define S3C_DxEPINT_XferCompl                  (1 << 0)
+
+#define S3C_DIEPTSIZ0                          S3C_HSOTG_REG(0x910)
+
+#define S3C_DIEPTSIZ0_PktCnt_MASK              (0x3 << 19)
+#define S3C_DIEPTSIZ0_PktCnt_SHIFT             (19)
+#define S3C_DIEPTSIZ0_PktCnt_LIMIT             (0x3)
+#define S3C_DIEPTSIZ0_PktCnt(_x)               ((_x) << 19)
+
+#define S3C_DIEPTSIZ0_XferSize_MASK            (0x7f << 0)
+#define S3C_DIEPTSIZ0_XferSize_SHIFT           (0)
+#define S3C_DIEPTSIZ0_XferSize_LIMIT           (0x7f)
+#define S3C_DIEPTSIZ0_XferSize(_x)             ((_x) << 0)
+
+
+#define DOEPTSIZ0                              S3C_HSOTG_REG(0xB10)
+#define S3C_DOEPTSIZ0_SUPCnt_MASK              (0x3 << 29)
+#define S3C_DOEPTSIZ0_SUPCnt_SHIFT             (29)
+#define S3C_DOEPTSIZ0_SUPCnt_LIMIT             (0x3)
+#define S3C_DOEPTSIZ0_SUPCnt(_x)               ((_x) << 29)
+
+#define S3C_DOEPTSIZ0_PktCnt                   (1 << 19)
+#define S3C_DOEPTSIZ0_XferSize_MASK            (0x7f << 0)
+#define S3C_DOEPTSIZ0_XferSize_SHIFT           (0)
+
+#define S3C_DIEPTSIZ(_a)                       S3C_HSOTG_REG(0x910 + ((_a) * 0x20))
+#define S3C_DOEPTSIZ(_a)                       S3C_HSOTG_REG(0xB10 + ((_a) * 0x20))
+
+#define S3C_DxEPTSIZ_MC_MASK                   (0x3 << 29)
+#define S3C_DxEPTSIZ_MC_SHIFT                  (29)
+#define S3C_DxEPTSIZ_MC_LIMIT                  (0x3)
+#define S3C_DxEPTSIZ_MC(_x)                    ((_x) << 29)
+
+#define S3C_DxEPTSIZ_PktCnt_MASK               (0x3ff << 19)
+#define S3C_DxEPTSIZ_PktCnt_SHIFT              (19)
+#define S3C_DxEPTSIZ_PktCnt_GET(_v)            (((_v) >> 19) & 0x3ff)
+#define S3C_DxEPTSIZ_PktCnt_LIMIT              (0x3ff)
+#define S3C_DxEPTSIZ_PktCnt(_x)                        ((_x) << 19)
+
+#define S3C_DxEPTSIZ_XferSize_MASK             (0x7ffff << 0)
+#define S3C_DxEPTSIZ_XferSize_SHIFT            (0)
+#define S3C_DxEPTSIZ_XferSize_GET(_v)          (((_v) >> 0) & 0x7ffff)
+#define S3C_DxEPTSIZ_XferSize_LIMIT            (0x7ffff)
+#define S3C_DxEPTSIZ_XferSize(_x)              ((_x) << 0)
+
+
+#define S3C_DIEPDMA(_a)                                S3C_HSOTG_REG(0x914 + ((_a) * 0x20))
+#define S3C_DOEPDMA(_a)                                S3C_HSOTG_REG(0xB14 + ((_a) * 0x20))
+
+#define S3C_EPFIFO(_a)                         S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000))
+
+#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_H */
index 1a91bf9..07bbb9b 100644 (file)
 #include <asm/setup.h>
 #include "pci.h"
 
+const char *pci_power_names[] = {
+       "error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown",
+};
+EXPORT_SYMBOL_GPL(pci_power_names);
+
 unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT;
 
 #ifdef CONFIG_PCI_DOMAINS
index 9c62f78..39d0926 100644 (file)
@@ -2336,7 +2336,7 @@ static int ATEN2011_startup(struct usb_serial *serial)
        return 0;
 }
 
-static void ATEN2011_shutdown(struct usb_serial *serial)
+static void ATEN2011_release(struct usb_serial *serial)
 {
        int i;
        struct ATENINTL_port *ATEN2011_port;
@@ -2382,7 +2382,7 @@ static struct usb_serial_driver aten_serial_driver = {
        .tiocmget =             ATEN2011_tiocmget,
        .tiocmset =             ATEN2011_tiocmset,
        .attach =               ATEN2011_startup,
-       .shutdown =             ATEN2011_shutdown,
+       .release =              ATEN2011_release,
        .read_bulk_callback =   ATEN2011_bulk_in_callback,
        .read_int_callback =    ATEN2011_interrupt_callback,
 };
index 5eee3f8..dcd49f1 100644 (file)
@@ -64,6 +64,7 @@ config USB_ARCH_HAS_EHCI
 config USB
        tristate "Support for Host-side USB"
        depends on USB_ARCH_HAS_HCD
+       select NLS  # for UTF-8 strings
        ---help---
          Universal Serial Bus (USB) is a specification for a serial bus
          subsystem which offers higher speeds and more features than the
index 0a3dc5e..19cb7d5 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_ISP116X_HCD) += host/
 obj-$(CONFIG_USB_OHCI_HCD)     += host/
 obj-$(CONFIG_USB_UHCI_HCD)     += host/
 obj-$(CONFIG_USB_FHCI_HCD)     += host/
+obj-$(CONFIG_USB_XHCI_HCD)     += host/
 obj-$(CONFIG_USB_SL811_HCD)    += host/
 obj-$(CONFIG_USB_U132_HCD)     += host/
 obj-$(CONFIG_USB_R8A66597_HCD) += host/
index ddeb691..38bfdb0 100644 (file)
@@ -937,9 +937,9 @@ static int acm_probe(struct usb_interface *intf,
        int buflen = intf->altsetting->extralen;
        struct usb_interface *control_interface;
        struct usb_interface *data_interface;
-       struct usb_endpoint_descriptor *epctrl;
-       struct usb_endpoint_descriptor *epread;
-       struct usb_endpoint_descriptor *epwrite;
+       struct usb_endpoint_descriptor *epctrl = NULL;
+       struct usb_endpoint_descriptor *epread = NULL;
+       struct usb_endpoint_descriptor *epwrite = NULL;
        struct usb_device *usb_dev = interface_to_usbdev(intf);
        struct acm *acm;
        int minor;
@@ -952,6 +952,7 @@ static int acm_probe(struct usb_interface *intf,
        unsigned long quirks;
        int num_rx_buf;
        int i;
+       int combined_interfaces = 0;
 
        /* normal quirks */
        quirks = (unsigned long)id->driver_info;
@@ -1033,9 +1034,15 @@ next_desc:
                        data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
                        control_interface = intf;
                } else {
-                       dev_dbg(&intf->dev,
-                                       "No union descriptor, giving up\n");
-                       return -ENODEV;
+                       if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
+                               dev_dbg(&intf->dev,"No union descriptor, giving up\n");
+                               return -ENODEV;
+                       } else {
+                               dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
+                               combined_interfaces = 1;
+                               control_interface = data_interface = intf;
+                               goto look_for_collapsed_interface;
+                       }
                }
        } else {
                control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
@@ -1049,6 +1056,36 @@ next_desc:
        if (data_interface_num != call_interface_num)
                dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
 
+       if (control_interface == data_interface) {
+               /* some broken devices designed for windows work this way */
+               dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
+               combined_interfaces = 1;
+               /* a popular other OS doesn't use it */
+               quirks |= NO_CAP_LINE;
+               if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
+                       dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
+                       return -EINVAL;
+               }
+look_for_collapsed_interface:
+               for (i = 0; i < 3; i++) {
+                       struct usb_endpoint_descriptor *ep;
+                       ep = &data_interface->cur_altsetting->endpoint[i].desc;
+
+                       if (usb_endpoint_is_int_in(ep))
+                               epctrl = ep;
+                       else if (usb_endpoint_is_bulk_out(ep))
+                               epwrite = ep;
+                       else if (usb_endpoint_is_bulk_in(ep))
+                               epread = ep;
+                       else
+                               return -EINVAL;
+               }
+               if (!epctrl || !epread || !epwrite)
+                       return -ENODEV;
+               else
+                       goto made_compressed_probe;
+       }
+
 skip_normal_probe:
 
        /*workaround for switched interfaces */
@@ -1068,10 +1105,11 @@ skip_normal_probe:
        }
 
        /* Accept probe requests only for the control interface */
-       if (intf != control_interface)
+       if (!combined_interfaces && intf != control_interface)
                return -ENODEV;
 
-       if (usb_interface_claimed(data_interface)) { /* valid in this context */
+       if (!combined_interfaces && usb_interface_claimed(data_interface)) {
+               /* valid in this context */
                dev_dbg(&intf->dev, "The data interface isn't available\n");
                return -EBUSY;
        }
@@ -1095,6 +1133,7 @@ skip_normal_probe:
                epread = epwrite;
                epwrite = t;
        }
+made_compressed_probe:
        dbg("interfaces are valid");
        for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
 
@@ -1112,12 +1151,15 @@ skip_normal_probe:
        ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
        readsize = le16_to_cpu(epread->wMaxPacketSize) *
                                (quirks == SINGLE_RX_URB ? 1 : 2);
+       acm->combined_interfaces = combined_interfaces;
        acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
        acm->control = control_interface;
        acm->data = data_interface;
        acm->minor = minor;
        acm->dev = usb_dev;
        acm->ctrl_caps = ac_management_function;
+       if (quirks & NO_CAP_LINE)
+               acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
        acm->ctrlsize = ctrlsize;
        acm->readsize = readsize;
        acm->rx_buflimit = num_rx_buf;
@@ -1223,9 +1265,10 @@ skip_normal_probe:
 
 skip_countries:
        usb_fill_int_urb(acm->ctrlurb, usb_dev,
-                       usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
-                       acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
-                       epctrl->bInterval);
+                        usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
+                        acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
+                        /* works around buggy devices */
+                        epctrl->bInterval ? epctrl->bInterval : 0xff);
        acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        acm->ctrlurb->transfer_dma = acm->ctrl_dma;
 
@@ -1312,7 +1355,8 @@ static void acm_disconnect(struct usb_interface *intf)
                                                                acm->ctrl_dma);
        acm_read_buffers_free(acm);
 
-       usb_driver_release_interface(&acm_driver, intf == acm->control ?
+       if (!acm->combined_interfaces)
+               usb_driver_release_interface(&acm_driver, intf == acm->control ?
                                        acm->data : acm->control);
 
        if (acm->port.count == 0) {
@@ -1451,6 +1495,9 @@ static struct usb_device_id acm_ids[] = {
                                           Maybe we should define a new
                                           quirk for this. */
        },
+       { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
+       .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+       },
 
        /* control interfaces with various AT-command sets */
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
index 4c38564..1602324 100644 (file)
@@ -125,6 +125,7 @@ struct acm {
        unsigned char clocal;                           /* termios CLOCAL */
        unsigned int ctrl_caps;                         /* control capabilities from the class specific header */
        unsigned int susp_count;                        /* number of suspended interfaces */
+       int combined_interfaces:1;                      /* control and data collapsed */
        struct acm_wb *delayed_wb;                      /* write queued for a device about to be woken */
 };
 
@@ -133,3 +134,4 @@ struct acm {
 /* constants describing various quirks and errors */
 #define NO_UNION_NORMAL                        1
 #define SINGLE_RX_URB                  2
+#define NO_CAP_LINE                    4
index c40a9b2..3703789 100644 (file)
@@ -927,21 +927,27 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        switch (cmd) {
        case USBTMC_IOCTL_CLEAR_OUT_HALT:
                retval = usbtmc_ioctl_clear_out_halt(data);
+               break;
 
        case USBTMC_IOCTL_CLEAR_IN_HALT:
                retval = usbtmc_ioctl_clear_in_halt(data);
+               break;
 
        case USBTMC_IOCTL_INDICATOR_PULSE:
                retval = usbtmc_ioctl_indicator_pulse(data);
+               break;
 
        case USBTMC_IOCTL_CLEAR:
                retval = usbtmc_ioctl_clear(data);
+               break;
 
        case USBTMC_IOCTL_ABORT_BULK_OUT:
                retval = usbtmc_ioctl_abort_bulk_out(data);
+               break;
 
        case USBTMC_IOCTL_ABORT_BULK_IN:
                retval = usbtmc_ioctl_abort_bulk_in(data);
+               break;
        }
 
        mutex_unlock(&data->io_mutex);
index e1759d1..69280c3 100644 (file)
@@ -28,7 +28,7 @@ comment "Miscellaneous USB options"
        depends on USB
 
 config USB_DEVICEFS
-       bool "USB device filesystem"
+       bool "USB device filesystem (DEPRECATED)" if EMBEDDED
        depends on USB
        ---help---
          If you say Y here (and to "/proc file system support" in the "File
@@ -46,11 +46,15 @@ config USB_DEVICEFS
          For the format of the various /proc/bus/usb/ files, please read
          <file:Documentation/usb/proc_usb_info.txt>.
 
-         Usbfs files can't handle Access Control Lists (ACL), which are the
-         default way to grant access to USB devices for untrusted users of a
-         desktop system. The usbfs functionality is replaced by real
-         device-nodes managed by udev. These nodes live in /dev/bus/usb and
-         are used by libusb.
+         Modern Linux systems do not use this.
+
+         Usbfs entries are files and not character devices; usbfs can't
+         handle Access Control Lists (ACL) which are the default way to
+         grant access to USB devices for untrusted users of a desktop
+         system.
+
+         The usbfs functionality is replaced by real device-nodes managed by
+         udev.  These nodes lived in /dev/bus/usb and are used by libusb.
 
 config USB_DEVICE_CLASS
        bool "USB device class-devices (DEPRECATED)"
index b607870..ec16e60 100644 (file)
@@ -4,14 +4,14 @@
 
 usbcore-objs   := usb.o hub.o hcd.o urb.o message.o driver.o \
                        config.o file.o buffer.o sysfs.o endpoint.o \
-                       devio.o notify.o generic.o quirks.o
+                       devio.o notify.o generic.o quirks.o devices.o
 
 ifeq ($(CONFIG_PCI),y)
        usbcore-objs    += hcd-pci.o
 endif
 
 ifeq ($(CONFIG_USB_DEVICEFS),y)
-       usbcore-objs    += inode.o devices.o
+       usbcore-objs    += inode.o
 endif
 
 obj-$(CONFIG_USB)      += usbcore.o
index 568244c..24dfb33 100644 (file)
@@ -19,6 +19,32 @@ static inline const char *plural(int n)
        return (n == 1 ? "" : "s");
 }
 
+/* FIXME: this is a kludge */
+static int find_next_descriptor_more(unsigned char *buffer, int size,
+    int dt1, int dt2, int dt3, int *num_skipped)
+{
+       struct usb_descriptor_header *h;
+       int n = 0;
+       unsigned char *buffer0 = buffer;
+
+       /* Find the next descriptor of type dt1 or dt2 or dt3 */
+       while (size > 0) {
+               h = (struct usb_descriptor_header *) buffer;
+               if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2 ||
+                               h->bDescriptorType == dt3)
+                       break;
+               buffer += h->bLength;
+               size -= h->bLength;
+               ++n;
+       }
+
+       /* Store the number of descriptors skipped and return the
+        * number of bytes skipped */
+       if (num_skipped)
+               *num_skipped = n;
+       return buffer - buffer0;
+}
+
 static int find_next_descriptor(unsigned char *buffer, int size,
     int dt1, int dt2, int *num_skipped)
 {
@@ -43,6 +69,129 @@ static int find_next_descriptor(unsigned char *buffer, int size,
        return buffer - buffer0;
 }
 
+static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
+               int inum, int asnum, struct usb_host_endpoint *ep,
+               int num_ep, unsigned char *buffer, int size)
+{
+       unsigned char *buffer_start = buffer;
+       struct usb_ss_ep_comp_descriptor        *desc;
+       int retval;
+       int num_skipped;
+       int max_tx;
+       int i;
+
+       /* Allocate space for the SS endpoint companion descriptor */
+       ep->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp),
+                       GFP_KERNEL);
+       if (!ep->ss_ep_comp)
+               return -ENOMEM;
+       desc = (struct usb_ss_ep_comp_descriptor *) buffer;
+       if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
+               dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "
+                               " interface %d altsetting %d ep %d: "
+                               "using minimum values\n",
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               ep->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE;
+               ep->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
+               ep->ss_ep_comp->desc.bMaxBurst = 0;
+               /*
+                * Leave bmAttributes as zero, which will mean no streams for
+                * bulk, and isoc won't support multiple bursts of packets.
+                * With bursts of only one packet, and a Mult of 1, the max
+                * amount of data moved per endpoint service interval is one
+                * packet.
+                */
+               if (usb_endpoint_xfer_isoc(&ep->desc) ||
+                               usb_endpoint_xfer_int(&ep->desc))
+                       ep->ss_ep_comp->desc.wBytesPerInterval =
+                               ep->desc.wMaxPacketSize;
+               /*
+                * The next descriptor is for an Endpoint or Interface,
+                * no extra descriptors to copy into the companion structure,
+                * and we didn't eat up any of the buffer.
+                */
+               retval = 0;
+               goto valid;
+       }
+       memcpy(&ep->ss_ep_comp->desc, desc, USB_DT_SS_EP_COMP_SIZE);
+       desc = &ep->ss_ep_comp->desc;
+       buffer += desc->bLength;
+       size -= desc->bLength;
+
+       /* Eat up the other descriptors we don't care about */
+       ep->ss_ep_comp->extra = buffer;
+       i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
+                       USB_DT_INTERFACE, &num_skipped);
+       ep->ss_ep_comp->extralen = i;
+       buffer += i;
+       size -= i;
+       retval = buffer - buffer_start + i;
+       if (num_skipped > 0)
+               dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
+                               num_skipped, plural(num_skipped),
+                               "SuperSpeed endpoint companion");
+
+       /* Check the various values */
+       if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
+               dev_warn(ddev, "Control endpoint with bMaxBurst = %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to zero\n", desc->bMaxBurst,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bMaxBurst = 0;
+       }
+       if (desc->bMaxBurst > 15) {
+               dev_warn(ddev, "Endpoint with bMaxBurst = %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to 15\n", desc->bMaxBurst,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bMaxBurst = 15;
+       }
+       if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc))
+                       && desc->bmAttributes != 0) {
+               dev_warn(ddev, "%s endpoint with bmAttributes = %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to zero\n",
+                               usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
+                               desc->bmAttributes,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bmAttributes = 0;
+       }
+       if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) {
+               dev_warn(ddev, "Bulk endpoint with more than 65536 streams in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to max\n",
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bmAttributes = 16;
+       }
+       if (usb_endpoint_xfer_isoc(&ep->desc) && desc->bmAttributes > 2) {
+               dev_warn(ddev, "Isoc endpoint has Mult of %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to 3\n", desc->bmAttributes + 1,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bmAttributes = 2;
+       }
+       if (usb_endpoint_xfer_isoc(&ep->desc)) {
+               max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) *
+                       (desc->bmAttributes + 1);
+       } else if (usb_endpoint_xfer_int(&ep->desc)) {
+               max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1);
+       } else {
+               goto valid;
+       }
+       if (desc->wBytesPerInterval > max_tx) {
+               dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to %d\n",
+                               usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
+                               desc->wBytesPerInterval,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress,
+                               max_tx);
+               desc->wBytesPerInterval = max_tx;
+       }
+valid:
+       return retval;
+}
+
 static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
     int asnum, struct usb_host_interface *ifp, int num_ep,
     unsigned char *buffer, int size)
@@ -50,7 +199,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
        unsigned char *buffer0 = buffer;
        struct usb_endpoint_descriptor *d;
        struct usb_host_endpoint *endpoint;
-       int n, i, j;
+       int n, i, j, retval;
 
        d = (struct usb_endpoint_descriptor *) buffer;
        buffer += d->bLength;
@@ -92,6 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
        if (usb_endpoint_xfer_int(d)) {
                i = 1;
                switch (to_usb_device(ddev)->speed) {
+               case USB_SPEED_SUPER:
                case USB_SPEED_HIGH:
                        /* Many device manufacturers are using full-speed
                         * bInterval values in high-speed interrupt endpoint
@@ -161,17 +311,39 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
                                cfgno, inum, asnum, d->bEndpointAddress,
                                maxp);
        }
-
-       /* Skip over any Class Specific or Vendor Specific descriptors;
-        * find the next endpoint or interface descriptor */
-       endpoint->extra = buffer;
-       i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
-           USB_DT_INTERFACE, &n);
-       endpoint->extralen = i;
+       /* Allocate room for and parse any SS endpoint companion descriptors */
+       if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) {
+               endpoint->extra = buffer;
+               i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP,
+                               USB_DT_ENDPOINT, USB_DT_INTERFACE, &n);
+               endpoint->extralen = i;
+               buffer += i;
+               size -= i;
+
+               if (size > 0) {
+                       retval = usb_parse_ss_endpoint_companion(ddev, cfgno,
+                                       inum, asnum, endpoint, num_ep, buffer,
+                                       size);
+                       if (retval >= 0) {
+                               buffer += retval;
+                               retval = buffer - buffer0;
+                       }
+               } else {
+                       retval = buffer - buffer0;
+               }
+       } else {
+               /* Skip over any Class Specific or Vendor Specific descriptors;
+                * find the next endpoint or interface descriptor */
+               endpoint->extra = buffer;
+               i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
+                               USB_DT_INTERFACE, &n);
+               endpoint->extralen = i;
+               retval = buffer - buffer0 + i;
+       }
        if (n > 0)
                dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
                    n, plural(n), "endpoint");
-       return buffer - buffer0 + i;
+       return retval;
 
 skip_to_next_endpoint_or_interface_descriptor:
        i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
@@ -452,6 +624,8 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
                kref_init(&intfc->ref);
        }
 
+       /* FIXME: parse the BOS descriptor */
+
        /* Skip over any Class Specific or Vendor Specific descriptors;
         * find the first interface descriptor */
        config->extra = buffer;
index d0a21a5..69e5773 100644 (file)
@@ -154,16 +154,11 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in
 static int usb_probe_device(struct device *dev)
 {
        struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
-       struct usb_device *udev;
+       struct usb_device *udev = to_usb_device(dev);
        int error = -ENODEV;
 
        dev_dbg(dev, "%s\n", __func__);
 
-       if (!is_usb_device(dev))        /* Sanity check */
-               return error;
-
-       udev = to_usb_device(dev);
-
        /* TODO: Add real matching code */
 
        /* The device should always appear to be in use
@@ -203,18 +198,13 @@ static void usb_cancel_queued_reset(struct usb_interface *iface)
 static int usb_probe_interface(struct device *dev)
 {
        struct usb_driver *driver = to_usb_driver(dev->driver);
-       struct usb_interface *intf;
-       struct usb_device *udev;
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct usb_device *udev = interface_to_usbdev(intf);
        const struct usb_device_id *id;
        int error = -ENODEV;
 
        dev_dbg(dev, "%s\n", __func__);
 
-       if (is_usb_device(dev))         /* Sanity check */
-               return error;
-
-       intf = to_usb_interface(dev);
-       udev = interface_to_usbdev(intf);
        intf->needs_binding = 0;
 
        if (udev->authorized == 0) {
@@ -385,7 +375,6 @@ void usb_driver_release_interface(struct usb_driver *driver,
                                        struct usb_interface *iface)
 {
        struct device *dev = &iface->dev;
-       struct usb_device *udev = interface_to_usbdev(iface);
 
        /* this should never happen, don't release something that's not ours */
        if (!dev->driver || dev->driver != &driver->drvwrap.driver)
@@ -394,23 +383,19 @@ void usb_driver_release_interface(struct usb_driver *driver,
        /* don't release from within disconnect() */
        if (iface->condition != USB_INTERFACE_BOUND)
                return;
+       iface->condition = USB_INTERFACE_UNBINDING;
 
-       /* don't release if the interface hasn't been added yet */
+       /* Release via the driver core only if the interface
+        * has already been registered
+        */
        if (device_is_registered(dev)) {
-               iface->condition = USB_INTERFACE_UNBINDING;
                device_release_driver(dev);
        } else {
-               iface->condition = USB_INTERFACE_UNBOUND;
-               usb_cancel_queued_reset(iface);
+               down(&dev->sem);
+               usb_unbind_interface(dev);
+               dev->driver = NULL;
+               up(&dev->sem);
        }
-       dev->driver = NULL;
-       usb_set_intfdata(iface, NULL);
-
-       usb_pm_lock(udev);
-       iface->condition = USB_INTERFACE_UNBOUND;
-       mark_quiesced(iface);
-       iface->needs_remote_wakeup = 0;
-       usb_pm_unlock(udev);
 }
 EXPORT_SYMBOL_GPL(usb_driver_release_interface);
 
@@ -598,7 +583,7 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
                /* TODO: Add real matching code */
                return 1;
 
-       } else {
+       } else if (is_usb_interface(dev)) {
                struct usb_interface *intf;
                struct usb_driver *usb_drv;
                const struct usb_device_id *id;
@@ -630,11 +615,14 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
        /* driver is often null here; dev_dbg() would oops */
        pr_debug("usb %s: uevent\n", dev_name(dev));
 
-       if (is_usb_device(dev))
+       if (is_usb_device(dev)) {
                usb_dev = to_usb_device(dev);
-       else {
+       } else if (is_usb_interface(dev)) {
                struct usb_interface *intf = to_usb_interface(dev);
+
                usb_dev = interface_to_usbdev(intf);
+       } else {
+               return 0;
        }
 
        if (usb_dev->devnum < 0) {
@@ -1762,6 +1750,7 @@ int usb_suspend(struct device *dev, pm_message_t msg)
 int usb_resume(struct device *dev, pm_message_t msg)
 {
        struct usb_device       *udev;
+       int                     status;
 
        udev = to_usb_device(dev);
 
@@ -1771,7 +1760,14 @@ int usb_resume(struct device *dev, pm_message_t msg)
         */
        if (udev->skip_sys_resume)
                return 0;
-       return usb_external_resume_device(udev, msg);
+       status = usb_external_resume_device(udev, msg);
+
+       /* Avoid PM error messages for devices disconnected while suspended
+        * as we'll display regular disconnect messages just a bit later.
+        */
+       if (status == -ENODEV)
+               return 0;
+       return status;
 }
 
 #endif /* CONFIG_PM */
index 40dee2a..bc39fc4 100644 (file)
 #include <linux/usb.h>
 #include "usb.h"
 
-#define MAX_ENDPOINT_MINORS (64*128*32)
-static int usb_endpoint_major;
-static DEFINE_IDR(endpoint_idr);
-
 struct ep_device {
        struct usb_endpoint_descriptor *desc;
        struct usb_device *udev;
        struct device dev;
-       int minor;
 };
 #define to_ep_device(_dev) \
        container_of(_dev, struct ep_device, dev)
 
+struct device_type usb_ep_device_type = {
+       .name =         "usb_endpoint",
+};
+
 struct ep_attribute {
        struct attribute attr;
        ssize_t (*show)(struct usb_device *,
@@ -160,118 +159,10 @@ static struct attribute_group *ep_dev_groups[] = {
        NULL
 };
 
-static int usb_endpoint_major_init(void)
-{
-       dev_t dev;
-       int error;
-
-       error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS,
-                                   "usb_endpoint");
-       if (error) {
-               printk(KERN_ERR "Unable to get a dynamic major for "
-                      "usb endpoints.\n");
-               return error;
-       }
-       usb_endpoint_major = MAJOR(dev);
-
-       return error;
-}
-
-static void usb_endpoint_major_cleanup(void)
-{
-       unregister_chrdev_region(MKDEV(usb_endpoint_major, 0),
-                                MAX_ENDPOINT_MINORS);
-}
-
-static int endpoint_get_minor(struct ep_device *ep_dev)
-{
-       static DEFINE_MUTEX(minor_lock);
-       int retval = -ENOMEM;
-       int id;
-
-       mutex_lock(&minor_lock);
-       if (idr_pre_get(&endpoint_idr, GFP_KERNEL) == 0)
-               goto exit;
-
-       retval = idr_get_new(&endpoint_idr, ep_dev, &id);
-       if (retval < 0) {
-               if (retval == -EAGAIN)
-                       retval = -ENOMEM;
-               goto exit;
-       }
-       ep_dev->minor = id & MAX_ID_MASK;
-exit:
-       mutex_unlock(&minor_lock);
-       return retval;
-}
-
-static void endpoint_free_minor(struct ep_device *ep_dev)
-{
-       idr_remove(&endpoint_idr, ep_dev->minor);
-}
-
-static struct endpoint_class {
-       struct kref kref;
-       struct class *class;
-} *ep_class;
-
-static int init_endpoint_class(void)
-{
-       int result = 0;
-
-       if (ep_class != NULL) {
-               kref_get(&ep_class->kref);
-               goto exit;
-       }
-
-       ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL);
-       if (!ep_class) {
-               result = -ENOMEM;
-               goto exit;
-       }
-
-       kref_init(&ep_class->kref);
-       ep_class->class = class_create(THIS_MODULE, "usb_endpoint");
-       if (IS_ERR(ep_class->class)) {
-               result = PTR_ERR(ep_class->class);
-               goto class_create_error;
-       }
-
-       result = usb_endpoint_major_init();
-       if (result)
-               goto endpoint_major_error;
-
-       goto exit;
-
-endpoint_major_error:
-       class_destroy(ep_class->class);
-class_create_error:
-       kfree(ep_class);
-       ep_class = NULL;
-exit:
-       return result;
-}
-
-static void release_endpoint_class(struct kref *kref)
-{
-       /* Ok, we cheat as we know we only have one ep_class */
-       class_destroy(ep_class->class);
-       kfree(ep_class);
-       ep_class = NULL;
-       usb_endpoint_major_cleanup();
-}
-
-static void destroy_endpoint_class(void)
-{
-       if (ep_class)
-               kref_put(&ep_class->kref, release_endpoint_class);
-}
-
 static void ep_device_release(struct device *dev)
 {
        struct ep_device *ep_dev = to_ep_device(dev);
 
-       endpoint_free_minor(ep_dev);
        kfree(ep_dev);
 }
 
@@ -279,62 +170,32 @@ int usb_create_ep_devs(struct device *parent,
                        struct usb_host_endpoint *endpoint,
                        struct usb_device *udev)
 {
-       char name[8];
        struct ep_device *ep_dev;
        int retval;
 
-       retval = init_endpoint_class();
-       if (retval)
-               goto exit;
-
        ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
        if (!ep_dev) {
                retval = -ENOMEM;
-               goto error_alloc;
-       }
-
-       retval = endpoint_get_minor(ep_dev);
-       if (retval) {
-               dev_err(parent, "can not allocate minor number for %s\n",
-                       dev_name(&ep_dev->dev));
-               goto error_register;
+               goto exit;
        }
 
        ep_dev->desc = &endpoint->desc;
        ep_dev->udev = udev;
        ep_dev->dev.groups = ep_dev_groups;
-       ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor);
-       ep_dev->dev.class = ep_class->class;
+       ep_dev->dev.type = &usb_ep_device_type;
        ep_dev->dev.parent = parent;
        ep_dev->dev.release = ep_device_release;
-       dev_set_name(&ep_dev->dev, "usbdev%d.%d_ep%02x",
-                udev->bus->busnum, udev->devnum,
-                endpoint->desc.bEndpointAddress);
+       dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
 
        retval = device_register(&ep_dev->dev);
        if (retval)
-               goto error_chrdev;
+               goto error_register;
 
-       /* create the symlink to the old-style "ep_XX" directory */
-       sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
-       retval = sysfs_create_link(&parent->kobj, &ep_dev->dev.kobj, name);
-       if (retval)
-               goto error_link;
        endpoint->ep_dev = ep_dev;
        return retval;
 
-error_link:
-       device_unregister(&ep_dev->dev);
-       destroy_endpoint_class();
-       return retval;
-
-error_chrdev:
-       endpoint_free_minor(ep_dev);
-
 error_register:
        kfree(ep_dev);
-error_alloc:
-       destroy_endpoint_class();
 exit:
        return retval;
 }
@@ -344,12 +205,7 @@ void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
        struct ep_device *ep_dev = endpoint->ep_dev;
 
        if (ep_dev) {
-               char name[8];
-
-               sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
-               sysfs_remove_link(&ep_dev->dev.parent->kobj, name);
                device_unregister(&ep_dev->dev);
                endpoint->ep_dev = NULL;
-               destroy_endpoint_class();
        }
 }
index a4301dc..91f2885 100644 (file)
@@ -185,194 +185,198 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
 
-
-#ifdef CONFIG_PM
-
 /**
- * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
- * @dev: USB Host Controller being suspended
- * @message: Power Management message describing this state transition
- *
- * Store this function in the HCD's struct pci_driver as .suspend.
+ * usb_hcd_pci_shutdown - shutdown host controller
+ * @dev: USB Host Controller being shutdown
  */
-int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
+void usb_hcd_pci_shutdown(struct pci_dev *dev)
+{
+       struct usb_hcd          *hcd;
+
+       hcd = pci_get_drvdata(dev);
+       if (!hcd)
+               return;
+
+       if (hcd->driver->shutdown)
+               hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int check_root_hub_suspended(struct device *dev)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+
+       if (!(hcd->state == HC_STATE_SUSPENDED ||
+                       hcd->state == HC_STATE_HALT)) {
+               dev_warn(dev, "Root hub is not suspended\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+static int hcd_pci_suspend(struct device *dev)
 {
-       struct usb_hcd          *hcd = pci_get_drvdata(dev);
-       int                     retval = 0;
-       int                     wake, w;
-       int                     has_pci_pm;
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
 
        /* Root hub suspend should have stopped all downstream traffic,
         * and all bus master traffic.  And done so for both the interface
         * and the stub usb_device (which we check here).  But maybe it
         * didn't; writing sysfs power/state files ignores such rules...
-        *
-        * We must ignore the FREEZE vs SUSPEND distinction here, because
-        * otherwise the swsusp will save (and restore) garbage state.
         */
-       if (!(hcd->state == HC_STATE_SUSPENDED ||
-                       hcd->state == HC_STATE_HALT)) {
-               dev_warn(&dev->dev, "Root hub is not suspended\n");
-               retval = -EBUSY;
-               goto done;
-       }
+       retval = check_root_hub_suspended(dev);
+       if (retval)
+               return retval;
 
        /* We might already be suspended (runtime PM -- not yet written) */
-       if (dev->current_state != PCI_D0)
-               goto done;
+       if (pci_dev->current_state != PCI_D0)
+               return retval;
 
        if (hcd->driver->pci_suspend) {
-               retval = hcd->driver->pci_suspend(hcd, message);
+               retval = hcd->driver->pci_suspend(hcd);
                suspend_report_result(hcd->driver->pci_suspend, retval);
                if (retval)
-                       goto done;
+                       return retval;
        }
 
-       synchronize_irq(dev->irq);
+       synchronize_irq(pci_dev->irq);
 
        /* Downstream ports from this root hub should already be quiesced, so
         * there will be no DMA activity.  Now we can shut down the upstream
-        * link (except maybe for PME# resume signaling) and enter some PCI
-        * low power state, if the hardware allows.
+        * link (except maybe for PME# resume signaling).  We'll enter a
+        * low power state during suspend_noirq, if the hardware allows.
         */
-       pci_disable_device(dev);
+       pci_disable_device(pci_dev);
+       return retval;
+}
+
+static int hcd_pci_suspend_noirq(struct device *dev)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
+
+       retval = check_root_hub_suspended(dev);
+       if (retval)
+               return retval;
 
-       pci_save_state(dev);
+       pci_save_state(pci_dev);
 
-       /* Don't fail on error to enable wakeup.  We rely on pci code
-        * to reject requests the hardware can't implement, rather
-        * than coding the same thing.
+       /* If the root hub is HALTed rather than SUSPENDed,
+        * disallow remote wakeup.
         */
-       wake = (hcd->state == HC_STATE_SUSPENDED &&
-                       device_may_wakeup(&dev->dev));
-       w = pci_wake_from_d3(dev, wake);
-       if (w < 0)
-               wake = w;
-       dev_dbg(&dev->dev, "wakeup: %d\n", wake);
-
-       /* Don't change state if we don't need to */
-       if (message.event == PM_EVENT_FREEZE ||
-                       message.event == PM_EVENT_PRETHAW) {
-               dev_dbg(&dev->dev, "--> no state change\n");
-               goto done;
-       }
+       if (hcd->state == HC_STATE_HALT)
+               device_set_wakeup_enable(dev, 0);
+       dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev));
 
-       has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-       if (!has_pci_pm) {
-               dev_dbg(&dev->dev, "--> PCI D0 legacy\n");
+       /* Possibly enable remote wakeup,
+        * choose the appropriate low-power state, and go to that state.
+        */
+       retval = pci_prepare_to_sleep(pci_dev);
+       if (retval == -EIO) {           /* Low-power not supported */
+               dev_dbg(dev, "--> PCI D0 legacy\n");
+               retval = 0;
+       } else if (retval == 0) {
+               dev_dbg(dev, "--> PCI %s\n",
+                               pci_power_name(pci_dev->current_state));
        } else {
-
-               /* NOTE:  dev->current_state becomes nonzero only here, and
-                * only for devices that support PCI PM.  Also, exiting
-                * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset
-                * some device state (e.g. as part of clock reinit).
-                */
-               retval = pci_set_power_state(dev, PCI_D3hot);
-               suspend_report_result(pci_set_power_state, retval);
-               if (retval == 0) {
-                       dev_dbg(&dev->dev, "--> PCI D3\n");
-               } else {
-                       dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n",
-                                       retval);
-                       pci_restore_state(dev);
-               }
+               suspend_report_result(pci_prepare_to_sleep, retval);
+               return retval;
        }
 
 #ifdef CONFIG_PPC_PMAC
-       if (retval == 0) {
-               /* Disable ASIC clocks for USB */
-               if (machine_is(powermac)) {
-                       struct device_node      *of_node;
-
-                       of_node = pci_device_to_OF_node(dev);
-                       if (of_node)
-                               pmac_call_feature(PMAC_FTR_USB_ENABLE,
-                                                       of_node, 0, 0);
-               }
+       /* Disable ASIC clocks for USB */
+       if (machine_is(powermac)) {
+               struct device_node      *of_node;
+
+               of_node = pci_device_to_OF_node(pci_dev);
+               if (of_node)
+                       pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
        }
 #endif
-
- done:
        return retval;
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
 
-/**
- * usb_hcd_pci_resume - power management resume of a PCI-based HCD
- * @dev: USB Host Controller being resumed
- *
- * Store this function in the HCD's struct pci_driver as .resume.
- */
-int usb_hcd_pci_resume(struct pci_dev *dev)
+static int hcd_pci_resume_noirq(struct device *dev)
 {
-       struct usb_hcd          *hcd;
-       int                     retval;
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
 
 #ifdef CONFIG_PPC_PMAC
        /* Reenable ASIC clocks for USB */
        if (machine_is(powermac)) {
                struct device_node *of_node;
 
-               of_node = pci_device_to_OF_node(dev);
+               of_node = pci_device_to_OF_node(pci_dev);
                if (of_node)
                        pmac_call_feature(PMAC_FTR_USB_ENABLE,
                                                of_node, 0, 1);
        }
 #endif
 
-       pci_restore_state(dev);
+       /* Go back to D0 and disable remote wakeup */
+       pci_back_from_sleep(pci_dev);
+       return 0;
+}
+
+static int resume_common(struct device *dev, bool hibernated)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
 
-       hcd = pci_get_drvdata(dev);
        if (hcd->state != HC_STATE_SUSPENDED) {
-               dev_dbg(hcd->self.controller,
-                               "can't resume, not suspended!\n");
+               dev_dbg(dev, "can't resume, not suspended!\n");
                return 0;
        }
 
-       pci_enable_wake(dev, PCI_D0, false);
-
-       retval = pci_enable_device(dev);
+       retval = pci_enable_device(pci_dev);
        if (retval < 0) {
-               dev_err(&dev->dev, "can't re-enable after resume, %d!\n",
-                               retval);
+               dev_err(dev, "can't re-enable after resume, %d!\n", retval);
                return retval;
        }
 
-       pci_set_master(dev);
-
-       /* yes, ignore this result too... */
-       (void) pci_wake_from_d3(dev, 0);
+       pci_set_master(pci_dev);
 
        clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
 
        if (hcd->driver->pci_resume) {
-               retval = hcd->driver->pci_resume(hcd);
+               retval = hcd->driver->pci_resume(hcd, hibernated);
                if (retval) {
-                       dev_err(hcd->self.controller,
-                               "PCI post-resume error %d!\n", retval);
+                       dev_err(dev, "PCI post-resume error %d!\n", retval);
                        usb_hc_died(hcd);
                }
        }
        return retval;
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);
 
-#endif /* CONFIG_PM */
-
-/**
- * usb_hcd_pci_shutdown - shutdown host controller
- * @dev: USB Host Controller being shutdown
- */
-void usb_hcd_pci_shutdown(struct pci_dev *dev)
+static int hcd_pci_resume(struct device *dev)
 {
-       struct usb_hcd          *hcd;
-
-       hcd = pci_get_drvdata(dev);
-       if (!hcd)
-               return;
+       return resume_common(dev, false);
+}
 
-       if (hcd->driver->shutdown)
-               hcd->driver->shutdown(hcd);
+static int hcd_pci_restore(struct device *dev)
+{
+       return resume_common(dev, true);
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
 
+struct dev_pm_ops usb_hcd_pci_pm_ops = {
+       .suspend        = hcd_pci_suspend,
+       .suspend_noirq  = hcd_pci_suspend_noirq,
+       .resume_noirq   = hcd_pci_resume_noirq,
+       .resume         = hcd_pci_resume,
+       .freeze         = check_root_hub_suspended,
+       .freeze_noirq   = check_root_hub_suspended,
+       .thaw_noirq     = NULL,
+       .thaw           = NULL,
+       .poweroff       = hcd_pci_suspend,
+       .poweroff_noirq = hcd_pci_suspend_noirq,
+       .restore_noirq  = hcd_pci_resume_noirq,
+       .restore        = hcd_pci_restore,
+};
+EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
+
+#endif /* CONFIG_PM_SLEEP */
index 42b93da..ce3f453 100644 (file)
@@ -128,6 +128,27 @@ static inline int is_root_hub(struct usb_device *udev)
 #define KERNEL_REL     ((LINUX_VERSION_CODE >> 16) & 0x0ff)
 #define KERNEL_VER     ((LINUX_VERSION_CODE >> 8) & 0x0ff)
 
+/* usb 3.0 root hub device descriptor */
+static const u8 usb3_rh_dev_descriptor[18] = {
+       0x12,       /*  __u8  bLength; */
+       0x01,       /*  __u8  bDescriptorType; Device */
+       0x00, 0x03, /*  __le16 bcdUSB; v3.0 */
+
+       0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
+       0x00,       /*  __u8  bDeviceSubClass; */
+       0x03,       /*  __u8  bDeviceProtocol; USB 3.0 hub */
+       0x09,       /*  __u8  bMaxPacketSize0; 2^9 = 512 Bytes */
+
+       0x6b, 0x1d, /*  __le16 idVendor; Linux Foundation */
+       0x02, 0x00, /*  __le16 idProduct; device 0x0002 */
+       KERNEL_VER, KERNEL_REL, /*  __le16 bcdDevice */
+
+       0x03,       /*  __u8  iManufacturer; */
+       0x02,       /*  __u8  iProduct; */
+       0x01,       /*  __u8  iSerialNumber; */
+       0x01        /*  __u8  bNumConfigurations; */
+};
+
 /* usb 2.0 root hub device descriptor */
 static const u8 usb2_rh_dev_descriptor [18] = {
        0x12,       /*  __u8  bLength; */
@@ -273,6 +294,47 @@ static const u8 hs_rh_config_descriptor [] = {
        0x0c        /*  __u8  ep_bInterval; (256ms -- usb 2.0 spec) */
 };
 
+static const u8 ss_rh_config_descriptor[] = {
+       /* one configuration */
+       0x09,       /*  __u8  bLength; */
+       0x02,       /*  __u8  bDescriptorType; Configuration */
+       0x19, 0x00, /*  __le16 wTotalLength; FIXME */
+       0x01,       /*  __u8  bNumInterfaces; (1) */
+       0x01,       /*  __u8  bConfigurationValue; */
+       0x00,       /*  __u8  iConfiguration; */
+       0xc0,       /*  __u8  bmAttributes;
+                                Bit 7: must be set,
+                                    6: Self-powered,
+                                    5: Remote wakeup,
+                                    4..0: resvd */
+       0x00,       /*  __u8  MaxPower; */
+
+       /* one interface */
+       0x09,       /*  __u8  if_bLength; */
+       0x04,       /*  __u8  if_bDescriptorType; Interface */
+       0x00,       /*  __u8  if_bInterfaceNumber; */
+       0x00,       /*  __u8  if_bAlternateSetting; */
+       0x01,       /*  __u8  if_bNumEndpoints; */
+       0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+       0x00,       /*  __u8  if_bInterfaceSubClass; */
+       0x00,       /*  __u8  if_bInterfaceProtocol; */
+       0x00,       /*  __u8  if_iInterface; */
+
+       /* one endpoint (status change endpoint) */
+       0x07,       /*  __u8  ep_bLength; */
+       0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+       0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+                   /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
+                    * see hub.c:hub_configure() for details. */
+       (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
+       0x0c        /*  __u8  ep_bInterval; (256ms -- usb 2.0 spec) */
+       /*
+        * All 3.0 hubs should have an endpoint companion descriptor,
+        * but we're ignoring that for now.  FIXME?
+        */
+};
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -426,23 +488,39 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
        case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
                switch (wValue & 0xff00) {
                case USB_DT_DEVICE << 8:
-                       if (hcd->driver->flags & HCD_USB2)
+                       switch (hcd->driver->flags & HCD_MASK) {
+                       case HCD_USB3:
+                               bufp = usb3_rh_dev_descriptor;
+                               break;
+                       case HCD_USB2:
                                bufp = usb2_rh_dev_descriptor;
-                       else if (hcd->driver->flags & HCD_USB11)
+                               break;
+                       case HCD_USB11:
                                bufp = usb11_rh_dev_descriptor;
-                       else
+                               break;
+                       default:
                                goto error;
+                       }
                        len = 18;
                        if (hcd->has_tt)
                                patch_protocol = 1;
                        break;
                case USB_DT_CONFIG << 8:
-                       if (hcd->driver->flags & HCD_USB2) {
+                       switch (hcd->driver->flags & HCD_MASK) {
+                       case HCD_USB3:
+                               bufp = ss_rh_config_descriptor;
+                               len = sizeof ss_rh_config_descriptor;
+                               break;
+                       case HCD_USB2:
                                bufp = hs_rh_config_descriptor;
                                len = sizeof hs_rh_config_descriptor;
-                       } else {
+                               break;
+                       case HCD_USB11:
                                bufp = fs_rh_config_descriptor;
                                len = sizeof fs_rh_config_descriptor;
+                               break;
+                       default:
+                               goto error;
                        }
                        if (device_can_wakeup(&hcd->self.root_hub->dev))
                                patch_wakeup = 1;
@@ -755,23 +833,6 @@ static struct attribute_group usb_bus_attr_group = {
 
 /*-------------------------------------------------------------------------*/
 
-static struct class *usb_host_class;
-
-int usb_host_init(void)
-{
-       int retval = 0;
-
-       usb_host_class = class_create(THIS_MODULE, "usb_host");
-       if (IS_ERR(usb_host_class))
-               retval = PTR_ERR(usb_host_class);
-       return retval;
-}
-
-void usb_host_cleanup(void)
-{
-       class_destroy(usb_host_class);
-}
-
 /**
  * usb_bus_init - shared initialization code
  * @bus: the bus structure being initialized
@@ -818,12 +879,6 @@ static int usb_register_bus(struct usb_bus *bus)
        set_bit (busnum, busmap.busmap);
        bus->busnum = busnum;
 
-       bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0),
-                                bus, "usb_host%d", busnum);
-       result = PTR_ERR(bus->dev);
-       if (IS_ERR(bus->dev))
-               goto error_create_class_dev;
-
        /* Add it to the local list of buses */
        list_add (&bus->bus_list, &usb_bus_list);
        mutex_unlock(&usb_bus_list_lock);
@@ -834,8 +889,6 @@ static int usb_register_bus(struct usb_bus *bus)
                  "number %d\n", bus->busnum);
        return 0;
 
-error_create_class_dev:
-       clear_bit(busnum, busmap.busmap);
 error_find_busnum:
        mutex_unlock(&usb_bus_list_lock);
        return result;
@@ -865,8 +918,6 @@ static void usb_deregister_bus (struct usb_bus *bus)
        usb_notify_remove_bus(bus);
 
        clear_bit (bus->busnum, busmap.busmap);
-
-       device_unregister(bus->dev);
 }
 
 /**
@@ -1199,7 +1250,8 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
 
        /* Map the URB's buffers for DMA access.
         * Lower level HCD code should use *_dma exclusively,
-        * unless it uses pio or talks to another transport.
+        * unless it uses pio or talks to another transport,
+        * or uses the provided scatter gather list for bulk.
         */
        if (is_root_hub(urb->dev))
                return 0;
@@ -1520,6 +1572,92 @@ rescan:
        }
 }
 
+/* Check whether a new configuration or alt setting for an interface
+ * will exceed the bandwidth for the bus (or the host controller resources).
+ * Only pass in a non-NULL config or interface, not both!
+ * Passing NULL for both new_config and new_intf means the device will be
+ * de-configured by issuing a set configuration 0 command.
+ */
+int usb_hcd_check_bandwidth(struct usb_device *udev,
+               struct usb_host_config *new_config,
+               struct usb_interface *new_intf)
+{
+       int num_intfs, i, j;
+       struct usb_interface_cache *intf_cache;
+       struct usb_host_interface *alt = 0;
+       int ret = 0;
+       struct usb_hcd *hcd;
+       struct usb_host_endpoint *ep;
+
+       hcd = bus_to_hcd(udev->bus);
+       if (!hcd->driver->check_bandwidth)
+               return 0;
+
+       /* Configuration is being removed - set configuration 0 */
+       if (!new_config && !new_intf) {
+               for (i = 1; i < 16; ++i) {
+                       ep = udev->ep_out[i];
+                       if (ep)
+                               hcd->driver->drop_endpoint(hcd, udev, ep);
+                       ep = udev->ep_in[i];
+                       if (ep)
+                               hcd->driver->drop_endpoint(hcd, udev, ep);
+               }
+               hcd->driver->check_bandwidth(hcd, udev);
+               return 0;
+       }
+       /* Check if the HCD says there's enough bandwidth.  Enable all endpoints
+        * each interface's alt setting 0 and ask the HCD to check the bandwidth
+        * of the bus.  There will always be bandwidth for endpoint 0, so it's
+        * ok to exclude it.
+        */
+       if (new_config) {
+               num_intfs = new_config->desc.bNumInterfaces;
+               /* Remove endpoints (except endpoint 0, which is always on the
+                * schedule) from the old config from the schedule
+                */
+               for (i = 1; i < 16; ++i) {
+                       ep = udev->ep_out[i];
+                       if (ep) {
+                               ret = hcd->driver->drop_endpoint(hcd, udev, ep);
+                               if (ret < 0)
+                                       goto reset;
+                       }
+                       ep = udev->ep_in[i];
+                       if (ep) {
+                               ret = hcd->driver->drop_endpoint(hcd, udev, ep);
+                               if (ret < 0)
+                                       goto reset;
+                       }
+               }
+               for (i = 0; i < num_intfs; ++i) {
+
+                       /* Dig the endpoints for alt setting 0 out of the
+                        * interface cache for this interface
+                        */
+                       intf_cache = new_config->intf_cache[i];
+                       for (j = 0; j < intf_cache->num_altsetting; j++) {
+                               if (intf_cache->altsetting[j].desc.bAlternateSetting == 0)
+                                       alt = &intf_cache->altsetting[j];
+                       }
+                       if (!alt) {
+                               printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i);
+                               continue;
+                       }
+                       for (j = 0; j < alt->desc.bNumEndpoints; j++) {
+                               ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
+                               if (ret < 0)
+                                       goto reset;
+                       }
+               }
+       }
+       ret = hcd->driver->check_bandwidth(hcd, udev);
+reset:
+       if (ret < 0)
+               hcd->driver->reset_bandwidth(hcd, udev);
+       return ret;
+}
+
 /* Disables the endpoint: synchronizes with the hcd to make sure all
  * endpoint state is gone from hardware.  usb_hcd_flush_endpoint() must
  * have been called previously.  Use for set_configuration, set_interface,
@@ -1897,8 +2035,20 @@ int usb_add_hcd(struct usb_hcd *hcd,
                retval = -ENOMEM;
                goto err_allocate_root_hub;
        }
-       rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
-                       USB_SPEED_FULL;
+
+       switch (hcd->driver->flags & HCD_MASK) {
+       case HCD_USB11:
+               rhdev->speed = USB_SPEED_FULL;
+               break;
+       case HCD_USB2:
+               rhdev->speed = USB_SPEED_HIGH;
+               break;
+       case HCD_USB3:
+               rhdev->speed = USB_SPEED_SUPER;
+               break;
+       default:
+               goto err_allocate_root_hub;
+       }
        hcd->self.root_hub = rhdev;
 
        /* wakeup flag init defaults to "everything works" for root hubs,
index e7d4479..d397ecf 100644 (file)
@@ -173,6 +173,8 @@ struct hc_driver {
 #define        HCD_LOCAL_MEM   0x0002          /* HC needs local memory */
 #define        HCD_USB11       0x0010          /* USB 1.1 */
 #define        HCD_USB2        0x0020          /* USB 2.0 */
+#define        HCD_USB3        0x0040          /* USB 3.0 */
+#define        HCD_MASK        0x0070
 
        /* called to init HCD and root hub */
        int     (*reset) (struct usb_hcd *hcd);
@@ -182,10 +184,10 @@ struct hc_driver {
         * a whole, not just the root hub; they're for PCI bus glue.
         */
        /* called after suspending the hub, before entering D3 etc */
-       int     (*pci_suspend) (struct usb_hcd *hcd, pm_message_t message);
+       int     (*pci_suspend)(struct usb_hcd *hcd);
 
        /* called after entering D0 (etc), before resuming the hub */
-       int     (*pci_resume) (struct usb_hcd *hcd);
+       int     (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
 
        /* cleanly make HCD stop writing memory and doing I/O */
        void    (*stop) (struct usb_hcd *hcd);
@@ -224,6 +226,43 @@ struct hc_driver {
        void    (*relinquish_port)(struct usb_hcd *, int);
                /* has a port been handed over to a companion? */
        int     (*port_handed_over)(struct usb_hcd *, int);
+
+       /* xHCI specific functions */
+               /* Called by usb_alloc_dev to alloc HC device structures */
+       int     (*alloc_dev)(struct usb_hcd *, struct usb_device *);
+               /* Called by usb_release_dev to free HC device structures */
+       void    (*free_dev)(struct usb_hcd *, struct usb_device *);
+
+       /* Bandwidth computation functions */
+       /* Note that add_endpoint() can only be called once per endpoint before
+        * check_bandwidth() or reset_bandwidth() must be called.
+        * drop_endpoint() can only be called once per endpoint also.
+        * A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will
+        * add the endpoint to the schedule with possibly new parameters denoted by a
+        * different endpoint descriptor in usb_host_endpoint.
+        * A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is
+        * not allowed.
+        */
+               /* Allocate endpoint resources and add them to a new schedule */
+       int     (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
+               /* Drop an endpoint from a new schedule */
+       int     (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
+               /* Check that a new hardware configuration, set using
+                * endpoint_enable and endpoint_disable, does not exceed bus
+                * bandwidth.  This must be called before any set configuration
+                * or set interface requests are sent to the device.
+                */
+       int     (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
+               /* Reset the device schedule to the last known good schedule,
+                * which was set from a previous successful call to
+                * check_bandwidth().  This reverts any add_endpoint() and
+                * drop_endpoint() calls since that last successful call.
+                * Used for when a check_bandwidth() call fails due to resource
+                * or bandwidth constraints.
+                */
+       void    (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
+               /* Returns the hardware-chosen device address */
+       int     (*address_device)(struct usb_hcd *, struct usb_device *udev);
 };
 
 extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
@@ -242,6 +281,9 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
 extern void usb_hcd_reset_endpoint(struct usb_device *udev,
                struct usb_host_endpoint *ep);
 extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
+extern int usb_hcd_check_bandwidth(struct usb_device *udev,
+               struct usb_host_config *new_config,
+               struct usb_interface *new_intf);
 extern int usb_hcd_get_frame_number(struct usb_device *udev);
 
 extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
@@ -261,14 +303,11 @@ struct pci_device_id;
 extern int usb_hcd_pci_probe(struct pci_dev *dev,
                                const struct pci_device_id *id);
 extern void usb_hcd_pci_remove(struct pci_dev *dev);
-
-#ifdef CONFIG_PM
-extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
-extern int usb_hcd_pci_resume(struct pci_dev *dev);
-#endif /* CONFIG_PM */
-
 extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
 
+#ifdef CONFIG_PM_SLEEP
+extern struct dev_pm_ops       usb_hcd_pci_pm_ops;
+#endif
 #endif /* CONFIG_PCI */
 
 /* pci-ish (pdev null is ok) buffer alloc/mapping support */
index be86ae3..2af3b4f 100644 (file)
@@ -155,6 +155,8 @@ static inline char *portspeed(int portstatus)
                return "480 Mb/s";
        else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
                return "1.5 Mb/s";
+       else if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))
+               return "5.0 Gb/s";
        else
                return "12 Mb/s";
 }
@@ -457,13 +459,13 @@ static void hub_tt_kevent (struct work_struct *work)
 
        spin_lock_irqsave (&hub->tt.lock, flags);
        while (--limit && !list_empty (&hub->tt.clear_list)) {
-               struct list_head        *temp;
+               struct list_head        *next;
                struct usb_tt_clear     *clear;
                struct usb_device       *hdev = hub->hdev;
                int                     status;
 
-               temp = hub->tt.clear_list.next;
-               clear = list_entry (temp, struct usb_tt_clear, clear_list);
+               next = hub->tt.clear_list.next;
+               clear = list_entry (next, struct usb_tt_clear, clear_list);
                list_del (&clear->clear_list);
 
                /* drop lock so HCD can concurrently report other TT errors */
@@ -951,6 +953,9 @@ static int hub_configure(struct usb_hub *hub,
                                        ret);
                        hub->tt.hub = hdev;
                        break;
+               case 3:
+                       /* USB 3.0 hubs don't have a TT */
+                       break;
                default:
                        dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
                                hdev->descriptor.bDeviceProtocol);
@@ -1323,6 +1328,11 @@ EXPORT_SYMBOL_GPL(usb_set_device_state);
  * 0 is reserved by USB for default address; (b) Linux's USB stack
  * uses always #1 for the root hub of the controller. So USB stack's
  * port #1, which is wusb virtual-port #0 has address #2.
+ *
+ * Devices connected under xHCI are not as simple.  The host controller
+ * supports virtualization, so the hardware assigns device addresses and
+ * the HCD must setup data structures before issuing a set address
+ * command to the hardware.
  */
 static void choose_address(struct usb_device *udev)
 {
@@ -1642,6 +1652,9 @@ int usb_new_device(struct usb_device *udev)
        err = usb_configure_device(udev);       /* detect & probe dev/intfs */
        if (err < 0)
                goto fail;
+       dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
+                       udev->devnum, udev->bus->busnum,
+                       (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
        /* export the usbdev device-node for libusb */
        udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
                        (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
@@ -2395,19 +2408,29 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit);
 static int hub_set_address(struct usb_device *udev, int devnum)
 {
        int retval;
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
 
-       if (devnum <= 1)
+       /*
+        * The host controller will choose the device address,
+        * instead of the core having chosen it earlier
+        */
+       if (!hcd->driver->address_device && devnum <= 1)
                return -EINVAL;
        if (udev->state == USB_STATE_ADDRESS)
                return 0;
        if (udev->state != USB_STATE_DEFAULT)
                return -EINVAL;
-       retval = usb_control_msg(udev, usb_sndaddr0pipe(),
-               USB_REQ_SET_ADDRESS, 0, devnum, 0,
-               NULL, 0, USB_CTRL_SET_TIMEOUT);
+       if (hcd->driver->address_device) {
+               retval = hcd->driver->address_device(hcd, udev);
+       } else {
+               retval = usb_control_msg(udev, usb_sndaddr0pipe(),
+                               USB_REQ_SET_ADDRESS, 0, devnum, 0,
+                               NULL, 0, USB_CTRL_SET_TIMEOUT);
+               if (retval == 0)
+                       update_address(udev, devnum);
+       }
        if (retval == 0) {
                /* Device now using proper address. */
-               update_address(udev, devnum);
                usb_set_device_state(udev, USB_STATE_ADDRESS);
                usb_ep0_reinit(udev);
        }
@@ -2430,6 +2453,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        static DEFINE_MUTEX(usb_address0_mutex);
 
        struct usb_device       *hdev = hub->hdev;
+       struct usb_hcd          *hcd = bus_to_hcd(hdev->bus);
        int                     i, j, retval;
        unsigned                delay = HUB_SHORT_RESET_TIME;
        enum usb_device_speed   oldspeed = udev->speed;
@@ -2452,11 +2476,24 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 
        mutex_lock(&usb_address0_mutex);
 
-       /* Reset the device; full speed may morph to high speed */
-       retval = hub_port_reset(hub, port1, udev, delay);
-       if (retval < 0)         /* error or disconnect */
+       if ((hcd->driver->flags & HCD_USB3) && udev->config) {
+               /* FIXME this will need special handling by the xHCI driver. */
+               dev_dbg(&udev->dev,
+                               "xHCI reset of configured device "
+                               "not supported yet.\n");
+               retval = -EINVAL;
                goto fail;
-                               /* success, speed is known */
+       } else if (!udev->config && oldspeed == USB_SPEED_SUPER) {
+               /* Don't reset USB 3.0 devices during an initial setup */
+               usb_set_device_state(udev, USB_STATE_DEFAULT);
+       } else {
+               /* Reset the device; full speed may morph to high speed */
+               /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
+               retval = hub_port_reset(hub, port1, udev, delay);
+               if (retval < 0)         /* error or disconnect */
+                       goto fail;
+               /* success, speed is known */
+       }
        retval = -ENODEV;
 
        if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
@@ -2471,6 +2508,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
         */
        switch (udev->speed) {
+       case USB_SPEED_SUPER:
        case USB_SPEED_VARIABLE:        /* fixed at 512 */
                udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
                break;
@@ -2496,16 +2534,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        case USB_SPEED_LOW:     speed = "low";  break;
        case USB_SPEED_FULL:    speed = "full"; break;
        case USB_SPEED_HIGH:    speed = "high"; break;
+       case USB_SPEED_SUPER:
+                               speed = "super";
+                               break;
        case USB_SPEED_VARIABLE:
                                speed = "variable";
                                type = "Wireless ";
                                break;
        default:                speed = "?";    break;
        }
-       dev_info (&udev->dev,
-                 "%s %s speed %sUSB device using %s and address %d\n",
-                 (udev->config) ? "reset" : "new", speed, type,
-                 udev->bus->controller->driver->name, devnum);
+       if (udev->speed != USB_SPEED_SUPER)
+               dev_info(&udev->dev,
+                               "%s %s speed %sUSB device using %s and address %d\n",
+                               (udev->config) ? "reset" : "new", speed, type,
+                               udev->bus->controller->driver->name, devnum);
 
        /* Set up TT records, if needed  */
        if (hdev->tt) {
@@ -2530,7 +2572,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         * value.
         */
        for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
-               if (USE_NEW_SCHEME(retry_counter)) {
+               /*
+                * An xHCI controller cannot send any packets to a device until
+                * a set address command successfully completes.
+                */
+               if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
                        struct usb_device_descriptor *buf;
                        int r = 0;
 
@@ -2596,7 +2642,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                 * unauthorized address in the Connect Ack sequence;
                 * authorization will assign the final address.
                 */
-               if (udev->wusb == 0) {
+               if (udev->wusb == 0) {
                        for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
                                retval = hub_set_address(udev, devnum);
                                if (retval >= 0)
@@ -2609,13 +2655,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                                        devnum, retval);
                                goto fail;
                        }
+                       if (udev->speed == USB_SPEED_SUPER) {
+                               devnum = udev->devnum;
+                               dev_info(&udev->dev,
+                                               "%s SuperSpeed USB device using %s and address %d\n",
+                                               (udev->config) ? "reset" : "new",
+                                               udev->bus->controller->driver->name, devnum);
+                       }
 
                        /* cope with hardware quirkiness:
                         *  - let SET_ADDRESS settle, some device hardware wants it
                         *  - read ep0 maxpacket even for high and low speed,
                         */
                        msleep(10);
-                       if (USE_NEW_SCHEME(retry_counter))
+                       if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
                                break;
                }
 
@@ -2634,8 +2687,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        if (retval)
                goto fail;
 
-       i = udev->descriptor.bMaxPacketSize0 == 0xff?   /* wusb device? */
-           512 : udev->descriptor.bMaxPacketSize0;
+       if (udev->descriptor.bMaxPacketSize0 == 0xff ||
+                       udev->speed == USB_SPEED_SUPER)
+               i = 512;
+       else
+               i = udev->descriptor.bMaxPacketSize0;
        if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
                if (udev->speed != USB_SPEED_FULL ||
                                !(i == 8 || i == 16 || i == 32 || i == 64)) {
@@ -2847,19 +2903,41 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
 
                usb_set_device_state(udev, USB_STATE_POWERED);
-               udev->speed = USB_SPEED_UNKNOWN;
                udev->bus_mA = hub->mA_per_port;
                udev->level = hdev->level + 1;
                udev->wusb = hub_is_wusb(hub);
 
-               /* set the address */
-               choose_address(udev);
-               if (udev->devnum <= 0) {
-                       status = -ENOTCONN;     /* Don't retry */
-                       goto loop;
+               /*
+                * USB 3.0 devices are reset automatically before the connect
+                * port status change appears, and the root hub port status
+                * shows the correct speed.  We also get port change
+                * notifications for USB 3.0 devices from the USB 3.0 portion of
+                * an external USB 3.0 hub, but this isn't handled correctly yet
+                * FIXME.
+                */
+
+               if (!(hcd->driver->flags & HCD_USB3))
+                       udev->speed = USB_SPEED_UNKNOWN;
+               else if ((hdev->parent == NULL) &&
+                               (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED)))
+                       udev->speed = USB_SPEED_SUPER;
+               else
+                       udev->speed = USB_SPEED_UNKNOWN;
+
+               /*
+                * xHCI needs to issue an address device command later
+                * in the hub_port_init sequence for SS/HS/FS/LS devices.
+                */
+               if (!(hcd->driver->flags & HCD_USB3)) {
+                       /* set the address */
+                       choose_address(udev);
+                       if (udev->devnum <= 0) {
+                               status = -ENOTCONN;     /* Don't retry */
+                               goto loop;
+                       }
                }
 
-               /* reset and get descriptor */
+               /* reset (non-USB 3.0 devices) and get descriptor */
                status = hub_port_init(hub, udev, port1, i);
                if (status < 0)
                        goto loop;
index 2a116ce..889c0f3 100644 (file)
 #define USB_PORT_FEAT_L1               5       /* L1 suspend */
 #define USB_PORT_FEAT_POWER            8
 #define USB_PORT_FEAT_LOWSPEED         9
+/* This value was never in Table 11-17 */
 #define USB_PORT_FEAT_HIGHSPEED                10
+/* This value is also fake */
+#define USB_PORT_FEAT_SUPERSPEED       11
 #define USB_PORT_FEAT_C_CONNECTION     16
 #define USB_PORT_FEAT_C_ENABLE         17
 #define USB_PORT_FEAT_C_SUSPEND                18
index b626283..2bed83c 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mm.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
+#include <linux/nls.h>
 #include <linux/device.h>
 #include <linux/scatterlist.h>
 #include <linux/usb/quirks.h>
@@ -364,6 +365,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
        int i;
        int urb_flags;
        int dma;
+       int use_sg;
 
        if (!io || !dev || !sg
                        || usb_pipecontrol(pipe)
@@ -391,7 +393,19 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
        if (io->entries <= 0)
                return io->entries;
 
-       io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
+       /* If we're running on an xHCI host controller, queue the whole scatter
+        * gather list with one call to urb_enqueue().  This is only for bulk,
+        * as that endpoint type does not care how the data gets broken up
+        * across frames.
+        */
+       if (usb_pipebulk(pipe) &&
+                       bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) {
+               io->urbs = kmalloc(sizeof *io->urbs, mem_flags);
+               use_sg = true;
+       } else {
+               io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
+               use_sg = false;
+       }
        if (!io->urbs)
                goto nomem;
 
@@ -401,62 +415,92 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
        if (usb_pipein(pipe))
                urb_flags |= URB_SHORT_NOT_OK;
 
-       for_each_sg(sg, sg, io->entries, i) {
-               unsigned len;
-
-               io->urbs[i] = usb_alloc_urb(0, mem_flags);
-               if (!io->urbs[i]) {
-                       io->entries = i;
+       if (use_sg) {
+               io->urbs[0] = usb_alloc_urb(0, mem_flags);
+               if (!io->urbs[0]) {
+                       io->entries = 0;
                        goto nomem;
                }
 
-               io->urbs[i]->dev = NULL;
-               io->urbs[i]->pipe = pipe;
-               io->urbs[i]->interval = period;
-               io->urbs[i]->transfer_flags = urb_flags;
-
-               io->urbs[i]->complete = sg_complete;
-               io->urbs[i]->context = io;
-
-               /*
-                * Some systems need to revert to PIO when DMA is temporarily
-                * unavailable.  For their sakes, both transfer_buffer and
-                * transfer_dma are set when possible.  However this can only
-                * work on systems without:
-                *
-                *  - HIGHMEM, since DMA buffers located in high memory are
-                *    not directly addressable by the CPU for PIO;
-                *
-                *  - IOMMU, since dma_map_sg() is allowed to use an IOMMU to
-                *    make virtually discontiguous buffers be "dma-contiguous"
-                *    so that PIO and DMA need diferent numbers of URBs.
-                *
-                * So when HIGHMEM or IOMMU are in use, transfer_buffer is NULL
-                * to prevent stale pointers and to help spot bugs.
-                */
-               if (dma) {
-                       io->urbs[i]->transfer_dma = sg_dma_address(sg);
-                       len = sg_dma_len(sg);
+               io->urbs[0]->dev = NULL;
+               io->urbs[0]->pipe = pipe;
+               io->urbs[0]->interval = period;
+               io->urbs[0]->transfer_flags = urb_flags;
+
+               io->urbs[0]->complete = sg_complete;
+               io->urbs[0]->context = io;
+               /* A length of zero means transfer the whole sg list */
+               io->urbs[0]->transfer_buffer_length = length;
+               if (length == 0) {
+                       for_each_sg(sg, sg, io->entries, i) {
+                               io->urbs[0]->transfer_buffer_length +=
+                                       sg_dma_len(sg);
+                       }
+               }
+               io->urbs[0]->sg = io;
+               io->urbs[0]->num_sgs = io->entries;
+               io->entries = 1;
+       } else {
+               for_each_sg(sg, sg, io->entries, i) {
+                       unsigned len;
+
+                       io->urbs[i] = usb_alloc_urb(0, mem_flags);
+                       if (!io->urbs[i]) {
+                               io->entries = i;
+                               goto nomem;
+                       }
+
+                       io->urbs[i]->dev = NULL;
+                       io->urbs[i]->pipe = pipe;
+                       io->urbs[i]->interval = period;
+                       io->urbs[i]->transfer_flags = urb_flags;
+
+                       io->urbs[i]->complete = sg_complete;
+                       io->urbs[i]->context = io;
+
+                       /*
+                        * Some systems need to revert to PIO when DMA is
+                        * temporarily unavailable.  For their sakes, both
+                        * transfer_buffer and transfer_dma are set when
+                        * possible.  However this can only work on systems
+                        * without:
+                        *
+                        *  - HIGHMEM, since DMA buffers located in high memory
+                        *    are not directly addressable by the CPU for PIO;
+                        *
+                        *  - IOMMU, since dma_map_sg() is allowed to use an
+                        *    IOMMU to make virtually discontiguous buffers be
+                        *    "dma-contiguous" so that PIO and DMA need diferent
+                        *    numbers of URBs.
+                        *
+                        * So when HIGHMEM or IOMMU are in use, transfer_buffer
+                        * is NULL to prevent stale pointers and to help spot
+                        * bugs.
+                        */
+                       if (dma) {
+                               io->urbs[i]->transfer_dma = sg_dma_address(sg);
+                               len = sg_dma_len(sg);
 #if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU)
-                       io->urbs[i]->transfer_buffer = NULL;
+                               io->urbs[i]->transfer_buffer = NULL;
 #else
-                       io->urbs[i]->transfer_buffer = sg_virt(sg);
+                               io->urbs[i]->transfer_buffer = sg_virt(sg);
 #endif
-               } else {
-                       /* hc may use _only_ transfer_buffer */
-                       io->urbs[i]->transfer_buffer = sg_virt(sg);
-                       len = sg->length;
-               }
+                       } else {
+                               /* hc may use _only_ transfer_buffer */
+                               io->urbs[i]->transfer_buffer = sg_virt(sg);
+                               len = sg->length;
+                       }
 
-               if (length) {
-                       len = min_t(unsigned, len, length);
-                       length -= len;
-                       if (length == 0)
-                               io->entries = i + 1;
+                       if (length) {
+                               len = min_t(unsigned, len, length);
+                               length -= len;
+                               if (length == 0)
+                                       io->entries = i + 1;
+                       }
+                       io->urbs[i]->transfer_buffer_length = len;
                }
-               io->urbs[i]->transfer_buffer_length = len;
+               io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
        }
-       io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
 
        /* transaction state */
        io->count = io->entries;
@@ -509,6 +553,10 @@ EXPORT_SYMBOL_GPL(usb_sg_init);
  * could be transferred.  That capability is less useful for low or full
  * speed interrupt endpoints, which allow at most one packet per millisecond,
  * of at most 8 or 64 bytes (respectively).
+ *
+ * It is not necessary to call this function to reserve bandwidth for devices
+ * under an xHCI host controller, as the bandwidth is reserved when the
+ * configuration or interface alt setting is selected.
  */
 void usb_sg_wait(struct usb_sg_request *io)
 {
@@ -759,7 +807,7 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
 }
 
 /**
- * usb_string - returns ISO 8859-1 version of a string descriptor
+ * usb_string - returns UTF-8 version of a string descriptor
  * @dev: the device whose string descriptor is being retrieved
  * @index: the number of the descriptor
  * @buf: where to put the string
@@ -767,17 +815,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
  * Context: !in_interrupt ()
  *
  * This converts the UTF-16LE encoded strings returned by devices, from
- * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
- * that are more usable in most kernel contexts.  Note that all characters
- * in the chosen descriptor that can't be encoded using ISO-8859-1
- * are converted to the question mark ("?") character, and this function
+ * usb_get_string_descriptor(), to null-terminated UTF-8 encoded ones
+ * that are more usable in most kernel contexts.  Note that this function
  * chooses strings in the first language supported by the device.
  *
- * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit
- * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,
- * and is appropriate for use many uses of English and several other
- * Western European languages.  (But it doesn't include the "Euro" symbol.)
- *
  * This call is synchronous, and may not be used in an interrupt context.
  *
  * Returns length of the string (>= 0) or usb_control_msg status (< 0).
@@ -786,7 +827,6 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
 {
        unsigned char *tbuf;
        int err;
-       unsigned int u, idx;
 
        if (dev->state == USB_STATE_SUSPENDED)
                return -EHOSTUNREACH;
@@ -821,16 +861,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
                goto errout;
 
        size--;         /* leave room for trailing NULL char in output buffer */
-       for (idx = 0, u = 2; u < err; u += 2) {
-               if (idx >= size)
-                       break;
-               if (tbuf[u+1])                  /* high byte */
-                       buf[idx++] = '?';  /* non ISO-8859-1 character */
-               else
-                       buf[idx++] = tbuf[u];
-       }
-       buf[idx] = 0;
-       err = idx;
+       err = utf16s_to_utf8s((wchar_t *) &tbuf[2], (err - 2) / 2,
+                       UTF16_LITTLE_ENDIAN, buf, size);
+       buf[err] = 0;
 
        if (tbuf[1] != USB_DT_STRING)
                dev_dbg(&dev->dev,
@@ -843,6 +876,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
 }
 EXPORT_SYMBOL_GPL(usb_string);
 
+/* one UTF-8-encoded 16-bit character has at most three bytes */
+#define MAX_USB_STRING_SIZE (127 * 3 + 1)
+
 /**
  * usb_cache_string - read a string descriptor and cache it for later use
  * @udev: the device whose string descriptor is being read
@@ -860,9 +896,9 @@ char *usb_cache_string(struct usb_device *udev, int index)
        if (index <= 0)
                return NULL;
 
-       buf = kmalloc(256, GFP_KERNEL);
+       buf = kmalloc(MAX_USB_STRING_SIZE, GFP_KERNEL);
        if (buf) {
-               len = usb_string(udev, index, buf, 256);
+               len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
                if (len > 0) {
                        smallbuf = kmalloc(++len, GFP_KERNEL);
                        if (!smallbuf)
@@ -1664,6 +1700,21 @@ free_interfaces:
        if (ret)
                goto free_interfaces;
 
+       /* Make sure we have bandwidth (and available HCD resources) for this
+        * configuration.  Remove endpoints from the schedule if we're dropping
+        * this configuration to set configuration 0.  After this point, the
+        * host controller will not allow submissions to dropped endpoints.  If
+        * this call fails, the device state is unchanged.
+        */
+       if (cp)
+               ret = usb_hcd_check_bandwidth(dev, cp, NULL);
+       else
+               ret = usb_hcd_check_bandwidth(dev, NULL, NULL);
+       if (ret < 0) {
+               usb_autosuspend_device(dev);
+               goto free_interfaces;
+       }
+
        /* if it's already configured, clear out old state first.
         * getting rid of old interfaces means unbinding their drivers.
         */
@@ -1686,6 +1737,7 @@ free_interfaces:
        dev->actconfig = cp;
        if (!cp) {
                usb_set_device_state(dev, USB_STATE_ADDRESS);
+               usb_hcd_check_bandwidth(dev, NULL, NULL);
                usb_autosuspend_device(dev);
                goto free_interfaces;
        }
index c667891..b5c72e4 100644 (file)
@@ -552,8 +552,8 @@ static struct attribute *dev_string_attrs[] = {
 static mode_t dev_string_attrs_are_visible(struct kobject *kobj,
                struct attribute *a, int n)
 {
-       struct usb_device *udev = to_usb_device(
-                       container_of(kobj, struct device, kobj));
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct usb_device *udev = to_usb_device(dev);
 
        if (a == &dev_attr_manufacturer.attr) {
                if (udev->manufacturer == NULL)
@@ -585,8 +585,8 @@ static ssize_t
 read_descriptors(struct kobject *kobj, struct bin_attribute *attr,
                char *buf, loff_t off, size_t count)
 {
-       struct usb_device *udev = to_usb_device(
-                       container_of(kobj, struct device, kobj));
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct usb_device *udev = to_usb_device(dev);
        size_t nleft = count;
        size_t srclen, n;
        int cfgno;
@@ -786,8 +786,8 @@ static struct attribute *intf_assoc_attrs[] = {
 static mode_t intf_assoc_attrs_are_visible(struct kobject *kobj,
                struct attribute *a, int n)
 {
-       struct usb_interface *intf = to_usb_interface(
-                       container_of(kobj, struct device, kobj));
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct usb_interface *intf = to_usb_interface(dev);
 
        if (intf->intf_assoc == NULL)
                return 0;
index 3376055..0885d4a 100644 (file)
@@ -241,6 +241,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
  * If the USB subsystem can't allocate sufficient bandwidth to perform
  * the periodic request, submitting such a periodic request should fail.
  *
+ * For devices under xHCI, the bandwidth is reserved at configuration time, or
+ * when the alt setting is selected.  If there is not enough bus bandwidth, the
+ * configuration/alt setting request will fail.  Therefore, submissions to
+ * periodic endpoints on devices under xHCI should never fail due to bandwidth
+ * constraints.
+ *
  * Device drivers must explicitly request that repetition, by ensuring that
  * some URB is always on the endpoint's queue (except possibly for short
  * periods during completion callacks).  When there is no longer an urb
@@ -351,6 +357,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
        if (xfertype == USB_ENDPOINT_XFER_ISOC) {
                int     n, len;
 
+               /* FIXME SuperSpeed isoc endpoints have up to 16 bursts */
                /* "high bandwidth" mode, 1-3 packets/uframe? */
                if (dev->speed == USB_SPEED_HIGH) {
                        int     mult = 1 + ((max >> 11) & 0x03);
@@ -426,6 +433,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                        return -EINVAL;
                /* too big? */
                switch (dev->speed) {
+               case USB_SPEED_SUPER:   /* units are 125us */
+                       /* Handle up to 2^(16-1) microframes */
+                       if (urb->interval > (1 << 15))
+                               return -EINVAL;
+                       max = 1 << 15;
                case USB_SPEED_HIGH:    /* units are microframes */
                        /* NOTE usb handles 2^15 */
                        if (urb->interval > (1024 * 8))
index 927a27d..a26f738 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/usb.h>
 #include <linux/mutex.h>
 #include <linux/workqueue.h>
+#include <linux/debugfs.h>
 
 #include <asm/io.h>
 #include <linux/scatterlist.h>
@@ -139,8 +140,7 @@ static int __find_interface(struct device *dev, void *data)
        struct find_interface_arg *arg = data;
        struct usb_interface *intf;
 
-       /* can't look at usb devices, only interfaces */
-       if (is_usb_device(dev))
+       if (!is_usb_interface(dev))
                return 0;
 
        intf = to_usb_interface(dev);
@@ -184,11 +184,16 @@ EXPORT_SYMBOL_GPL(usb_find_interface);
 static void usb_release_dev(struct device *dev)
 {
        struct usb_device *udev;
+       struct usb_hcd *hcd;
 
        udev = to_usb_device(dev);
+       hcd = bus_to_hcd(udev->bus);
 
        usb_destroy_configuration(udev);
-       usb_put_hcd(bus_to_hcd(udev->bus));
+       /* Root hubs aren't real devices, so don't free HCD resources */
+       if (hcd->driver->free_dev && udev->parent)
+               hcd->driver->free_dev(hcd, udev);
+       usb_put_hcd(hcd);
        kfree(udev->product);
        kfree(udev->manufacturer);
        kfree(udev->serial);
@@ -359,6 +364,13 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
                kfree(dev);
                return NULL;
        }
+       /* Root hubs aren't true devices, so don't allocate HCD resources */
+       if (usb_hcd->driver->alloc_dev && parent &&
+               !usb_hcd->driver->alloc_dev(usb_hcd, dev)) {
+               usb_put_hcd(bus_to_hcd(bus));
+               kfree(dev);
+               return NULL;
+       }
 
        device_initialize(&dev->dev);
        dev->dev.bus = &usb_bus_type;
@@ -386,18 +398,24 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
         */
        if (unlikely(!parent)) {
                dev->devpath[0] = '0';
+               dev->route = 0;
 
                dev->dev.parent = bus->controller;
                dev_set_name(&dev->dev, "usb%d", bus->busnum);
                root_hub = 1;
        } else {
                /* match any labeling on the hubs; it's one-based */
-               if (parent->devpath[0] == '0')
+               if (parent->devpath[0] == '0') {
                        snprintf(dev->devpath, sizeof dev->devpath,
                                "%d", port1);
-               else
+                       /* Root ports are not counted in route string */
+                       dev->route = 0;
+               } else {
                        snprintf(dev->devpath, sizeof dev->devpath,
                                "%s.%d", parent->devpath, port1);
+                       dev->route = parent->route +
+                               (port1 << ((parent->level - 1)*4));
+               }
 
                dev->dev.parent = &parent->dev;
                dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
@@ -810,12 +828,12 @@ void usb_buffer_dmasync(struct urb *urb)
                return;
 
        if (controller->dma_mask) {
-               dma_sync_single(controller,
+               dma_sync_single_for_cpu(controller,
                        urb->transfer_dma, urb->transfer_buffer_length,
                        usb_pipein(urb->pipe)
                                ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
                if (usb_pipecontrol(urb->pipe))
-                       dma_sync_single(controller,
+                       dma_sync_single_for_cpu(controller,
                                        urb->setup_dma,
                                        sizeof(struct usb_ctrlrequest),
                                        DMA_TO_DEVICE);
@@ -933,8 +951,8 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
                        || !controller->dma_mask)
                return;
 
-       dma_sync_sg(controller, sg, n_hw_ents,
-                       is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       dma_sync_sg_for_cpu(controller, sg, n_hw_ents,
+                           is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg);
 #endif
@@ -1012,6 +1030,35 @@ static struct notifier_block usb_bus_nb = {
        .notifier_call = usb_bus_notify,
 };
 
+struct dentry *usb_debug_root;
+EXPORT_SYMBOL_GPL(usb_debug_root);
+
+struct dentry *usb_debug_devices;
+
+static int usb_debugfs_init(void)
+{
+       usb_debug_root = debugfs_create_dir("usb", NULL);
+       if (!usb_debug_root)
+               return -ENOENT;
+
+       usb_debug_devices = debugfs_create_file("devices", 0444,
+                                               usb_debug_root, NULL,
+                                               &usbfs_devices_fops);
+       if (!usb_debug_devices) {
+               debugfs_remove(usb_debug_root);
+               usb_debug_root = NULL;
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+static void usb_debugfs_cleanup(void)
+{
+       debugfs_remove(usb_debug_devices);
+       debugfs_remove(usb_debug_root);
+}
+
 /*
  * Init
  */
@@ -1023,6 +1070,10 @@ static int __init usb_init(void)
                return 0;
        }
 
+       retval = usb_debugfs_init();
+       if (retval)
+               goto out;
+
        retval = ksuspend_usb_init();
        if (retval)
                goto out;
@@ -1032,9 +1083,6 @@ static int __init usb_init(void)
        retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
        if (retval)
                goto bus_notifier_failed;
-       retval = usb_host_init();
-       if (retval)
-               goto host_init_failed;
        retval = usb_major_init();
        if (retval)
                goto major_init_failed;
@@ -1064,8 +1112,6 @@ usb_devio_init_failed:
 driver_register_failed:
        usb_major_cleanup();
 major_init_failed:
-       usb_host_cleanup();
-host_init_failed:
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
 bus_notifier_failed:
        bus_unregister(&usb_bus_type);
@@ -1090,10 +1136,10 @@ static void __exit usb_exit(void)
        usb_deregister(&usbfs_driver);
        usb_devio_cleanup();
        usb_hub_cleanup();
-       usb_host_cleanup();
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
        bus_unregister(&usb_bus_type);
        ksuspend_usb_cleanup();
+       usb_debugfs_cleanup();
 }
 
 subsys_initcall(usb_init);
index 79d8a9e..e2a8cfa 100644 (file)
@@ -41,8 +41,6 @@ extern int  usb_hub_init(void);
 extern void usb_hub_cleanup(void);
 extern int usb_major_init(void);
 extern void usb_major_cleanup(void);
-extern int usb_host_init(void);
-extern void usb_host_cleanup(void);
 
 #ifdef CONFIG_PM
 
@@ -106,6 +104,7 @@ extern struct workqueue_struct *ksuspend_usb_wq;
 extern struct bus_type usb_bus_type;
 extern struct device_type usb_device_type;
 extern struct device_type usb_if_device_type;
+extern struct device_type usb_ep_device_type;
 extern struct usb_device_driver usb_generic_driver;
 
 static inline int is_usb_device(const struct device *dev)
@@ -113,6 +112,16 @@ static inline int is_usb_device(const struct device *dev)
        return dev->type == &usb_device_type;
 }
 
+static inline int is_usb_interface(const struct device *dev)
+{
+       return dev->type == &usb_if_device_type;
+}
+
+static inline int is_usb_endpoint(const struct device *dev)
+{
+       return dev->type == &usb_ep_device_type;
+}
+
 /* Do the same for device drivers and interface drivers. */
 
 static inline int is_usb_device_driver(struct device_driver *drv)
index 080bb1e..5d1ddf4 100644 (file)
@@ -156,7 +156,7 @@ config USB_ATMEL_USBA
 
 config USB_GADGET_FSL_USB2
        boolean "Freescale Highspeed USB DR Peripheral Controller"
-       depends on FSL_SOC
+       depends on FSL_SOC || ARCH_MXC
        select USB_GADGET_DUALSPEED
        help
           Some of Freescale PowerPC processors have a High Speed
@@ -253,7 +253,7 @@ config USB_PXA25X_SMALL
 
 config USB_GADGET_PXA27X
        boolean "PXA 27x"
-       depends on ARCH_PXA && PXA27x
+       depends on ARCH_PXA && (PXA27x || PXA3xx)
        select USB_OTG_UTILS
        help
           Intel's PXA 27x series XScale ARM v5TE processors include
@@ -272,6 +272,20 @@ config USB_PXA27X
        default USB_GADGET
        select USB_GADGET_SELECTED
 
+config USB_GADGET_S3C_HSOTG
+       boolean "S3C HS/OtG USB Device controller"
+       depends on S3C_DEV_USB_HSOTG
+       select USB_GADGET_S3C_HSOTG_PIO
+       help
+         The Samsung S3C64XX USB2.0 high-speed gadget controller
+         integrated into the S3C64XX series SoC.
+
+config USB_S3C_HSOTG
+       tristate
+       depends on USB_GADGET_S3C_HSOTG
+       default USB_GADGET
+       select USB_GADGET_SELECTED
+
 config USB_GADGET_S3C2410
        boolean "S3C2410 USB Device Controller"
        depends on ARCH_S3C2410
@@ -460,6 +474,27 @@ config USB_GOKU
        default USB_GADGET
        select USB_GADGET_SELECTED
 
+config USB_GADGET_LANGWELL
+       boolean "Intel Langwell USB Device Controller"
+       depends on PCI
+       select USB_GADGET_DUALSPEED
+       help
+          Intel Langwell USB Device Controller is a High-Speed USB
+          On-The-Go device controller.
+
+          The number of programmable endpoints is different through
+          controller revision.
+
+          Say "y" to link the driver statically, or "m" to build a
+          dynamically linked module called "langwell_udc" and force all
+          gadget drivers to also be dynamically linked.
+
+config USB_LANGWELL
+       tristate
+       depends on USB_GADGET_LANGWELL
+       default USB_GADGET
+       select USB_GADGET_SELECTED
+
 
 #
 # LAST -- dummy/emulated controller
@@ -566,6 +601,20 @@ config USB_ZERO_HNPTEST
          the "B-Peripheral" role, that device will use HNP to let this
          one serve as the USB host instead (in the "B-Host" role).
 
+config USB_AUDIO
+       tristate "Audio Gadget (EXPERIMENTAL)"
+       depends on SND
+       help
+         Gadget Audio is compatible with USB Audio Class specification 1.0.
+         It will include at least one AudioControl interface, zero or more
+         AudioStream interface and zero or more MIDIStream interface.
+
+         Gadget Audio will use on-board ALSA (CONFIG_SND) audio card to
+         playback or capture audio stream.
+
+         Say "y" to link the driver statically, or "m" to build a
+         dynamically linked module called "g_audio".
+
 config USB_ETH
        tristate "Ethernet Gadget (with CDC Ethernet support)"
        depends on NET
index 39a51d7..e6017e6 100644 (file)
@@ -18,14 +18,21 @@ obj-$(CONFIG_USB_S3C2410)   += s3c2410_udc.o
 obj-$(CONFIG_USB_AT91)         += at91_udc.o
 obj-$(CONFIG_USB_ATMEL_USBA)   += atmel_usba_udc.o
 obj-$(CONFIG_USB_FSL_USB2)     += fsl_usb2_udc.o
+fsl_usb2_udc-objs              := fsl_udc_core.o
+ifeq ($(CONFIG_ARCH_MXC),y)
+fsl_usb2_udc-objs              += fsl_mx3_udc.o
+endif
 obj-$(CONFIG_USB_M66592)       += m66592-udc.o
 obj-$(CONFIG_USB_FSL_QE)       += fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX)      += ci13xxx_udc.o
+obj-$(CONFIG_USB_S3C_HSOTG)    += s3c-hsotg.o
+obj-$(CONFIG_USB_LANGWELL)     += langwell_udc.o
 
 #
 # USB gadget drivers
 #
 g_zero-objs                    := zero.o
+g_audio-objs                   := audio.o
 g_ether-objs                   := ether.o
 g_serial-objs                  := serial.o
 g_midi-objs                    := gmidi.o
@@ -35,6 +42,7 @@ g_printer-objs                        := printer.o
 g_cdc-objs                     := cdc2.o
 
 obj-$(CONFIG_USB_ZERO)         += g_zero.o
+obj-$(CONFIG_USB_AUDIO)                += g_audio.o
 obj-$(CONFIG_USB_ETH)          += g_ether.o
 obj-$(CONFIG_USB_GADGETFS)     += gadgetfs.o
 obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
index 53bcdd2..72bae8f 100644 (file)
@@ -485,7 +485,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
                return -ESHUTDOWN;
        }
 
-       tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+       tmp = usb_endpoint_type(desc);
        switch (tmp) {
        case USB_ENDPOINT_XFER_CONTROL:
                DBG("only one control endpoint\n");
@@ -517,7 +517,7 @@ ok:
        local_irq_save(flags);
 
        /* initialize endpoint to match this descriptor */
-       ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
+       ep->is_in = usb_endpoint_dir_in(desc);
        ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
        ep->stopped = 0;
        if (ep->is_in)
index 05c913c..4e970cf 100644 (file)
@@ -326,13 +326,7 @@ static int vbus_is_present(struct usba_udc *udc)
        return 1;
 }
 
-#if defined(CONFIG_AVR32)
-
-static void toggle_bias(int is_on)
-{
-}
-
-#elif defined(CONFIG_ARCH_AT91)
+#if defined(CONFIG_ARCH_AT91SAM9RL)
 
 #include <mach/at91_pmc.h>
 
@@ -346,7 +340,13 @@ static void toggle_bias(int is_on)
                at91_sys_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
 }
 
-#endif /* CONFIG_ARCH_AT91 */
+#else
+
+static void toggle_bias(int is_on)
+{
+}
+
+#endif /* CONFIG_ARCH_AT91SAM9RL */
 
 static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
 {
@@ -550,12 +550,12 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
                        ep->ep.name, ept_cfg, maxpacket);
 
-       if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+       if (usb_endpoint_dir_in(desc)) {
                ep->is_in = 1;
                ept_cfg |= USBA_EPT_DIR_IN;
        }
 
-       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       switch (usb_endpoint_type(desc)) {
        case USB_ENDPOINT_XFER_CONTROL:
                ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
                ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
new file mode 100644 (file)
index 0000000..94de7e8
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * audio.c -- Audio gadget driver
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+
+#include "u_audio.h"
+
+#define DRIVER_DESC            "Linux USB Audio Gadget"
+#define DRIVER_VERSION         "Dec 18, 2008"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module.  So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+#include "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+#include "u_audio.c"
+#include "f_audio.c"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID. */
+#define AUDIO_VENDOR_NUM               0x0525  /* NetChip */
+#define AUDIO_PRODUCT_NUM              0xa4a1  /* Linux-USB Audio Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+       .bLength =              sizeof device_desc,
+       .bDescriptorType =      USB_DT_DEVICE,
+
+       .bcdUSB =               __constant_cpu_to_le16(0x200),
+
+       .bDeviceClass =         USB_CLASS_PER_INTERFACE,
+       .bDeviceSubClass =      0,
+       .bDeviceProtocol =      0,
+       /* .bMaxPacketSize0 = f(hardware) */
+
+       /* Vendor and product id defaults change according to what configs
+        * we support.  (As does bNumConfigurations.)  These values can
+        * also be overridden by module parameters.
+        */
+       .idVendor =             __constant_cpu_to_le16(AUDIO_VENDOR_NUM),
+       .idProduct =            __constant_cpu_to_le16(AUDIO_PRODUCT_NUM),
+       /* .bcdDevice = f(hardware) */
+       /* .iManufacturer = DYNAMIC */
+       /* .iProduct = DYNAMIC */
+       /* NO SERIAL NUMBER */
+       .bNumConfigurations =   1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+       .bLength =              sizeof otg_descriptor,
+       .bDescriptorType =      USB_DT_OTG,
+
+       /* REVISIT SRP-only hardware is possible, although
+        * it would not be called "OTG" ...
+        */
+       .bmAttributes =         USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+       (struct usb_descriptor_header *) &otg_descriptor,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Handle USB audio endpoint set/get command in setup class request
+ */
+
+static int audio_set_endpoint_req(struct usb_configuration *c,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       int                     value = -EOPNOTSUPP;
+       u16                     ep = le16_to_cpu(ctrl->wIndex);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+                       ctrl->bRequest, w_value, len, ep);
+
+       switch (ctrl->bRequest) {
+       case SET_CUR:
+               value = 0;
+               break;
+
+       case SET_MIN:
+               break;
+
+       case SET_MAX:
+               break;
+
+       case SET_RES:
+               break;
+
+       case SET_MEM:
+               break;
+
+       default:
+               break;
+       }
+
+       return value;
+}
+
+static int audio_get_endpoint_req(struct usb_configuration *c,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       int value = -EOPNOTSUPP;
+       u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16 len = le16_to_cpu(ctrl->wLength);
+       u16 w_value = le16_to_cpu(ctrl->wValue);
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+                       ctrl->bRequest, w_value, len, ep);
+
+       switch (ctrl->bRequest) {
+       case GET_CUR:
+       case GET_MIN:
+       case GET_MAX:
+       case GET_RES:
+               value = 3;
+               break;
+       case GET_MEM:
+               break;
+       default:
+               break;
+       }
+
+       return value;
+}
+
+static int
+audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct usb_request *req = cdev->req;
+       int value = -EOPNOTSUPP;
+       u16 w_index = le16_to_cpu(ctrl->wIndex);
+       u16 w_value = le16_to_cpu(ctrl->wValue);
+       u16 w_length = le16_to_cpu(ctrl->wLength);
+
+       /* composite driver infrastructure handles everything except
+        * Audio class messages; interface activation uses set_alt().
+        */
+       switch (ctrl->bRequestType) {
+       case USB_AUDIO_SET_ENDPOINT:
+               value = audio_set_endpoint_req(c, ctrl);
+               break;
+
+       case USB_AUDIO_GET_ENDPOINT:
+               value = audio_get_endpoint_req(c, ctrl);
+               break;
+
+       default:
+               ERROR(cdev, "Invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "Audio req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "Audio response on err %d\n", value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __init audio_do_config(struct usb_configuration *c)
+{
+       /* FIXME alloc iConfiguration string, set it in c->strings */
+
+       if (gadget_is_otg(c->cdev->gadget)) {
+               c->descriptors = otg_desc;
+               c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+       }
+
+       audio_bind_config(c);
+
+       return 0;
+}
+
+static struct usb_configuration audio_config_driver = {
+       .label                  = DRIVER_DESC,
+       .bind                   = audio_do_config,
+       .setup                  = audio_setup,
+       .bConfigurationValue    = 1,
+       /* .iConfiguration = DYNAMIC */
+       .bmAttributes           = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init audio_bind(struct usb_composite_dev *cdev)
+{
+       int                     gcnum;
+       int                     status;
+
+       gcnum = usb_gadget_controller_number(cdev->gadget);
+       if (gcnum >= 0)
+               device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
+       else {
+               ERROR(cdev, "controller '%s' not recognized; trying %s\n",
+                       cdev->gadget->name,
+                       audio_config_driver.label);
+               device_desc.bcdDevice =
+                       __constant_cpu_to_le16(0x0300 | 0x0099);
+       }
+
+       /* device descriptor strings: manufacturer, product */
+       snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+               init_utsname()->sysname, init_utsname()->release,
+               cdev->gadget->name);
+       status = usb_string_id(cdev);
+       if (status < 0)
+               goto fail;
+       strings_dev[STRING_MANUFACTURER_IDX].id = status;
+       device_desc.iManufacturer = status;
+
+       status = usb_string_id(cdev);
+       if (status < 0)
+               goto fail;
+       strings_dev[STRING_PRODUCT_IDX].id = status;
+       device_desc.iProduct = status;
+
+       status = usb_add_config(cdev, &audio_config_driver);
+       if (status < 0)
+               goto fail;
+
+       INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
+       return 0;
+
+fail:
+       return status;
+}
+
+static int __exit audio_unbind(struct usb_composite_dev *cdev)
+{
+       return 0;
+}
+
+static struct usb_composite_driver audio_driver = {
+       .name           = "g_audio",
+       .dev            = &device_desc,
+       .strings        = audio_strings,
+       .bind           = audio_bind,
+       .unbind         = __exit_p(audio_unbind),
+};
+
+static int __init init(void)
+{
+       return usb_composite_register(&audio_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+       usb_composite_unregister(&audio_driver);
+}
+module_exit(cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
+MODULE_LICENSE("GPL");
+
index 38e531e..c7cb87a 100644 (file)
@@ -1977,9 +1977,9 @@ static int ep_enable(struct usb_ep *ep,
        if (!list_empty(&mEp->qh[mEp->dir].queue))
                warn("enabling a non-empty endpoint!");
 
-       mEp->dir  = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? TX : RX;
-       mEp->num  =  desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-       mEp->type =  desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+       mEp->dir  = usb_endpoint_dir_in(desc) ? TX : RX;
+       mEp->num  = usb_endpoint_num(desc);
+       mEp->type = usb_endpoint_type(desc);
 
        mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize);
 
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
new file mode 100644 (file)
index 0000000..66527ba
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * f_audio.c -- USB Audio class function driver
+  *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+
+#include "u_audio.h"
+
+#define OUT_EP_MAX_PACKET_SIZE 200
+static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
+module_param(req_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
+
+static int req_count = 256;
+module_param(req_count, int, S_IRUGO);
+MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
+
+static int audio_buf_size = 48000;
+module_param(audio_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
+
+/*
+ * DESCRIPTORS ... most are static, but strings and full
+ * configuration descriptors are built on demand.
+ */
+
+/*
+ * We have two interfaces- AudioControl and AudioStreaming
+ * TODO: only supcard playback currently
+ */
+#define F_AUDIO_AC_INTERFACE   0
+#define F_AUDIO_AS_INTERFACE   1
+#define F_AUDIO_NUM_INTERFACES 2
+
+/* B.3.1  Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc __initdata = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
+};
+
+DECLARE_USB_AC_HEADER_DESCRIPTOR(2);
+
+#define USB_DT_AC_HEADER_LENGH USB_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static struct usb_ac_header_descriptor_2 ac_header_desc = {
+       .bLength =              USB_DT_AC_HEADER_LENGH,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   HEADER,
+       .bcdADC =               __constant_cpu_to_le16(0x0100),
+       .wTotalLength =         __constant_cpu_to_le16(USB_DT_AC_HEADER_LENGH),
+       .bInCollection =        F_AUDIO_NUM_INTERFACES,
+       .baInterfaceNr = {
+               [0] =           F_AUDIO_AC_INTERFACE,
+               [1] =           F_AUDIO_AS_INTERFACE,
+       }
+};
+
+#define INPUT_TERMINAL_ID      1
+static struct usb_input_terminal_descriptor input_terminal_desc = {
+       .bLength =              USB_DT_AC_INPUT_TERMINAL_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   INPUT_TERMINAL,
+       .bTerminalID =          INPUT_TERMINAL_ID,
+       .wTerminalType =        USB_AC_TERMINAL_STREAMING,
+       .bAssocTerminal =       0,
+       .wChannelConfig =       0x3,
+};
+
+DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID                2
+static struct usb_ac_feature_unit_descriptor_0 feature_unit_desc = {
+       .bLength                = USB_DT_AC_FEATURE_UNIT_SIZE(0),
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype     = FEATURE_UNIT,
+       .bUnitID                = FEATURE_UNIT_ID,
+       .bSourceID              = INPUT_TERMINAL_ID,
+       .bControlSize           = 2,
+       .bmaControls[0]         = (FU_MUTE | FU_VOLUME),
+};
+
+static struct usb_audio_control mute_control = {
+       .list = LIST_HEAD_INIT(mute_control.list),
+       .name = "Mute Control",
+       .type = MUTE_CONTROL,
+       /* Todo: add real Mute control code */
+       .set = generic_set_cmd,
+       .get = generic_get_cmd,
+};
+
+static struct usb_audio_control volume_control = {
+       .list = LIST_HEAD_INIT(volume_control.list),
+       .name = "Volume Control",
+       .type = VOLUME_CONTROL,
+       /* Todo: add real Volume control code */
+       .set = generic_set_cmd,
+       .get = generic_get_cmd,
+};
+
+static struct usb_audio_control_selector feature_unit = {
+       .list = LIST_HEAD_INIT(feature_unit.list),
+       .id = FEATURE_UNIT_ID,
+       .name = "Mute & Volume Control",
+       .type = FEATURE_UNIT,
+       .desc = (struct usb_descriptor_header *)&feature_unit_desc,
+};
+
+#define OUTPUT_TERMINAL_ID     3
+static struct usb_output_terminal_descriptor output_terminal_desc = {
+       .bLength                = USB_DT_AC_OUTPUT_TERMINAL_SIZE,
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype     = OUTPUT_TERMINAL,
+       .bTerminalID            = OUTPUT_TERMINAL_ID,
+       .wTerminalType          = USB_AC_OUTPUT_TERMINAL_SPEAKER,
+       .bAssocTerminal         = FEATURE_UNIT_ID,
+       .bSourceID              = FEATURE_UNIT_ID,
+};
+
+/* B.4.1  Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        1,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2  Class-Specific AS Interface Descriptor */
+static struct usb_as_header_descriptor as_header_desc = {
+       .bLength =              USB_DT_AS_HEADER_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   AS_GENERAL,
+       .bTerminalLink =        INPUT_TERMINAL_ID,
+       .bDelay =               1,
+       .wFormatTag =           USB_AS_AUDIO_FORMAT_TYPE_I_PCM,
+};
+
+DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(1);
+
+static struct usb_as_formate_type_i_discrete_descriptor_1 as_type_i_desc = {
+       .bLength =              USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   FORMAT_TYPE,
+       .bFormatType =          USB_AS_FORMAT_TYPE_I,
+       .bSubframeSize =        2,
+       .bBitResolution =       16,
+       .bSamFreqType =         1,
+};
+
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor as_out_ep_desc __initdata = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_AS_ENDPOINT_ADAPTIVE
+                               | USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
+       .bInterval =            4,
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct usb_as_iso_endpoint_descriptor as_iso_out_desc __initdata = {
+       .bLength =              USB_AS_ISO_ENDPOINT_DESC_SIZE,
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   EP_GENERAL,
+       .bmAttributes =         1,
+       .bLockDelayUnits =      1,
+       .wLockDelay =           __constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *f_audio_desc[] __initdata = {
+       (struct usb_descriptor_header *)&ac_interface_desc,
+       (struct usb_descriptor_header *)&ac_header_desc,
+
+       (struct usb_descriptor_header *)&input_terminal_desc,
+       (struct usb_descriptor_header *)&output_terminal_desc,
+       (struct usb_descriptor_header *)&feature_unit_desc,
+
+       (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+       (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+       (struct usb_descriptor_header *)&as_header_desc,
+
+       (struct usb_descriptor_header *)&as_type_i_desc,
+
+       (struct usb_descriptor_header *)&as_out_ep_desc,
+       (struct usb_descriptor_header *)&as_iso_out_desc,
+       NULL,
+};
+
+/* string IDs are assigned dynamically */
+
+#define STRING_MANUFACTURER_IDX                0
+#define STRING_PRODUCT_IDX             1
+
+static char manufacturer[50];
+
+static struct usb_string strings_dev[] = {
+       [STRING_MANUFACTURER_IDX].s = manufacturer,
+       [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+       .language       = 0x0409,       /* en-us */
+       .strings        = strings_dev,
+};
+
+static struct usb_gadget_strings *audio_strings[] = {
+       &stringtab_dev,
+       NULL,
+};
+
+/*
+ * This function is an ALSA sound card following USB Audio Class Spec 1.0.
+ */
+
+/*-------------------------------------------------------------------------*/
+struct f_audio_buf {
+       u8 *buf;
+       int actual;
+       struct list_head list;
+};
+
+static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
+{
+       struct f_audio_buf *copy_buf;
+
+       copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
+       if (!copy_buf)
+               return (struct f_audio_buf *)-ENOMEM;
+
+       copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
+       if (!copy_buf->buf) {
+               kfree(copy_buf);
+               return (struct f_audio_buf *)-ENOMEM;
+       }
+
+       return copy_buf;
+}
+
+static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
+{
+       kfree(audio_buf->buf);
+       kfree(audio_buf);
+}
+/*-------------------------------------------------------------------------*/
+
+struct f_audio {
+       struct gaudio                   card;
+
+       /* endpoints handle full and/or high speeds */
+       struct usb_ep                   *out_ep;
+       struct usb_endpoint_descriptor  *out_desc;
+
+       spinlock_t                      lock;
+       struct f_audio_buf *copy_buf;
+       struct work_struct playback_work;
+       struct list_head play_queue;
+
+       /* Control Set command */
+       struct list_head cs;
+       u8 set_cmd;
+       struct usb_audio_control *set_con;
+};
+
+static inline struct f_audio *func_to_audio(struct usb_function *f)
+{
+       return container_of(f, struct f_audio, card.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void f_audio_playback_work(struct work_struct *data)
+{
+       struct f_audio *audio = container_of(data, struct f_audio,
+                                       playback_work);
+       struct f_audio_buf *play_buf;
+
+       spin_lock_irq(&audio->lock);
+       if (list_empty(&audio->play_queue)) {
+               spin_unlock_irq(&audio->lock);
+               return;
+       }
+       play_buf = list_first_entry(&audio->play_queue,
+                       struct f_audio_buf, list);
+       list_del(&play_buf->list);
+       spin_unlock_irq(&audio->lock);
+
+       u_audio_playback(&audio->card, play_buf->buf, play_buf->actual);
+       f_audio_buffer_free(play_buf);
+
+       return;
+}
+
+static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_audio *audio = req->context;
+       struct usb_composite_dev *cdev = audio->card.func.config->cdev;
+       struct f_audio_buf *copy_buf = audio->copy_buf;
+       int err;
+
+       if (!copy_buf)
+               return -EINVAL;
+
+       /* Copy buffer is full, add it to the play_queue */
+       if (audio_buf_size - copy_buf->actual < req->actual) {
+               list_add_tail(&copy_buf->list, &audio->play_queue);
+               schedule_work(&audio->playback_work);
+               copy_buf = f_audio_buffer_alloc(audio_buf_size);
+               if (copy_buf < 0)
+                       return -ENOMEM;
+       }
+
+       memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual);
+       copy_buf->actual += req->actual;
+       audio->copy_buf = copy_buf;
+
+       err = usb_ep_queue(ep, req, GFP_ATOMIC);
+       if (err)
+               ERROR(cdev, "%s queue req: %d\n", ep->name, err);
+
+       return 0;
+
+}
+
+static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_audio *audio = req->context;
+       int status = req->status;
+       u32 data = 0;
+       struct usb_ep *out_ep = audio->out_ep;
+
+       switch (status) {
+
+       case 0:                         /* normal completion? */
+               if (ep == out_ep)
+                       f_audio_out_ep_complete(ep, req);
+               else if (audio->set_con) {
+                       memcpy(&data, req->buf, req->length);
+                       audio->set_con->set(audio->set_con, audio->set_cmd,
+                                       le16_to_cpu(data));
+                       audio->set_con = NULL;
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static int audio_set_intf_req(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       u8                      id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u8                      con_sel = (w_value >> 8) & 0xFF;
+       u8                      cmd = (ctrl->bRequest & 0x0F);
+       struct usb_audio_control_selector *cs;
+       struct usb_audio_control *con;
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
+                       ctrl->bRequest, w_value, len, id);
+
+       list_for_each_entry(cs, &audio->cs, list) {
+               if (cs->id == id) {
+                       list_for_each_entry(con, &cs->control, list) {
+                               if (con->type == con_sel) {
+                                       audio->set_con = con;
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       audio->set_cmd = cmd;
+       req->context = audio;
+       req->complete = f_audio_complete;
+
+       return len;
+}
+
+static int audio_get_intf_req(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u8                      id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u8                      con_sel = (w_value >> 8) & 0xFF;
+       u8                      cmd = (ctrl->bRequest & 0x0F);
+       struct usb_audio_control_selector *cs;
+       struct usb_audio_control *con;
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
+                       ctrl->bRequest, w_value, len, id);
+
+       list_for_each_entry(cs, &audio->cs, list) {
+               if (cs->id == id) {
+                       list_for_each_entry(con, &cs->control, list) {
+                               if (con->type == con_sel && con->get) {
+                                       value = con->get(con, cmd);
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       req->context = audio;
+       req->complete = f_audio_complete;
+       memcpy(req->buf, &value, len);
+
+       return len;
+}
+
+static int
+f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       /* composite driver infrastructure handles everything except
+        * Audio class messages; interface activation uses set_alt().
+        */
+       switch (ctrl->bRequestType) {
+       case USB_AUDIO_SET_INTF:
+               value = audio_set_intf_req(f, ctrl);
+               break;
+
+       case USB_AUDIO_GET_INTF:
+               value = audio_get_intf_req(f, ctrl);
+               break;
+
+       default:
+               ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "audio response on err %d\n", value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_ep *out_ep = audio->out_ep;
+       struct usb_request *req;
+       int i = 0, err = 0;
+
+       DBG(cdev, "intf %d, alt %d\n", intf, alt);
+
+       if (intf == 1) {
+               if (alt == 1) {
+                       usb_ep_enable(out_ep, audio->out_desc);
+                       out_ep->driver_data = audio;
+                       audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
+
+                       /*
+                        * allocate a bunch of read buffers
+                        * and queue them all at once.
+                        */
+                       for (i = 0; i < req_count && err == 0; i++) {
+                               req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
+                               if (req) {
+                                       req->buf = kzalloc(req_buf_size,
+                                                       GFP_ATOMIC);
+                                       if (req->buf) {
+                                               req->length = req_buf_size;
+                                               req->context = audio;
+                                               req->complete =
+                                                       f_audio_complete;
+                                               err = usb_ep_queue(out_ep,
+                                                       req, GFP_ATOMIC);
+                                               if (err)
+                                                       ERROR(cdev,
+                                                       "%s queue req: %d\n",
+                                                       out_ep->name, err);
+                                       } else
+                                               err = -ENOMEM;
+                               } else
+                                       err = -ENOMEM;
+                       }
+
+               } else {
+                       struct f_audio_buf *copy_buf = audio->copy_buf;
+                       if (copy_buf) {
+                               list_add_tail(&copy_buf->list,
+                                               &audio->play_queue);
+                               schedule_work(&audio->playback_work);
+                       }
+               }
+       }
+
+       return err;
+}
+
+static void f_audio_disable(struct usb_function *f)
+{
+       return;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void f_audio_build_desc(struct f_audio *audio)
+{
+       struct gaudio *card = &audio->card;
+       u8 *sam_freq;
+       int rate;
+
+       /* Set channel numbers */
+       input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
+       as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
+
+       /* Set sample rates */
+       rate = u_audio_get_playback_rate(card);
+       sam_freq = as_type_i_desc.tSamFreq[0];
+       memcpy(sam_freq, &rate, 3);
+
+       /* Todo: Set Sample bits and other parameters */
+
+       return;
+}
+
+/* audio function driver setup/binding */
+static int __init
+f_audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_audio          *audio = func_to_audio(f);
+       int                     status;
+       struct usb_ep           *ep;
+
+       f_audio_build_desc(audio);
+
+       /* allocate instance-specific interface IDs, and patch descriptors */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ac_interface_desc.bInterfaceNumber = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       as_interface_alt_0_desc.bInterfaceNumber = status;
+       as_interface_alt_1_desc.bInterfaceNumber = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
+       if (!ep)
+               goto fail;
+       audio->out_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       status = -ENOMEM;
+
+       /* supcard all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+
+       /* copy descriptors, and track endpoint copies */
+       if (gadget_is_dualspeed(c->cdev->gadget)) {
+               c->highspeed = true;
+               f->hs_descriptors = usb_copy_descriptors(f_audio_desc);
+       } else
+               f->descriptors = usb_copy_descriptors(f_audio_desc);
+
+       return 0;
+
+fail:
+
+       return status;
+}
+
+static void
+f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_audio          *audio = func_to_audio(f);
+
+       usb_free_descriptors(f->descriptors);
+       kfree(audio);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Todo: add more control selecotor dynamically */
+int __init control_selector_init(struct f_audio *audio)
+{
+       INIT_LIST_HEAD(&audio->cs);
+       list_add(&feature_unit.list, &audio->cs);
+
+       INIT_LIST_HEAD(&feature_unit.control);
+       list_add(&mute_control.list, &feature_unit.control);
+       list_add(&volume_control.list, &feature_unit.control);
+
+       volume_control.data[_CUR] = 0xffc0;
+       volume_control.data[_MIN] = 0xe3a0;
+       volume_control.data[_MAX] = 0xfff0;
+       volume_control.data[_RES] = 0x0030;
+
+       return 0;
+}
+
+/**
+ * audio_bind_config - add USB audio fucntion to a configuration
+ * @c: the configuration to supcard the USB audio function
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ */
+int __init audio_bind_config(struct usb_configuration *c)
+{
+       struct f_audio *audio;
+       int status;
+
+       /* allocate and initialize one new instance */
+       audio = kzalloc(sizeof *audio, GFP_KERNEL);
+       if (!audio)
+               return -ENOMEM;
+
+       audio->card.func.name = "g_audio";
+       audio->card.gadget = c->cdev->gadget;
+
+       INIT_LIST_HEAD(&audio->play_queue);
+       spin_lock_init(&audio->lock);
+
+       /* set up ASLA audio devices */
+       status = gaudio_setup(&audio->card);
+       if (status < 0)
+               goto setup_fail;
+
+       audio->card.func.strings = audio_strings;
+       audio->card.func.bind = f_audio_bind;
+       audio->card.func.unbind = f_audio_unbind;
+       audio->card.func.set_alt = f_audio_set_alt;
+       audio->card.func.setup = f_audio_setup;
+       audio->card.func.disable = f_audio_disable;
+       audio->out_desc = &as_out_ep_desc;
+
+       control_selector_init(audio);
+
+       INIT_WORK(&audio->playback_work, f_audio_playback_work);
+
+       status = usb_add_function(c, &audio->card.func);
+       if (status)
+               goto add_fail;
+
+       INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n",
+               audio_buf_size, req_buf_size, req_count);
+
+       return status;
+
+add_fail:
+       gaudio_cleanup(&audio->card);
+setup_fail:
+       kfree(audio);
+       return status;
+}
index 3279a47..424a37c 100644 (file)
@@ -475,7 +475,9 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
                if (rndis->port.in_ep->driver_data) {
                        DBG(cdev, "reset rndis\n");
                        gether_disconnect(&rndis->port);
-               } else {
+               }
+
+               if (!rndis->port.in) {
                        DBG(cdev, "init rndis\n");
                        rndis->port.in = ep_choose(cdev->gadget,
                                        rndis->hs.in, rndis->fs.in);
index 381a53b..1e6aa50 100644 (file)
 #include <linux/freezer.h>
 #include <linux/utsname.h>
 
+#include <asm/unaligned.h>
+
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
@@ -799,29 +801,9 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
 
 /* Routines for unaligned data access */
 
-static u16 get_be16(u8 *buf)
-{
-       return ((u16) buf[0] << 8) | ((u16) buf[1]);
-}
-
-static u32 get_be32(u8 *buf)
-{
-       return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) |
-                       ((u32) buf[2] << 8) | ((u32) buf[3]);
-}
-
-static void put_be16(u8 *buf, u16 val)
-{
-       buf[0] = val >> 8;
-       buf[1] = val;
-}
-
-static void put_be32(u8 *buf, u32 val)
+static u32 get_unaligned_be24(u8 *buf)
 {
-       buf[0] = val >> 24;
-       buf[1] = val >> 16;
-       buf[2] = val >> 8;
-       buf[3] = val & 0xff;
+       return 0xffffff & (u32) get_unaligned_be32(buf - 1);
 }
 
 
@@ -1582,9 +1564,9 @@ static int do_read(struct fsg_dev *fsg)
        /* Get the starting Logical Block Address and check that it's
         * not too big */
        if (fsg->cmnd[0] == SC_READ_6)
-               lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]);
+               lba = get_unaligned_be24(&fsg->cmnd[1]);
        else {
-               lba = get_be32(&fsg->cmnd[2]);
+               lba = get_unaligned_be32(&fsg->cmnd[2]);
 
                /* We allow DPO (Disable Page Out = don't save data in the
                 * cache) and FUA (Force Unit Access = don't read from the
@@ -1717,9 +1699,9 @@ static int do_write(struct fsg_dev *fsg)
        /* Get the starting Logical Block Address and check that it's
         * not too big */
        if (fsg->cmnd[0] == SC_WRITE_6)
-               lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]);
+               lba = get_unaligned_be24(&fsg->cmnd[1]);
        else {
-               lba = get_be32(&fsg->cmnd[2]);
+               lba = get_unaligned_be32(&fsg->cmnd[2]);
 
                /* We allow DPO (Disable Page Out = don't save data in the
                 * cache) and FUA (Force Unit Access = write directly to the
@@ -1940,7 +1922,7 @@ static int do_verify(struct fsg_dev *fsg)
 
        /* Get the starting Logical Block Address and check that it's
         * not too big */
-       lba = get_be32(&fsg->cmnd[2]);
+       lba = get_unaligned_be32(&fsg->cmnd[2]);
        if (lba >= curlun->num_sectors) {
                curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
                return -EINVAL;
@@ -1953,7 +1935,7 @@ static int do_verify(struct fsg_dev *fsg)
                return -EINVAL;
        }
 
-       verification_length = get_be16(&fsg->cmnd[7]);
+       verification_length = get_unaligned_be16(&fsg->cmnd[7]);
        if (unlikely(verification_length == 0))
                return -EIO;            // No default reply
 
@@ -2103,7 +2085,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
        memset(buf, 0, 18);
        buf[0] = valid | 0x70;                  // Valid, current error
        buf[2] = SK(sd);
-       put_be32(&buf[3], sdinfo);              // Sense information
+       put_unaligned_be32(sdinfo, &buf[3]);    /* Sense information */
        buf[7] = 18 - 8;                        // Additional sense length
        buf[12] = ASC(sd);
        buf[13] = ASCQ(sd);
@@ -2114,7 +2096,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
 static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
 {
        struct lun      *curlun = fsg->curlun;
-       u32             lba = get_be32(&fsg->cmnd[2]);
+       u32             lba = get_unaligned_be32(&fsg->cmnd[2]);
        int             pmi = fsg->cmnd[8];
        u8              *buf = (u8 *) bh->buf;
 
@@ -2124,8 +2106,9 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
                return -EINVAL;
        }
 
-       put_be32(&buf[0], curlun->num_sectors - 1);     // Max logical block
-       put_be32(&buf[4], 512);                         // Block length
+       put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+                                               /* Max logical block */
+       put_unaligned_be32(512, &buf[4]);       /* Block length */
        return 8;
 }
 
@@ -2144,7 +2127,7 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr)
                dest[0] = 0;            /* Reserved */
        } else {
                /* Absolute sector */
-               put_be32(dest, addr);
+               put_unaligned_be32(addr, dest);
        }
 }
 
@@ -2152,7 +2135,7 @@ static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
 {
        struct lun      *curlun = fsg->curlun;
        int             msf = fsg->cmnd[1] & 0x02;
-       u32             lba = get_be32(&fsg->cmnd[2]);
+       u32             lba = get_unaligned_be32(&fsg->cmnd[2]);
        u8              *buf = (u8 *) bh->buf;
 
        if ((fsg->cmnd[1] & ~0x02) != 0) {              /* Mask away MSF */
@@ -2252,10 +2235,13 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
                        buf[2] = 0x04;  // Write cache enable,
                                        // Read cache not disabled
                                        // No cache retention priorities
-                       put_be16(&buf[4], 0xffff);  // Don't disable prefetch
-                                       // Minimum prefetch = 0
-                       put_be16(&buf[8], 0xffff);  // Maximum prefetch
-                       put_be16(&buf[10], 0xffff); // Maximum prefetch ceiling
+                       put_unaligned_be16(0xffff, &buf[4]);
+                                       /* Don't disable prefetch */
+                                       /* Minimum prefetch = 0 */
+                       put_unaligned_be16(0xffff, &buf[8]);
+                                       /* Maximum prefetch */
+                       put_unaligned_be16(0xffff, &buf[10]);
+                                       /* Maximum prefetch ceiling */
                }
                buf += 12;
        }
@@ -2272,7 +2258,7 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
        if (mscmnd == SC_MODE_SENSE_6)
                buf0[0] = len - 1;
        else
-               put_be16(buf0, len - 2);
+               put_unaligned_be16(len - 2, buf0);
        return len;
 }
 
@@ -2360,9 +2346,10 @@ static int do_read_format_capacities(struct fsg_dev *fsg,
        buf[3] = 8;             // Only the Current/Maximum Capacity Descriptor
        buf += 4;
 
-       put_be32(&buf[0], curlun->num_sectors);         // Number of blocks
-       put_be32(&buf[4], 512);                         // Block length
-       buf[4] = 0x02;                                  // Current capacity
+       put_unaligned_be32(curlun->num_sectors, &buf[0]);
+                                               /* Number of blocks */
+       put_unaligned_be32(512, &buf[4]);       /* Block length */
+       buf[4] = 0x02;                          /* Current capacity */
        return 12;
 }
 
@@ -2882,7 +2869,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_MODE_SELECT_10:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
                                (1<<1) | (3<<7), 0,
                                "MODE SELECT(10)")) == 0)
@@ -2898,7 +2885,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_MODE_SENSE_10:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (1<<1) | (1<<2) | (3<<7), 0,
                                "MODE SENSE(10)")) == 0)
@@ -2923,7 +2910,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_READ_10:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9;
+               fsg->data_size_from_cmnd =
+                               get_unaligned_be16(&fsg->cmnd[7]) << 9;
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (1<<1) | (0xf<<2) | (3<<7), 1,
                                "READ(10)")) == 0)
@@ -2931,7 +2919,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_READ_12:
-               fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9;
+               fsg->data_size_from_cmnd =
+                               get_unaligned_be32(&fsg->cmnd[6]) << 9;
                if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST,
                                (1<<1) | (0xf<<2) | (0xf<<6), 1,
                                "READ(12)")) == 0)
@@ -2949,7 +2938,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
        case SC_READ_HEADER:
                if (!mod_data.cdrom)
                        goto unknown_cmnd;
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (3<<7) | (0x1f<<1), 1,
                                "READ HEADER")) == 0)
@@ -2959,7 +2948,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
        case SC_READ_TOC:
                if (!mod_data.cdrom)
                        goto unknown_cmnd;
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (7<<6) | (1<<1), 1,
                                "READ TOC")) == 0)
@@ -2967,7 +2956,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_READ_FORMAT_CAPACITIES:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (3<<7), 1,
                                "READ FORMAT CAPACITIES")) == 0)
@@ -3025,7 +3014,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_WRITE_10:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9;
+               fsg->data_size_from_cmnd =
+                               get_unaligned_be16(&fsg->cmnd[7]) << 9;
                if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
                                (1<<1) | (0xf<<2) | (3<<7), 1,
                                "WRITE(10)")) == 0)
@@ -3033,7 +3023,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_WRITE_12:
-               fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9;
+               fsg->data_size_from_cmnd =
+                               get_unaligned_be32(&fsg->cmnd[6]) << 9;
                if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST,
                                (1<<1) | (0xf<<2) | (0xf<<6), 1,
                                "WRITE(12)")) == 0)
diff --git a/drivers/usb/gadget/fsl_mx3_udc.c b/drivers/usb/gadget/fsl_mx3_udc.c
new file mode 100644 (file)
index 0000000..4bc2bf3
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * Description:
+ * Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c
+ * driver to function correctly on these systems.
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fsl_devices.h>
+#include <linux/platform_device.h>
+
+static struct clk *mxc_ahb_clk;
+static struct clk *mxc_usb_clk;
+
+int fsl_udc_clk_init(struct platform_device *pdev)
+{
+       struct fsl_usb2_platform_data *pdata;
+       unsigned long freq;
+       int ret;
+
+       pdata = pdev->dev.platform_data;
+
+       mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb");
+       if (IS_ERR(mxc_ahb_clk))
+               return PTR_ERR(mxc_ahb_clk);
+
+       ret = clk_enable(mxc_ahb_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n");
+               goto eenahb;
+       }
+
+       /* 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;
+       }
+
+       freq = clk_get_rate(mxc_usb_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);
+               goto eclkrate;
+       }
+
+       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:
+       clk_disable(mxc_ahb_clk);
+eenahb:
+       clk_put(mxc_ahb_clk);
+       return ret;
+}
+
+void fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+       struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+       /* 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;
+       }
+}
+
+void fsl_udc_clk_release(void)
+{
+       if (mxc_usb_clk) {
+               clk_disable(mxc_usb_clk);
+               clk_put(mxc_usb_clk);
+       }
+       clk_disable(mxc_ahb_clk);
+       clk_put(mxc_ahb_clk);
+}
similarity index 99%
rename from drivers/usb/gadget/fsl_usb2_udc.c
rename to drivers/usb/gadget/fsl_udc_core.c
index 9d7b95d..42a74b8 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
 #include <linux/dmapool.h>
+#include <linux/delay.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -57,7 +58,9 @@ static const char driver_name[] = "fsl-usb2-udc";
 static const char driver_desc[] = DRIVER_DESC;
 
 static struct usb_dr_device *dr_regs;
+#ifndef CONFIG_ARCH_MXC
 static struct usb_sys_interface *usb_sys_regs;
+#endif
 
 /* it is initialized in probe()  */
 static struct fsl_udc *udc_controller = NULL;
@@ -174,10 +177,34 @@ static void nuke(struct fsl_ep *ep, int status)
 
 static int dr_controller_setup(struct fsl_udc *udc)
 {
-       unsigned int tmp = 0, portctrl = 0, ctrl = 0;
+       unsigned int tmp, portctrl;
+#ifndef CONFIG_ARCH_MXC
+       unsigned int ctrl;
+#endif
        unsigned long timeout;
 #define FSL_UDC_RESET_TIMEOUT 1000
 
+       /* Config PHY interface */
+       portctrl = fsl_readl(&dr_regs->portsc1);
+       portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
+       switch (udc->phy_mode) {
+       case FSL_USB2_PHY_ULPI:
+               portctrl |= PORTSCX_PTS_ULPI;
+               break;
+       case FSL_USB2_PHY_UTMI_WIDE:
+               portctrl |= PORTSCX_PTW_16BIT;
+               /* fall through */
+       case FSL_USB2_PHY_UTMI:
+               portctrl |= PORTSCX_PTS_UTMI;
+               break;
+       case FSL_USB2_PHY_SERIAL:
+               portctrl |= PORTSCX_PTS_FSLS;
+               break;
+       default:
+               return -EINVAL;
+       }
+       fsl_writel(portctrl, &dr_regs->portsc1);
+
        /* Stop and reset the usb controller */
        tmp = fsl_readl(&dr_regs->usbcmd);
        tmp &= ~USB_CMD_RUN_STOP;
@@ -215,31 +242,12 @@ static int dr_controller_setup(struct fsl_udc *udc)
                udc->ep_qh, (int)tmp,
                fsl_readl(&dr_regs->endpointlistaddr));
 
-       /* Config PHY interface */
-       portctrl = fsl_readl(&dr_regs->portsc1);
-       portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
-       switch (udc->phy_mode) {
-       case FSL_USB2_PHY_ULPI:
-               portctrl |= PORTSCX_PTS_ULPI;
-               break;
-       case FSL_USB2_PHY_UTMI_WIDE:
-               portctrl |= PORTSCX_PTW_16BIT;
-               /* fall through */
-       case FSL_USB2_PHY_UTMI:
-               portctrl |= PORTSCX_PTS_UTMI;
-               break;
-       case FSL_USB2_PHY_SERIAL:
-               portctrl |= PORTSCX_PTS_FSLS;
-               break;
-       default:
-               return -EINVAL;
-       }
-       fsl_writel(portctrl, &dr_regs->portsc1);
-
        /* Config control enable i/o output, cpu endian register */
+#ifndef CONFIG_ARCH_MXC
        ctrl = __raw_readl(&usb_sys_regs->control);
        ctrl |= USB_CTRL_IOENB;
        __raw_writel(ctrl, &usb_sys_regs->control);
+#endif
 
 #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
        /* Turn on cache snooping hardware, since some PowerPC platforms
@@ -2043,6 +2051,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
        size -= t;
        next += t;
 
+#ifndef CONFIG_ARCH_MXC
        tmp_reg = usb_sys_regs->snoop1;
        t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
        size -= t;
@@ -2053,6 +2062,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
                        tmp_reg);
        size -= t;
        next += t;
+#endif
 
        /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
        ep = &udc->eps[0];
@@ -2263,14 +2273,21 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
                goto err_kfree;
        }
 
-       dr_regs = ioremap(res->start, res->end - res->start + 1);
+       dr_regs = ioremap(res->start, resource_size(res));
        if (!dr_regs) {
                ret = -ENOMEM;
                goto err_release_mem_region;
        }
 
+#ifndef CONFIG_ARCH_MXC
        usb_sys_regs = (struct usb_sys_interface *)
                        ((u32)dr_regs + USB_DR_SYS_OFFSET);
+#endif
+
+       /* Initialize USB clocks */
+       ret = fsl_udc_clk_init(pdev);
+       if (ret < 0)
+               goto err_iounmap_noclk;
 
        /* Read Device Controller Capability Parameters register */
        dccparams = fsl_readl(&dr_regs->dccparams);
@@ -2308,6 +2325,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
         * leave usbintr reg untouched */
        dr_controller_setup(udc_controller);
 
+       fsl_udc_clk_finalize(pdev);
+
        /* Setup gadget structure */
        udc_controller->gadget.ops = &fsl_gadget_ops;
        udc_controller->gadget.is_dualspeed = 1;
@@ -2362,6 +2381,8 @@ err_unregister:
 err_free_irq:
        free_irq(udc_controller->irq, udc_controller);
 err_iounmap:
+       fsl_udc_clk_release();
+err_iounmap_noclk:
        iounmap(dr_regs);
 err_release_mem_region:
        release_mem_region(res->start, res->end - res->start + 1);
@@ -2384,6 +2405,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
                return -ENODEV;
        udc_controller->done = &done;
 
+       fsl_udc_clk_release();
+
        /* DR has been stopped in usb_gadget_unregister_driver() */
        remove_proc_file();
 
index e63ef12..20aecee 100644 (file)
@@ -563,4 +563,22 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
                                        * 2 + ((windex & USB_DIR_IN) ? 1 : 0))
 #define get_pipe_by_ep(EP)     (ep_index(EP) * 2 + ep_is_in(EP))
 
+struct platform_device;
+#ifdef CONFIG_ARCH_MXC
+int fsl_udc_clk_init(struct platform_device *pdev);
+void fsl_udc_clk_finalize(struct platform_device *pdev);
+void fsl_udc_clk_release(void);
+#else
+static inline int fsl_udc_clk_init(struct platform_device *pdev)
+{
+       return 0;
+}
+static inline void fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+}
+static inline void fsl_udc_clk_release(void)
+{
+}
+#endif
+
 #endif
index ec6d439..8e0e9a0 100644 (file)
 #define gadget_is_musbhdrc(g)  0
 #endif
 
+#ifdef CONFIG_USB_GADGET_LANGWELL
+#define gadget_is_langwell(g)  (!strcmp("langwell_udc", (g)->name))
+#else
+#define gadget_is_langwell(g)  0
+#endif
+
 /* from Montavista kernel (?) */
 #ifdef CONFIG_USB_GADGET_MPC8272
 #define gadget_is_mpc8272(g)   !strcmp("mpc8272_udc", (g)->name)
@@ -231,6 +237,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
                return 0x22;
        else if (gadget_is_ci13xxx(gadget))
                return 0x23;
+       else if (gadget_is_langwell(gadget))
+               return 0x24;
        return -ENOENT;
 }
 
index de010c9..112bb40 100644 (file)
@@ -110,10 +110,10 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
                return -EINVAL;
        if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
                return -ESHUTDOWN;
-       if (ep->num != (desc->bEndpointAddress & 0x0f))
+       if (ep->num != usb_endpoint_num(desc))
                return -EINVAL;
 
-       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       switch (usb_endpoint_type(desc)) {
        case USB_ENDPOINT_XFER_BULK:
        case USB_ENDPOINT_XFER_INT:
                break;
@@ -142,7 +142,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        /* ep1/ep2 dma direction is chosen early; it works in the other
         * direction, with pio.  be cautious with out-dma.
         */
-       ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;
+       ep->is_in = usb_endpoint_dir_in(desc);
        if (ep->is_in) {
                mode |= 1;
                ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT);
index 168658b..c52a681 100644 (file)
@@ -415,6 +415,13 @@ static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req)
        u8      *buf;
        int     length, count, temp;
 
+       if (unlikely(__raw_readl(imx_ep->imx_usb->base +
+                                USB_EP_STAT(EP_NO(imx_ep))) & EPSTAT_ZLPS)) {
+               D_TRX(imx_ep->imx_usb->dev, "<%s> zlp still queued in EP %s\n",
+                       __func__, imx_ep->ep.name);
+               return -1;
+       }
+
        buf = req->req.buf + req->req.actual;
        prefetch(buf);
 
@@ -734,9 +741,12 @@ static struct usb_request *imx_ep_alloc_request
 {
        struct imx_request *req;
 
+       if (!usb_ep)
+               return NULL;
+
        req = kzalloc(sizeof *req, gfp_flags);
-       if (!req || !usb_ep)
-               return 0;
+       if (!req)
+               return NULL;
 
        INIT_LIST_HEAD(&req->queue);
        req->in_use = 0;
index d20937f..7d33f50 100644 (file)
@@ -384,9 +384,8 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                return value;
 
        /* halt any endpoint by doing a "wrong direction" i/o call */
-       if (data->desc.bEndpointAddress & USB_DIR_IN) {
-               if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                               == USB_ENDPOINT_XFER_ISOC)
+       if (usb_endpoint_dir_in(&data->desc)) {
+               if (usb_endpoint_xfer_isoc(&data->desc))
                        return -EINVAL;
                DBG (data->dev, "%s halt\n", data->name);
                spin_lock_irq (&data->dev->lock);
@@ -428,9 +427,8 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
                return value;
 
        /* halt any endpoint by doing a "wrong direction" i/o call */
-       if (!(data->desc.bEndpointAddress & USB_DIR_IN)) {
-               if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                               == USB_ENDPOINT_XFER_ISOC)
+       if (!usb_endpoint_dir_in(&data->desc)) {
+               if (usb_endpoint_xfer_isoc(&data->desc))
                        return -EINVAL;
                DBG (data->dev, "%s halt\n", data->name);
                spin_lock_irq (&data->dev->lock);
@@ -691,7 +689,7 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
        struct ep_data          *epdata = iocb->ki_filp->private_data;
        char                    *buf;
 
-       if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
+       if (unlikely(usb_endpoint_dir_in(&epdata->desc)))
                return -EINVAL;
 
        buf = kmalloc(iocb->ki_left, GFP_KERNEL);
@@ -711,7 +709,7 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
        size_t                  len = 0;
        int                     i = 0;
 
-       if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
+       if (unlikely(!usb_endpoint_dir_in(&epdata->desc)))
                return -EINVAL;
 
        buf = kmalloc(iocb->ki_left, GFP_KERNEL);
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
new file mode 100644 (file)
index 0000000..6829d59
--- /dev/null
@@ -0,0 +1,3373 @@
+/*
+ * Intel Langwell USB Device Controller driver
+ * Copyright (C) 2008-2009, Intel Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+/* #undef      DEBUG */
+/* #undef      VERBOSE */
+
+#if defined(CONFIG_USB_LANGWELL_OTG)
+#define        OTG_TRANSCEIVER
+#endif
+
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.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/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include "langwell_udc.h"
+
+
+#define        DRIVER_DESC             "Intel Langwell USB Device Controller driver"
+#define        DRIVER_VERSION          "16 May 2009"
+
+static const char driver_name[] = "langwell_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+
+/* controller device global variable */
+static struct langwell_udc     *the_controller;
+
+/* for endpoint 0 operations */
+static const struct usb_endpoint_descriptor
+langwell_ep0_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     0,
+       .bmAttributes =         USB_ENDPOINT_XFER_CONTROL,
+       .wMaxPacketSize =       EP0_MAX_PKT_SIZE,
+};
+
+
+/*-------------------------------------------------------------------------*/
+/* debugging */
+
+#ifdef DEBUG
+#define        DBG(dev, fmt, args...) \
+       pr_debug("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+#else
+#define        DBG(dev, fmt, args...) \
+       do { } while (0)
+#endif /* DEBUG */
+
+
+#ifdef VERBOSE
+#define        VDBG DBG
+#else
+#define        VDBG(dev, fmt, args...) \
+       do { } while (0)
+#endif /* VERBOSE */
+
+
+#define        ERROR(dev, fmt, args...) \
+       pr_err("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+#define        WARNING(dev, fmt, args...) \
+       pr_warning("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+#define        INFO(dev, fmt, args...) \
+       pr_info("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+
+#ifdef VERBOSE
+static inline void print_all_registers(struct langwell_udc *dev)
+{
+       int     i;
+
+       /* Capability Registers */
+       printk(KERN_DEBUG "Capability Registers (offset: "
+                       "0x%04x, length: 0x%08x)\n",
+                       CAP_REG_OFFSET,
+                       (u32)sizeof(struct langwell_cap_regs));
+       printk(KERN_DEBUG "caplength=0x%02x\n",
+                       readb(&dev->cap_regs->caplength));
+       printk(KERN_DEBUG "hciversion=0x%04x\n",
+                       readw(&dev->cap_regs->hciversion));
+       printk(KERN_DEBUG "hcsparams=0x%08x\n",
+                       readl(&dev->cap_regs->hcsparams));
+       printk(KERN_DEBUG "hccparams=0x%08x\n",
+                       readl(&dev->cap_regs->hccparams));
+       printk(KERN_DEBUG "dciversion=0x%04x\n",
+                       readw(&dev->cap_regs->dciversion));
+       printk(KERN_DEBUG "dccparams=0x%08x\n",
+                       readl(&dev->cap_regs->dccparams));
+
+       /* Operational Registers */
+       printk(KERN_DEBUG "Operational Registers (offset: "
+                       "0x%04x, length: 0x%08x)\n",
+                       OP_REG_OFFSET,
+                       (u32)sizeof(struct langwell_op_regs));
+       printk(KERN_DEBUG "extsts=0x%08x\n",
+                       readl(&dev->op_regs->extsts));
+       printk(KERN_DEBUG "extintr=0x%08x\n",
+                       readl(&dev->op_regs->extintr));
+       printk(KERN_DEBUG "usbcmd=0x%08x\n",
+                       readl(&dev->op_regs->usbcmd));
+       printk(KERN_DEBUG "usbsts=0x%08x\n",
+                       readl(&dev->op_regs->usbsts));
+       printk(KERN_DEBUG "usbintr=0x%08x\n",
+                       readl(&dev->op_regs->usbintr));
+       printk(KERN_DEBUG "frindex=0x%08x\n",
+                       readl(&dev->op_regs->frindex));
+       printk(KERN_DEBUG "ctrldssegment=0x%08x\n",
+                       readl(&dev->op_regs->ctrldssegment));
+       printk(KERN_DEBUG "deviceaddr=0x%08x\n",
+                       readl(&dev->op_regs->deviceaddr));
+       printk(KERN_DEBUG "endpointlistaddr=0x%08x\n",
+                       readl(&dev->op_regs->endpointlistaddr));
+       printk(KERN_DEBUG "ttctrl=0x%08x\n",
+                       readl(&dev->op_regs->ttctrl));
+       printk(KERN_DEBUG "burstsize=0x%08x\n",
+                       readl(&dev->op_regs->burstsize));
+       printk(KERN_DEBUG "txfilltuning=0x%08x\n",
+                       readl(&dev->op_regs->txfilltuning));
+       printk(KERN_DEBUG "txttfilltuning=0x%08x\n",
+                       readl(&dev->op_regs->txttfilltuning));
+       printk(KERN_DEBUG "ic_usb=0x%08x\n",
+                       readl(&dev->op_regs->ic_usb));
+       printk(KERN_DEBUG "ulpi_viewport=0x%08x\n",
+                       readl(&dev->op_regs->ulpi_viewport));
+       printk(KERN_DEBUG "configflag=0x%08x\n",
+                       readl(&dev->op_regs->configflag));
+       printk(KERN_DEBUG "portsc1=0x%08x\n",
+                       readl(&dev->op_regs->portsc1));
+       printk(KERN_DEBUG "devlc=0x%08x\n",
+                       readl(&dev->op_regs->devlc));
+       printk(KERN_DEBUG "otgsc=0x%08x\n",
+                       readl(&dev->op_regs->otgsc));
+       printk(KERN_DEBUG "usbmode=0x%08x\n",
+                       readl(&dev->op_regs->usbmode));
+       printk(KERN_DEBUG "endptnak=0x%08x\n",
+                       readl(&dev->op_regs->endptnak));
+       printk(KERN_DEBUG "endptnaken=0x%08x\n",
+                       readl(&dev->op_regs->endptnaken));
+       printk(KERN_DEBUG "endptsetupstat=0x%08x\n",
+                       readl(&dev->op_regs->endptsetupstat));
+       printk(KERN_DEBUG "endptprime=0x%08x\n",
+                       readl(&dev->op_regs->endptprime));
+       printk(KERN_DEBUG "endptflush=0x%08x\n",
+                       readl(&dev->op_regs->endptflush));
+       printk(KERN_DEBUG "endptstat=0x%08x\n",
+                       readl(&dev->op_regs->endptstat));
+       printk(KERN_DEBUG "endptcomplete=0x%08x\n",
+                       readl(&dev->op_regs->endptcomplete));
+
+       for (i = 0; i < dev->ep_max / 2; i++) {
+               printk(KERN_DEBUG "endptctrl[%d]=0x%08x\n",
+                               i, readl(&dev->op_regs->endptctrl[i]));
+       }
+}
+#endif /* VERBOSE */
+
+
+/*-------------------------------------------------------------------------*/
+
+#define        DIR_STRING(bAddress)    (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+#define is_in(ep)      (((ep)->ep_num == 0) ? ((ep)->dev->ep0_dir == \
+                       USB_DIR_IN) : ((ep)->desc->bEndpointAddress \
+                       & USB_DIR_IN) == USB_DIR_IN)
+
+
+#ifdef DEBUG
+static char *type_string(u8 bmAttributes)
+{
+       switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+       case USB_ENDPOINT_XFER_BULK:
+               return "bulk";
+       case USB_ENDPOINT_XFER_ISOC:
+               return "iso";
+       case USB_ENDPOINT_XFER_INT:
+               return "int";
+       };
+
+       return "control";
+}
+#endif
+
+
+/* configure endpoint control registers */
+static void ep_reset(struct langwell_ep *ep, unsigned char ep_num,
+               unsigned char is_in, unsigned char ep_type)
+{
+       struct langwell_udc     *dev;
+       u32                     endptctrl;
+
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in) {    /* TX */
+               if (ep_num)
+                       endptctrl |= EPCTRL_TXR;
+               endptctrl |= EPCTRL_TXE;
+               endptctrl |= ep_type << EPCTRL_TXT_SHIFT;
+       } else {        /* RX */
+               if (ep_num)
+                       endptctrl |= EPCTRL_RXR;
+               endptctrl |= EPCTRL_RXE;
+               endptctrl |= ep_type << EPCTRL_RXT_SHIFT;
+       }
+
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* reset ep0 dQH and endptctrl */
+static void ep0_reset(struct langwell_udc *dev)
+{
+       struct langwell_ep      *ep;
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* ep0 in and out */
+       for (i = 0; i < 2; i++) {
+               ep = &dev->ep[i];
+               ep->dev = dev;
+
+               /* ep0 dQH */
+               ep->dqh = &dev->ep_dqh[i];
+
+               /* configure ep0 endpoint capabilities in dQH */
+               ep->dqh->dqh_ios = 1;
+               ep->dqh->dqh_mpl = EP0_MAX_PKT_SIZE;
+
+               /* FIXME: enable ep0-in HW zero length termination select */
+               if (is_in(ep))
+                       ep->dqh->dqh_zlt = 0;
+               ep->dqh->dqh_mult = 0;
+
+               /* configure ep0 control registers */
+               ep_reset(&dev->ep[0], 0, i, USB_ENDPOINT_XFER_CONTROL);
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoints operations */
+
+/* configure endpoint, making it usable */
+static int langwell_ep_enable(struct usb_ep *_ep,
+               const struct usb_endpoint_descriptor *desc)
+{
+       struct langwell_udc     *dev;
+       struct langwell_ep      *ep;
+       u16                     max = 0;
+       unsigned long           flags;
+       int                     retval = 0;
+       unsigned char           zlt, ios = 0, mult = 0;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !desc || ep->desc
+                       || desc->bDescriptorType != USB_DT_ENDPOINT)
+               return -EINVAL;
+
+       if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       max = le16_to_cpu(desc->wMaxPacketSize);
+
+       /*
+        * disable HW zero length termination select
+        * driver handles zero length packet through req->req.zero
+        */
+       zlt = 1;
+
+       /*
+        * sanity check type, direction, address, and then
+        * initialize the endpoint capabilities fields in dQH
+        */
+       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       case USB_ENDPOINT_XFER_CONTROL:
+               ios = 1;
+               break;
+       case USB_ENDPOINT_XFER_BULK:
+               if ((dev->gadget.speed == USB_SPEED_HIGH
+                                       && max != 512)
+                               || (dev->gadget.speed == USB_SPEED_FULL
+                                       && max > 64)) {
+                       goto done;
+               }
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               if (strstr(ep->ep.name, "-iso")) /* bulk is ok */
+                       goto done;
+
+               switch (dev->gadget.speed) {
+               case USB_SPEED_HIGH:
+                       if (max <= 1024)
+                               break;
+               case USB_SPEED_FULL:
+                       if (max <= 64)
+                               break;
+               default:
+                       if (max <= 8)
+                               break;
+                       goto done;
+               }
+               break;
+       case USB_ENDPOINT_XFER_ISOC:
+               if (strstr(ep->ep.name, "-bulk")
+                               || strstr(ep->ep.name, "-int"))
+                       goto done;
+
+               switch (dev->gadget.speed) {
+               case USB_SPEED_HIGH:
+                       if (max <= 1024)
+                               break;
+               case USB_SPEED_FULL:
+                       if (max <= 1023)
+                               break;
+               default:
+                       goto done;
+               }
+               /*
+                * FIXME:
+                * calculate transactions needed for high bandwidth iso
+                */
+               mult = (unsigned char)(1 + ((max >> 11) & 0x03));
+               max = max & 0x8ff;      /* bit 0~10 */
+               /* 3 transactions at most */
+               if (mult > 3)
+                       goto done;
+               break;
+       default:
+               goto done;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* configure endpoint capabilities in dQH */
+       ep->dqh->dqh_ios = ios;
+       ep->dqh->dqh_mpl = cpu_to_le16(max);
+       ep->dqh->dqh_zlt = zlt;
+       ep->dqh->dqh_mult = mult;
+
+       ep->ep.maxpacket = max;
+       ep->desc = desc;
+       ep->stopped = 0;
+       ep->ep_num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+       /* ep_type */
+       ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+       /* configure endpoint control registers */
+       ep_reset(ep, ep->ep_num, is_in(ep), ep->ep_type);
+
+       DBG(dev, "enabled %s (ep%d%s-%s), max %04x\n",
+                       _ep->name,
+                       ep->ep_num,
+                       DIR_STRING(desc->bEndpointAddress),
+                       type_string(desc->bmAttributes),
+                       max);
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+done:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* retire a request */
+static void done(struct langwell_ep *ep, struct langwell_request *req,
+               int status)
+{
+       struct langwell_udc     *dev = ep->dev;
+       unsigned                stopped = ep->stopped;
+       struct langwell_dtd     *curr_dtd, *next_dtd;
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* remove the req from ep->queue */
+       list_del_init(&req->queue);
+
+       if (req->req.status == -EINPROGRESS)
+               req->req.status = status;
+       else
+               status = req->req.status;
+
+       /* free dTD for the request */
+       next_dtd = req->head;
+       for (i = 0; i < req->dtd_count; i++) {
+               curr_dtd = next_dtd;
+               if (i != req->dtd_count - 1)
+                       next_dtd = curr_dtd->next_dtd_virt;
+               dma_pool_free(dev->dtd_pool, curr_dtd, curr_dtd->dtd_dma);
+       }
+
+       if (req->mapped) {
+               dma_unmap_single(&dev->pdev->dev, req->req.dma, req->req.length,
+                       is_in(ep) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+               req->req.dma = DMA_ADDR_INVALID;
+               req->mapped = 0;
+       } else
+               dma_sync_single_for_cpu(&dev->pdev->dev, req->req.dma,
+                               req->req.length,
+                               is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+       if (status != -ESHUTDOWN)
+               DBG(dev, "complete %s, req %p, stat %d, len %u/%u\n",
+                       ep->ep.name, &req->req, status,
+                       req->req.actual, req->req.length);
+
+       /* don't modify queue heads during completion callback */
+       ep->stopped = 1;
+
+       spin_unlock(&dev->lock);
+       /* complete routine from gadget driver */
+       if (req->req.complete)
+               req->req.complete(&ep->ep, &req->req);
+
+       spin_lock(&dev->lock);
+       ep->stopped = stopped;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+static void langwell_ep_fifo_flush(struct usb_ep *_ep);
+
+/* delete all endpoint requests, called with spinlock held */
+static void nuke(struct langwell_ep *ep, int status)
+{
+       /* called with spinlock held */
+       ep->stopped = 1;
+
+       /* endpoint fifo flush */
+       if (&ep->ep && ep->desc)
+               langwell_ep_fifo_flush(&ep->ep);
+
+       while (!list_empty(&ep->queue)) {
+               struct langwell_request *req = NULL;
+               req = list_entry(ep->queue.next, struct langwell_request,
+                               queue);
+               done(ep, req, status);
+       }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint is no longer usable */
+static int langwell_ep_disable(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       unsigned long           flags;
+       struct langwell_udc     *dev;
+       int                     ep_num;
+       u32                     endptctrl;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* disable endpoint control register */
+       ep_num = ep->ep_num;
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl &= ~EPCTRL_TXE;
+       else
+               endptctrl &= ~EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       /* nuke all pending requests (does flush) */
+       nuke(ep, -ESHUTDOWN);
+
+       ep->desc = NULL;
+       ep->stopped = 1;
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       DBG(dev, "disabled %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+
+       return 0;
+}
+
+
+/* allocate a request object to use with this endpoint */
+static struct usb_request *langwell_alloc_request(struct usb_ep *_ep,
+               gfp_t gfp_flags)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req = NULL;
+
+       if (!_ep)
+               return NULL;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       req = kzalloc(sizeof(*req), gfp_flags);
+       if (!req)
+               return NULL;
+
+       req->req.dma = DMA_ADDR_INVALID;
+       INIT_LIST_HEAD(&req->queue);
+
+       VDBG(dev, "alloc request for %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+       return &req->req;
+}
+
+
+/* free a request object */
+static void langwell_free_request(struct usb_ep *_ep,
+               struct usb_request *_req)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req = NULL;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !_req)
+               return;
+
+       req = container_of(_req, struct langwell_request, req);
+       WARN_ON(!list_empty(&req->queue));
+
+       if (_req)
+               kfree(req);
+
+       VDBG(dev, "free request for %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* queue dTD and PRIME endpoint */
+static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req)
+{
+       u32                     bit_mask, usbcmd, endptstat, dtd_dma;
+       u8                      dtd_status;
+       int                     i;
+       struct langwell_dqh     *dqh;
+       struct langwell_udc     *dev;
+
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       i = ep->ep_num * 2 + is_in(ep);
+       dqh = &dev->ep_dqh[i];
+
+       if (ep->ep_num)
+               VDBG(dev, "%s\n", ep->name);
+       else
+               /* ep0 */
+               VDBG(dev, "%s-%s\n", ep->name, is_in(ep) ? "in" : "out");
+
+       VDBG(dev, "ep_dqh[%d] addr: 0x%08x\n", i, (u32)&(dev->ep_dqh[i]));
+
+       bit_mask = is_in(ep) ?
+               (1 << (ep->ep_num + 16)) : (1 << (ep->ep_num));
+
+       VDBG(dev, "bit_mask = 0x%08x\n", bit_mask);
+
+       /* check if the pipe is empty */
+       if (!(list_empty(&ep->queue))) {
+               /* add dTD to the end of linked list */
+               struct langwell_request *lastreq;
+               lastreq = list_entry(ep->queue.prev,
+                               struct langwell_request, queue);
+
+               lastreq->tail->dtd_next =
+                       cpu_to_le32(req->head->dtd_dma & DTD_NEXT_MASK);
+
+               /* read prime bit, if 1 goto out */
+               if (readl(&dev->op_regs->endptprime) & bit_mask)
+                       goto out;
+
+               do {
+                       /* set ATDTW bit in USBCMD */
+                       usbcmd = readl(&dev->op_regs->usbcmd);
+                       writel(usbcmd | CMD_ATDTW, &dev->op_regs->usbcmd);
+
+                       /* read correct status bit */
+                       endptstat = readl(&dev->op_regs->endptstat) & bit_mask;
+
+               } while (!(readl(&dev->op_regs->usbcmd) & CMD_ATDTW));
+
+               /* write ATDTW bit to 0 */
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               writel(usbcmd & ~CMD_ATDTW, &dev->op_regs->usbcmd);
+
+               if (endptstat)
+                       goto out;
+       }
+
+       /* write dQH next pointer and terminate bit to 0 */
+       dtd_dma = req->head->dtd_dma & DTD_NEXT_MASK;
+       dqh->dtd_next = cpu_to_le32(dtd_dma);
+
+       /* clear active and halt bit */
+       dtd_status = (u8) ~(DTD_STS_ACTIVE | DTD_STS_HALTED);
+       dqh->dtd_status &= dtd_status;
+       VDBG(dev, "dqh->dtd_status = 0x%x\n", dqh->dtd_status);
+
+       /* write 1 to endptprime register to PRIME endpoint */
+       bit_mask = is_in(ep) ? (1 << (ep->ep_num + 16)) : (1 << ep->ep_num);
+       VDBG(dev, "endprime bit_mask = 0x%08x\n", bit_mask);
+       writel(bit_mask, &dev->op_regs->endptprime);
+out:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* fill in the dTD structure to build a transfer descriptor */
+static struct langwell_dtd *build_dtd(struct langwell_request *req,
+               unsigned *length, dma_addr_t *dma, int *is_last)
+{
+       u32                      buf_ptr;
+       struct langwell_dtd     *dtd;
+       struct langwell_udc     *dev;
+       int                     i;
+
+       dev = req->ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* the maximum transfer length, up to 16k bytes */
+       *length = min(req->req.length - req->req.actual,
+                       (unsigned)DTD_MAX_TRANSFER_LENGTH);
+
+       /* create dTD dma_pool resource */
+       dtd = dma_pool_alloc(dev->dtd_pool, GFP_KERNEL, dma);
+       if (dtd == NULL)
+               return dtd;
+       dtd->dtd_dma = *dma;
+
+       /* initialize buffer page pointers */
+       buf_ptr = (u32)(req->req.dma + req->req.actual);
+       for (i = 0; i < 5; i++)
+               dtd->dtd_buf[i] = cpu_to_le32(buf_ptr + i * PAGE_SIZE);
+
+       req->req.actual += *length;
+
+       /* fill in total bytes with transfer size */
+       dtd->dtd_total = cpu_to_le16(*length);
+       VDBG(dev, "dtd->dtd_total = %d\n", dtd->dtd_total);
+
+       /* set is_last flag if req->req.zero is set or not */
+       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;
+
+       if (*is_last == 0)
+               VDBG(dev, "multi-dtd request!\n");
+
+       /* set interrupt on complete bit for the last dTD */
+       if (*is_last && !req->req.no_interrupt)
+               dtd->dtd_ioc = 1;
+
+       /* set multiplier override 0 for non-ISO and non-TX endpoint */
+       dtd->dtd_multo = 0;
+
+       /* set the active bit of status field to 1 */
+       dtd->dtd_status = DTD_STS_ACTIVE;
+       VDBG(dev, "dtd->dtd_status = 0x%02x\n", dtd->dtd_status);
+
+       VDBG(dev, "length = %d, dma addr= 0x%08x\n", *length, (int)*dma);
+       VDBG(dev, "<--- %s()\n", __func__);
+       return dtd;
+}
+
+
+/* generate dTD linked list for a request */
+static int req_to_dtd(struct langwell_request *req)
+{
+       unsigned                count;
+       int                     is_last, is_first = 1;
+       struct langwell_dtd     *dtd, *last_dtd = NULL;
+       struct langwell_udc     *dev;
+       dma_addr_t              dma;
+
+       dev = req->ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+       do {
+               dtd = build_dtd(req, &count, &dma, &is_last);
+               if (dtd == NULL)
+                       return -ENOMEM;
+
+               if (is_first) {
+                       is_first = 0;
+                       req->head = dtd;
+               } else {
+                       last_dtd->dtd_next = cpu_to_le32(dma);
+                       last_dtd->next_dtd_virt = dtd;
+               }
+               last_dtd = dtd;
+               req->dtd_count++;
+       } while (!is_last);
+
+       /* set terminate bit to 1 for the last dTD */
+       dtd->dtd_next = DTD_TERM;
+
+       req->tail = dtd;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* queue (submits) an I/O requests to an endpoint */
+static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+               gfp_t gfp_flags)
+{
+       struct langwell_request *req;
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       int                     is_iso = 0, zlflag = 0;
+
+       /* always require a cpu-view buffer */
+       req = container_of(_req, struct langwell_request, req);
+       ep = container_of(_ep, struct langwell_ep, ep);
+
+       if (!_req || !_req->complete || !_req->buf
+                       || !list_empty(&req->queue)) {
+               return -EINVAL;
+       }
+
+       if (unlikely(!_ep || !ep->desc))
+               return -EINVAL;
+
+       dev = ep->dev;
+       req->ep = ep;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+               if (req->req.length > ep->ep.maxpacket)
+                       return -EMSGSIZE;
+               is_iso = 1;
+       }
+
+       if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
+               return -ESHUTDOWN;
+
+       /* set up dma mapping in case the caller didn't */
+       if (_req->dma == DMA_ADDR_INVALID) {
+               /* WORKAROUND: WARN_ON(size == 0) */
+               if (_req->length == 0) {
+                       VDBG(dev, "req->length: 0->1\n");
+                       zlflag = 1;
+                       _req->length++;
+               }
+
+               _req->dma = dma_map_single(&dev->pdev->dev,
+                               _req->buf, _req->length,
+                               is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               if (zlflag && (_req->length == 1)) {
+                       VDBG(dev, "req->length: 1->0\n");
+                       zlflag = 0;
+                       _req->length = 0;
+               }
+
+               req->mapped = 1;
+               VDBG(dev, "req->mapped = 1\n");
+       } else {
+               dma_sync_single_for_device(&dev->pdev->dev,
+                               _req->dma, _req->length,
+                               is_in(ep) ?  DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               req->mapped = 0;
+               VDBG(dev, "req->mapped = 0\n");
+       }
+
+       DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n",
+                       _ep->name,
+                       _req, _req->length, _req->buf, _req->dma);
+
+       _req->status = -EINPROGRESS;
+       _req->actual = 0;
+       req->dtd_count = 0;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* build and put dTDs to endpoint queue */
+       if (!req_to_dtd(req)) {
+               queue_dtd(ep, req);
+       } else {
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return -ENOMEM;
+       }
+
+       /* update ep0 state */
+       if (ep->ep_num == 0)
+               dev->ep0_state = DATA_STATE_XMIT;
+
+       if (likely(req != NULL)) {
+               list_add_tail(&req->queue, &ep->queue);
+               VDBG(dev, "list_add_tail() \n");
+       }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* dequeue (cancels, unlinks) an I/O request from an endpoint */
+static int langwell_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req;
+       unsigned long           flags;
+       int                     stopped, ep_num, retval = 0;
+       u32                     endptctrl;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc || !_req)
+               return -EINVAL;
+
+       if (!dev->driver)
+               return -ESHUTDOWN;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       stopped = ep->stopped;
+
+       /* quiesce dma while we patch the queue */
+       ep->stopped = 1;
+       ep_num = ep->ep_num;
+
+       /* disable endpoint control register */
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl &= ~EPCTRL_TXE;
+       else
+               endptctrl &= ~EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       /* make sure it's still queued on this endpoint */
+       list_for_each_entry(req, &ep->queue, queue) {
+               if (&req->req == _req)
+                       break;
+       }
+
+       if (&req->req != _req) {
+               retval = -EINVAL;
+               goto done;
+       }
+
+       /* queue head may be partially complete. */
+       if (ep->queue.next == &req->queue) {
+               DBG(dev, "unlink (%s) dma\n", _ep->name);
+               _req->status = -ECONNRESET;
+               langwell_ep_fifo_flush(&ep->ep);
+
+               /* not the last request in endpoint queue */
+               if (likely(ep->queue.next == &req->queue)) {
+                       struct langwell_dqh     *dqh;
+                       struct langwell_request *next_req;
+
+                       dqh = ep->dqh;
+                       next_req = list_entry(req->queue.next,
+                                       struct langwell_request, queue);
+
+                       /* point the dQH to the first dTD of next request */
+                       writel((u32) next_req->head, &dqh->dqh_current);
+               }
+       } else {
+               struct langwell_request *prev_req;
+
+               prev_req = list_entry(req->queue.prev,
+                               struct langwell_request, queue);
+               writel(readl(&req->tail->dtd_next),
+                               &prev_req->tail->dtd_next);
+       }
+
+       done(ep, req, -ECONNRESET);
+
+done:
+       /* enable endpoint again */
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl |= EPCTRL_TXE;
+       else
+               endptctrl |= EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       ep->stopped = stopped;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint set/clear halt */
+static void ep_set_halt(struct langwell_ep *ep, int value)
+{
+       u32                     endptctrl = 0;
+       int                     ep_num;
+       struct langwell_udc     *dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       ep_num = ep->ep_num;
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+
+       /* value: 1 - set halt, 0 - clear halt */
+       if (value) {
+               /* set the stall bit */
+               if (is_in(ep))
+                       endptctrl |= EPCTRL_TXS;
+               else
+                       endptctrl |= EPCTRL_RXS;
+       } else {
+               /* clear the stall bit and reset data toggle */
+               if (is_in(ep)) {
+                       endptctrl &= ~EPCTRL_TXS;
+                       endptctrl |= EPCTRL_TXR;
+               } else {
+                       endptctrl &= ~EPCTRL_RXS;
+                       endptctrl |= EPCTRL_RXR;
+               }
+       }
+
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* set the endpoint halt feature */
+static int langwell_ep_set_halt(struct usb_ep *_ep, int value)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       int                     retval = 0;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       if (ep->desc && (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+                       == USB_ENDPOINT_XFER_ISOC)
+               return  -EOPNOTSUPP;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /*
+        * attempt to halt IN ep will fail if any transfer requests
+        * are still queue
+        */
+       if (!list_empty(&ep->queue) && is_in(ep) && value) {
+               /* IN endpoint FIFO holds bytes */
+               DBG(dev, "%s FIFO holds bytes\n", _ep->name);
+               retval = -EAGAIN;
+               goto done;
+       }
+
+       /* endpoint set/clear halt */
+       if (ep->ep_num) {
+               ep_set_halt(ep, value);
+       } else { /* endpoint 0 */
+               dev->ep0_state = WAIT_FOR_SETUP;
+               dev->ep0_dir = USB_DIR_OUT;
+       }
+done:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       DBG(dev, "%s %s halt\n", _ep->name, value ? "set" : "clear");
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/* set the halt feature and ignores clear requests */
+static int langwell_ep_set_wedge(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return usb_ep_set_halt(_ep);
+}
+
+
+/* flush contents of a fifo */
+static void langwell_ep_fifo_flush(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       u32                     flush_bit;
+       unsigned long           timeout;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc) {
+               VDBG(dev, "ep or ep->desc is NULL\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               return;
+       }
+
+       VDBG(dev, "%s-%s fifo flush\n", _ep->name, is_in(ep) ? "in" : "out");
+
+       /* flush endpoint buffer */
+       if (ep->ep_num == 0)
+               flush_bit = (1 << 16) | 1;
+       else if (is_in(ep))
+               flush_bit = 1 << (ep->ep_num + 16);     /* TX */
+       else
+               flush_bit = 1 << ep->ep_num;            /* RX */
+
+       /* wait until flush complete */
+       timeout = jiffies + FLUSH_TIMEOUT;
+       do {
+               writel(flush_bit, &dev->op_regs->endptflush);
+               while (readl(&dev->op_regs->endptflush)) {
+                       if (time_after(jiffies, timeout)) {
+                               ERROR(dev, "ep flush timeout\n");
+                               goto done;
+                       }
+                       cpu_relax();
+               }
+       } while (readl(&dev->op_regs->endptstat) & flush_bit);
+done:
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* endpoints operations structure */
+static const struct usb_ep_ops langwell_ep_ops = {
+
+       /* configure endpoint, making it usable */
+       .enable         = langwell_ep_enable,
+
+       /* endpoint is no longer usable */
+       .disable        = langwell_ep_disable,
+
+       /* allocate a request object to use with this endpoint */
+       .alloc_request  = langwell_alloc_request,
+
+       /* free a request object */
+       .free_request   = langwell_free_request,
+
+       /* queue (submits) an I/O requests to an endpoint */
+       .queue          = langwell_ep_queue,
+
+       /* dequeue (cancels, unlinks) an I/O request from an endpoint */
+       .dequeue        = langwell_ep_dequeue,
+
+       /* set the endpoint halt feature */
+       .set_halt       = langwell_ep_set_halt,
+
+       /* set the halt feature and ignores clear requests */
+       .set_wedge      = langwell_ep_set_wedge,
+
+       /* flush contents of a fifo */
+       .fifo_flush     = langwell_ep_fifo_flush,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* device controller usb_gadget_ops structure */
+
+/* returns the current frame number */
+static int langwell_get_frame(struct usb_gadget *_gadget)
+{
+       struct langwell_udc     *dev;
+       u16                     retval;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       retval = readl(&dev->op_regs->frindex) & FRINDEX_MASK;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/* tries to wake up the host connected to this gadget */
+static int langwell_wakeup(struct usb_gadget *_gadget)
+{
+       struct langwell_udc     *dev;
+       u32                     portsc1, devlc;
+       unsigned long           flags;
+
+       if (!_gadget)
+               return 0;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* Remote Wakeup feature not enabled by host */
+       if (!dev->remote_wakeup)
+               return -ENOTSUPP;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       portsc1 = readl(&dev->op_regs->portsc1);
+       if (!(portsc1 & PORTS_SUSP)) {
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return 0;
+       }
+
+       /* LPM L1 to L0, remote wakeup */
+       if (dev->lpm && dev->lpm_state == LPM_L1) {
+               portsc1 |= PORTS_SLP;
+               writel(portsc1, &dev->op_regs->portsc1);
+       }
+
+       /* force port resume */
+       if (dev->usb_state == USB_STATE_SUSPENDED) {
+               portsc1 |= PORTS_FPR;
+               writel(portsc1, &dev->op_regs->portsc1);
+       }
+
+       /* exit PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc &= ~LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* notify controller that VBUS is powered or not */
+static int langwell_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       u32                     usbcmd;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       VDBG(dev, "VBUS status: %s\n", is_active ? "on" : "off");
+
+       dev->vbus_active = (is_active != 0);
+       if (dev->driver && dev->softconnected && dev->vbus_active) {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd |= CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       } else {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd &= ~CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* constrain controller's VBUS power usage */
+static int langwell_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+       struct langwell_udc     *dev;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->transceiver) {
+               VDBG(dev, "otg_set_power\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               return otg_set_power(dev->transceiver, mA);
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return -ENOTSUPP;
+}
+
+
+/* D+ pullup, software-controlled connect/disconnect to USB host */
+static int langwell_pullup(struct usb_gadget *_gadget, int is_on)
+{
+       struct langwell_udc     *dev;
+       u32                     usbcmd;
+       unsigned long           flags;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       dev->softconnected = (is_on != 0);
+
+       if (dev->driver && dev->softconnected && dev->vbus_active) {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd |= CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       } else {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd &= ~CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* device controller usb_gadget_ops structure */
+static const struct usb_gadget_ops langwell_ops = {
+
+       /* returns the current frame number */
+       .get_frame      = langwell_get_frame,
+
+       /* tries to wake up the host connected to this gadget */
+       .wakeup         = langwell_wakeup,
+
+       /* set the device selfpowered feature, always selfpowered */
+       /* .set_selfpowered = langwell_set_selfpowered, */
+
+       /* notify controller that VBUS is powered or not */
+       .vbus_session   = langwell_vbus_session,
+
+       /* constrain controller's VBUS power usage */
+       .vbus_draw      = langwell_vbus_draw,
+
+       /* D+ pullup, software-controlled connect/disconnect to USB host */
+       .pullup         = langwell_pullup,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* device controller operations */
+
+/* reset device controller */
+static int langwell_udc_reset(struct langwell_udc *dev)
+{
+       u32             usbcmd, usbmode, devlc, endpointlistaddr;
+       unsigned long   timeout;
+
+       if (!dev)
+               return -EINVAL;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* set controller to stop state */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd &= ~CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       /* reset device controller */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd |= CMD_RST;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       /* wait for reset to complete */
+       timeout = jiffies + RESET_TIMEOUT;
+       while (readl(&dev->op_regs->usbcmd) & CMD_RST) {
+               if (time_after(jiffies, timeout)) {
+                       ERROR(dev, "device reset timeout\n");
+                       return -ETIMEDOUT;
+               }
+               cpu_relax();
+       }
+
+       /* set controller to device mode */
+       usbmode = readl(&dev->op_regs->usbmode);
+       usbmode |= MODE_DEVICE;
+
+       /* turn setup lockout off, require setup tripwire in usbcmd */
+       usbmode |= MODE_SLOM;
+
+       writel(usbmode, &dev->op_regs->usbmode);
+       usbmode = readl(&dev->op_regs->usbmode);
+       VDBG(dev, "usbmode=0x%08x\n", usbmode);
+
+       /* Write-Clear setup status */
+       writel(0, &dev->op_regs->usbsts);
+
+       /* if support USB LPM, ACK all LPM token */
+       if (dev->lpm) {
+               devlc = readl(&dev->op_regs->devlc);
+               devlc &= ~LPM_STL;      /* don't STALL LPM token */
+               devlc &= ~LPM_NYT_ACK;  /* ACK LPM token */
+               writel(devlc, &dev->op_regs->devlc);
+       }
+
+       /* fill endpointlistaddr register */
+       endpointlistaddr = dev->ep_dqh_dma;
+       endpointlistaddr &= ENDPOINTLISTADDR_MASK;
+       writel(endpointlistaddr, &dev->op_regs->endpointlistaddr);
+
+       VDBG(dev, "dQH base (vir: %p, phy: 0x%08x), endpointlistaddr=0x%08x\n",
+                       dev->ep_dqh, endpointlistaddr,
+                       readl(&dev->op_regs->endpointlistaddr));
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* reinitialize device controller endpoints */
+static int eps_reinit(struct langwell_udc *dev)
+{
+       struct langwell_ep      *ep;
+       char                    name[14];
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* initialize ep0 */
+       ep = &dev->ep[0];
+       ep->dev = dev;
+       strncpy(ep->name, "ep0", sizeof(ep->name));
+       ep->ep.name = ep->name;
+       ep->ep.ops = &langwell_ep_ops;
+       ep->stopped = 0;
+       ep->ep.maxpacket = EP0_MAX_PKT_SIZE;
+       ep->ep_num = 0;
+       ep->desc = &langwell_ep0_desc;
+       INIT_LIST_HEAD(&ep->queue);
+
+       ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
+
+       /* initialize other endpoints */
+       for (i = 2; i < dev->ep_max; i++) {
+               ep = &dev->ep[i];
+               if (i % 2)
+                       snprintf(name, sizeof(name), "ep%din", i / 2);
+               else
+                       snprintf(name, sizeof(name), "ep%dout", i / 2);
+               ep->dev = dev;
+               strncpy(ep->name, name, sizeof(ep->name));
+               ep->ep.name = ep->name;
+
+               ep->ep.ops = &langwell_ep_ops;
+               ep->stopped = 0;
+               ep->ep.maxpacket = (unsigned short) ~0;
+               ep->ep_num = i / 2;
+
+               INIT_LIST_HEAD(&ep->queue);
+               list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+               ep->dqh = &dev->ep_dqh[i];
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* enable interrupt and set controller to run state */
+static void langwell_udc_start(struct langwell_udc *dev)
+{
+       u32     usbintr, usbcmd;
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* enable interrupts */
+       usbintr = INTR_ULPIE    /* ULPI */
+               | INTR_SLE      /* suspend */
+               /* | INTR_SRE   SOF received */
+               | INTR_URE      /* USB reset */
+               | INTR_AAE      /* async advance */
+               | INTR_SEE      /* system error */
+               | INTR_FRE      /* frame list rollover */
+               | INTR_PCE      /* port change detect */
+               | INTR_UEE      /* USB error interrupt */
+               | INTR_UE;      /* USB interrupt */
+       writel(usbintr, &dev->op_regs->usbintr);
+
+       /* clear stopped bit */
+       dev->stopped = 0;
+
+       /* set controller to run */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd |= CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* disable interrupt and set controller to stop state */
+static void langwell_udc_stop(struct langwell_udc *dev)
+{
+       u32     usbcmd;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* disable all interrupts */
+       writel(0, &dev->op_regs->usbintr);
+
+       /* set stopped bit */
+       dev->stopped = 1;
+
+       /* set controller to stop state */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd &= ~CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* stop all USB activities */
+static void stop_activity(struct langwell_udc *dev,
+               struct usb_gadget_driver *driver)
+{
+       struct langwell_ep      *ep;
+       DBG(dev, "---> %s()\n", __func__);
+
+       nuke(&dev->ep[0], -ESHUTDOWN);
+
+       list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+               nuke(ep, -ESHUTDOWN);
+       }
+
+       /* report disconnect; the driver is already quiesced */
+       if (driver) {
+               spin_unlock(&dev->lock);
+               driver->disconnect(&dev->gadget);
+               spin_lock(&dev->lock);
+       }
+
+       DBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* device "function" sysfs attribute file */
+static ssize_t show_function(struct device *_dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct langwell_udc     *dev = the_controller;
+
+       if (!dev->driver || !dev->driver->function
+                       || strlen(dev->driver->function) > PAGE_SIZE)
+               return 0;
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
+
+
+/* device "langwell_udc" sysfs attribute file */
+static ssize_t show_langwell_udc(struct device *_dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct langwell_udc     *dev = the_controller;
+       struct langwell_request *req;
+       struct langwell_ep      *ep = NULL;
+       char                    *next;
+       unsigned                size;
+       unsigned                t;
+       unsigned                i;
+       unsigned long           flags;
+       u32                     tmp_reg;
+
+       next = buf;
+       size = PAGE_SIZE;
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* driver basic information */
+       t = scnprintf(next, size,
+                       DRIVER_DESC "\n"
+                       "%s version: %s\n"
+                       "Gadget driver: %s\n\n",
+                       driver_name, DRIVER_VERSION,
+                       dev->driver ? dev->driver->driver.name : "(none)");
+       size -= t;
+       next += t;
+
+       /* device registers */
+       tmp_reg = readl(&dev->op_regs->usbcmd);
+       t = scnprintf(next, size,
+                       "USBCMD reg:\n"
+                       "SetupTW: %d\n"
+                       "Run/Stop: %s\n\n",
+                       (tmp_reg & CMD_SUTW) ? 1 : 0,
+                       (tmp_reg & CMD_RUNSTOP) ? "Run" : "Stop");
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->usbsts);
+       t = scnprintf(next, size,
+                       "USB Status Reg:\n"
+                       "Device Suspend: %d\n"
+                       "Reset Received: %d\n"
+                       "System Error: %s\n"
+                       "USB Error Interrupt: %s\n\n",
+                       (tmp_reg & STS_SLI) ? 1 : 0,
+                       (tmp_reg & STS_URI) ? 1 : 0,
+                       (tmp_reg & STS_SEI) ? "Error" : "No error",
+                       (tmp_reg & STS_UEI) ? "Error detected" : "No error");
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->usbintr);
+       t = scnprintf(next, size,
+                       "USB Intrrupt Enable Reg:\n"
+                       "Sleep Enable: %d\n"
+                       "SOF Received Enable: %d\n"
+                       "Reset Enable: %d\n"
+                       "System Error Enable: %d\n"
+                       "Port Change Dectected Enable: %d\n"
+                       "USB Error Intr Enable: %d\n"
+                       "USB Intr Enable: %d\n\n",
+                       (tmp_reg & INTR_SLE) ? 1 : 0,
+                       (tmp_reg & INTR_SRE) ? 1 : 0,
+                       (tmp_reg & INTR_URE) ? 1 : 0,
+                       (tmp_reg & INTR_SEE) ? 1 : 0,
+                       (tmp_reg & INTR_PCE) ? 1 : 0,
+                       (tmp_reg & INTR_UEE) ? 1 : 0,
+                       (tmp_reg & INTR_UE) ? 1 : 0);
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->frindex);
+       t = scnprintf(next, size,
+                       "USB Frame Index Reg:\n"
+                       "Frame Number is 0x%08x\n\n",
+                       (tmp_reg & FRINDEX_MASK));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->deviceaddr);
+       t = scnprintf(next, size,
+                       "USB Device Address Reg:\n"
+                       "Device Addr is 0x%x\n\n",
+                       USBADR(tmp_reg));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->endpointlistaddr);
+       t = scnprintf(next, size,
+                       "USB Endpoint List Address Reg:\n"
+                       "Endpoint List Pointer is 0x%x\n\n",
+                       EPBASE(tmp_reg));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->portsc1);
+       t = scnprintf(next, size,
+               "USB Port Status & Control Reg:\n"
+               "Port Reset: %s\n"
+               "Port Suspend Mode: %s\n"
+               "Over-current Change: %s\n"
+               "Port Enable/Disable Change: %s\n"
+               "Port Enabled/Disabled: %s\n"
+               "Current Connect Status: %s\n\n",
+               (tmp_reg & PORTS_PR) ? "Reset" : "Not Reset",
+               (tmp_reg & PORTS_SUSP) ? "Suspend " : "Not Suspend",
+               (tmp_reg & PORTS_OCC) ? "Detected" : "No",
+               (tmp_reg & PORTS_PEC) ? "Changed" : "Not Changed",
+               (tmp_reg & PORTS_PE) ? "Enable" : "Not Correct",
+               (tmp_reg & PORTS_CCS) ?  "Attached" : "Not Attached");
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->devlc);
+       t = scnprintf(next, size,
+               "Device LPM Control Reg:\n"
+               "Parallel Transceiver : %d\n"
+               "Serial Transceiver : %d\n"
+               "Port Speed: %s\n"
+               "Port Force Full Speed Connenct: %s\n"
+               "PHY Low Power Suspend Clock Disable: %s\n"
+               "BmAttributes: %d\n\n",
+               LPM_PTS(tmp_reg),
+               (tmp_reg & LPM_STS) ? 1 : 0,
+               ({
+                       char    *s;
+                       switch (LPM_PSPD(tmp_reg)) {
+                       case LPM_SPEED_FULL:
+                               s = "Full Speed"; break;
+                       case LPM_SPEED_LOW:
+                               s = "Low Speed"; break;
+                       case LPM_SPEED_HIGH:
+                               s = "High Speed"; break;
+                       default:
+                               s = "Unknown Speed"; break;
+                       }
+                       s;
+               }),
+               (tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force",
+               (tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled",
+               LPM_BA(tmp_reg));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->usbmode);
+       t = scnprintf(next, size,
+                       "USB Mode Reg:\n"
+                       "Controller Mode is : %s\n\n", ({
+                               char *s;
+                               switch (MODE_CM(tmp_reg)) {
+                               case MODE_IDLE:
+                                       s = "Idle"; break;
+                               case MODE_DEVICE:
+                                       s = "Device Controller"; break;
+                               case MODE_HOST:
+                                       s = "Host Controller"; break;
+                               default:
+                                       s = "None"; break;
+                               }
+                               s;
+                       }));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->endptsetupstat);
+       t = scnprintf(next, size,
+                       "Endpoint Setup Status Reg:\n"
+                       "SETUP on ep 0x%04x\n\n",
+                       tmp_reg & SETUPSTAT_MASK);
+       size -= t;
+       next += t;
+
+       for (i = 0; i < dev->ep_max / 2; i++) {
+               tmp_reg = readl(&dev->op_regs->endptctrl[i]);
+               t = scnprintf(next, size, "EP Ctrl Reg [%d]: 0x%08x\n",
+                               i, tmp_reg);
+               size -= t;
+               next += t;
+       }
+       tmp_reg = readl(&dev->op_regs->endptprime);
+       t = scnprintf(next, size, "EP Prime Reg: 0x%08x\n\n", tmp_reg);
+       size -= t;
+       next += t;
+
+       /* langwell_udc, langwell_ep, langwell_request structure information */
+       ep = &dev->ep[0];
+       t = scnprintf(next, size, "%s MaxPacketSize: 0x%x, ep_num: %d\n",
+                       ep->ep.name, ep->ep.maxpacket, ep->ep_num);
+       size -= t;
+       next += t;
+
+       if (list_empty(&ep->queue)) {
+               t = scnprintf(next, size, "its req queue is empty\n\n");
+               size -= t;
+               next += t;
+       } else {
+               list_for_each_entry(req, &ep->queue, queue) {
+                       t = scnprintf(next, size,
+                               "req %p actual 0x%x length 0x%x  buf %p\n",
+                               &req->req, req->req.actual,
+                               req->req.length, req->req.buf);
+                       size -= t;
+                       next += t;
+               }
+       }
+       /* other gadget->eplist ep */
+       list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+               if (ep->desc) {
+                       t = scnprintf(next, size,
+                                       "\n%s MaxPacketSize: 0x%x, "
+                                       "ep_num: %d\n",
+                                       ep->ep.name, ep->ep.maxpacket,
+                                       ep->ep_num);
+                       size -= t;
+                       next += t;
+
+                       if (list_empty(&ep->queue)) {
+                               t = scnprintf(next, size,
+                                               "its req queue is empty\n\n");
+                               size -= t;
+                               next += t;
+                       } else {
+                               list_for_each_entry(req, &ep->queue, queue) {
+                                       t = scnprintf(next, size,
+                                               "req %p actual 0x%x length "
+                                               "0x%x  buf %p\n",
+                                               &req->req, req->req.actual,
+                                               req->req.length, req->req.buf);
+                                       size -= t;
+                                       next += t;
+                               }
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL);
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests.  then usb traffic follows until a
+ * disconnect is reported.  then a host may connect again, or
+ * the driver might get unbound.
+ */
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+       struct langwell_udc     *dev = the_controller;
+       unsigned long           flags;
+       int                     retval;
+
+       if (!dev)
+               return -ENODEV;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       if (dev->driver)
+               return -EBUSY;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* hook up the driver ... */
+       driver->driver.bus = NULL;
+       dev->driver = driver;
+       dev->gadget.dev.driver = &driver->driver;
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       retval = driver->bind(&dev->gadget);
+       if (retval) {
+               DBG(dev, "bind to driver %s --> %d\n",
+                               driver->driver.name, retval);
+               dev->driver = NULL;
+               dev->gadget.dev.driver = NULL;
+               return retval;
+       }
+
+       retval = device_create_file(&dev->pdev->dev, &dev_attr_function);
+       if (retval)
+               goto err_unbind;
+
+       dev->usb_state = USB_STATE_ATTACHED;
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->ep0_dir = USB_DIR_OUT;
+
+       /* enable interrupt and set controller to run state */
+       if (dev->got_irq)
+               langwell_udc_start(dev);
+
+       VDBG(dev, "After langwell_udc_start(), print all registers:\n");
+#ifdef VERBOSE
+       print_all_registers(dev);
+#endif
+
+       INFO(dev, "register driver: %s\n", driver->driver.name);
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+
+err_unbind:
+       driver->unbind(&dev->gadget);
+       dev->gadget.dev.driver = NULL;
+       dev->driver = NULL;
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+
+/* unregister gadget driver */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+       struct langwell_udc     *dev = the_controller;
+       unsigned long           flags;
+
+       if (!dev)
+               return -ENODEV;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       if (unlikely(!driver || !driver->bind || !driver->unbind))
+               return -EINVAL;
+
+       /* unbind OTG transceiver */
+       if (dev->transceiver)
+               (void)otg_set_peripheral(dev->transceiver, 0);
+
+       /* disable interrupt and set controller to stop state */
+       langwell_udc_stop(dev);
+
+       dev->usb_state = USB_STATE_ATTACHED;
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->ep0_dir = USB_DIR_OUT;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* stop all usb activities */
+       dev->gadget.speed = USB_SPEED_UNKNOWN;
+       stop_activity(dev, driver);
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       /* unbind gadget driver */
+       driver->unbind(&dev->gadget);
+       dev->gadget.dev.driver = NULL;
+       dev->driver = NULL;
+
+       device_remove_file(&dev->pdev->dev, &dev_attr_function);
+
+       INFO(dev, "unregistered driver '%s'\n", driver->driver.name);
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * setup tripwire is used as a semaphore to ensure that the setup data
+ * payload is extracted from a dQH without being corrupted
+ */
+static void setup_tripwire(struct langwell_udc *dev)
+{
+       u32                     usbcmd,
+                               endptsetupstat;
+       unsigned long           timeout;
+       struct langwell_dqh     *dqh;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* ep0 OUT dQH */
+       dqh = &dev->ep_dqh[EP_DIR_OUT];
+
+       /* Write-Clear endptsetupstat */
+       endptsetupstat = readl(&dev->op_regs->endptsetupstat);
+       writel(endptsetupstat, &dev->op_regs->endptsetupstat);
+
+       /* wait until endptsetupstat is cleared */
+       timeout = jiffies + SETUPSTAT_TIMEOUT;
+       while (readl(&dev->op_regs->endptsetupstat)) {
+               if (time_after(jiffies, timeout)) {
+                       ERROR(dev, "setup_tripwire timeout\n");
+                       break;
+               }
+               cpu_relax();
+       }
+
+       /* while a hazard exists when setup packet arrives */
+       do {
+               /* set setup tripwire bit */
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               writel(usbcmd | CMD_SUTW, &dev->op_regs->usbcmd);
+
+               /* copy the setup packet to local buffer */
+               memcpy(&dev->local_setup_buff, &dqh->dqh_setup, 8);
+       } while (!(readl(&dev->op_regs->usbcmd) & CMD_SUTW));
+
+       /* Write-Clear setup tripwire bit */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       writel(usbcmd & ~CMD_SUTW, &dev->op_regs->usbcmd);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* protocol ep0 stall, will automatically be cleared on new transaction */
+static void ep0_stall(struct langwell_udc *dev)
+{
+       u32     endptctrl;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* set TX and RX to stall */
+       endptctrl = readl(&dev->op_regs->endptctrl[0]);
+       endptctrl |= EPCTRL_TXS | EPCTRL_RXS;
+       writel(endptctrl, &dev->op_regs->endptctrl[0]);
+
+       /* update ep0 state */
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->ep0_dir = USB_DIR_OUT;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* PRIME a status phase for ep0 */
+static int prime_status_phase(struct langwell_udc *dev, int dir)
+{
+       struct langwell_request *req;
+       struct langwell_ep      *ep;
+       int                     status = 0;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dir == EP_DIR_IN)
+               dev->ep0_dir = USB_DIR_IN;
+       else
+               dev->ep0_dir = USB_DIR_OUT;
+
+       ep = &dev->ep[0];
+       dev->ep0_state = WAIT_FOR_OUT_STATUS;
+
+       req = dev->status_req;
+
+       req->ep = ep;
+       req->req.length = 0;
+       req->req.status = -EINPROGRESS;
+       req->req.actual = 0;
+       req->req.complete = NULL;
+       req->dtd_count = 0;
+
+       if (!req_to_dtd(req))
+               status = queue_dtd(ep, req);
+       else
+               return -ENOMEM;
+
+       if (status)
+               ERROR(dev, "can't queue ep0 status request\n");
+
+       list_add_tail(&req->queue, &ep->queue);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return status;
+}
+
+
+/* SET_ADDRESS request routine */
+static void set_address(struct langwell_udc *dev, u16 value,
+               u16 index, u16 length)
+{
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* save the new address to device struct */
+       dev->dev_addr = (u8) value;
+       VDBG(dev, "dev->dev_addr = %d\n", dev->dev_addr);
+
+       /* update usb state */
+       dev->usb_state = USB_STATE_ADDRESS;
+
+       /* STATUS phase */
+       if (prime_status_phase(dev, EP_DIR_IN))
+               ep0_stall(dev);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* return endpoint by windex */
+static struct langwell_ep *get_ep_by_windex(struct langwell_udc *dev,
+               u16 wIndex)
+{
+       struct langwell_ep              *ep;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+               return &dev->ep[0];
+
+       list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+               u8      bEndpointAddress;
+               if (!ep->desc)
+                       continue;
+
+               bEndpointAddress = ep->desc->bEndpointAddress;
+               if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+                       continue;
+
+               if ((wIndex & USB_ENDPOINT_NUMBER_MASK)
+                       == (bEndpointAddress & USB_ENDPOINT_NUMBER_MASK))
+                       return ep;
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return NULL;
+}
+
+
+/* return whether endpoint is stalled, 0: not stalled; 1: stalled */
+static int ep_is_stall(struct langwell_ep *ep)
+{
+       struct langwell_udc     *dev = ep->dev;
+       u32                     endptctrl;
+       int                     retval;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       endptctrl = readl(&dev->op_regs->endptctrl[ep->ep_num]);
+       if (is_in(ep))
+               retval = endptctrl & EPCTRL_TXS ? 1 : 0;
+       else
+               retval = endptctrl & EPCTRL_RXS ? 1 : 0;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/* GET_STATUS request routine */
+static void get_status(struct langwell_udc *dev, u8 request_type, u16 value,
+               u16 index, u16 length)
+{
+       struct langwell_request *req;
+       struct langwell_ep      *ep;
+       u16     status_data = 0;        /* 16 bits cpu view status data */
+       int     status = 0;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       ep = &dev->ep[0];
+
+       if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+               /* get device status */
+               status_data = 1 << USB_DEVICE_SELF_POWERED;
+               status_data |= dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+       } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+               /* get interface status */
+               status_data = 0;
+       } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+               /* get endpoint status */
+               struct langwell_ep      *epn;
+               epn = get_ep_by_windex(dev, index);
+               /* stall if endpoint doesn't exist */
+               if (!epn)
+                       goto stall;
+
+               status_data = ep_is_stall(epn) << USB_ENDPOINT_HALT;
+       }
+
+       dev->ep0_dir = USB_DIR_IN;
+
+       /* borrow the per device status_req */
+       req = dev->status_req;
+
+       /* fill in the reqest structure */
+       *((u16 *) req->req.buf) = cpu_to_le16(status_data);
+       req->ep = ep;
+       req->req.length = 2;
+       req->req.status = -EINPROGRESS;
+       req->req.actual = 0;
+       req->req.complete = NULL;
+       req->dtd_count = 0;
+
+       /* prime the data phase */
+       if (!req_to_dtd(req))
+               status = queue_dtd(ep, req);
+       else                    /* no mem */
+               goto stall;
+
+       if (status) {
+               ERROR(dev, "response error on GET_STATUS request\n");
+               goto stall;
+       }
+
+       list_add_tail(&req->queue, &ep->queue);
+       dev->ep0_state = DATA_STATE_XMIT;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+stall:
+       ep0_stall(dev);
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* setup packet interrupt handler */
+static void handle_setup_packet(struct langwell_udc *dev,
+               struct usb_ctrlrequest *setup)
+{
+       u16     wValue = le16_to_cpu(setup->wValue);
+       u16     wIndex = le16_to_cpu(setup->wIndex);
+       u16     wLength = le16_to_cpu(setup->wLength);
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* ep0 fifo flush */
+       nuke(&dev->ep[0], -ESHUTDOWN);
+
+       DBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+                       setup->bRequestType, setup->bRequest,
+                       wValue, wIndex, wLength);
+
+       /* RNDIS gadget delegate */
+       if ((setup->bRequestType == 0x21) && (setup->bRequest == 0x00)) {
+               /* USB_CDC_SEND_ENCAPSULATED_COMMAND */
+               goto delegate;
+       }
+
+       /* USB_CDC_GET_ENCAPSULATED_RESPONSE */
+       if ((setup->bRequestType == 0xa1) && (setup->bRequest == 0x01)) {
+               /* USB_CDC_GET_ENCAPSULATED_RESPONSE */
+               goto delegate;
+       }
+
+       /* We process some stardard setup requests here */
+       switch (setup->bRequest) {
+       case USB_REQ_GET_STATUS:
+               DBG(dev, "SETUP: USB_REQ_GET_STATUS\n");
+               /* get status, DATA and STATUS phase */
+               if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+                                       != (USB_DIR_IN | USB_TYPE_STANDARD))
+                       break;
+               get_status(dev, setup->bRequestType, wValue, wIndex, wLength);
+               goto end;
+
+       case USB_REQ_SET_ADDRESS:
+               DBG(dev, "SETUP: USB_REQ_SET_ADDRESS\n");
+               /* STATUS phase */
+               if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
+                                               | USB_RECIP_DEVICE))
+                       break;
+               set_address(dev, wValue, wIndex, wLength);
+               goto end;
+
+       case USB_REQ_CLEAR_FEATURE:
+       case USB_REQ_SET_FEATURE:
+               /* STATUS phase */
+       {
+               int rc = -EOPNOTSUPP;
+               if (setup->bRequest == USB_REQ_SET_FEATURE)
+                       DBG(dev, "SETUP: USB_REQ_SET_FEATURE\n");
+               else if (setup->bRequest == USB_REQ_CLEAR_FEATURE)
+                       DBG(dev, "SETUP: USB_REQ_CLEAR_FEATURE\n");
+
+               if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
+                               == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
+                       struct langwell_ep      *epn;
+                       epn = get_ep_by_windex(dev, wIndex);
+                       /* stall if endpoint doesn't exist */
+                       if (!epn) {
+                               ep0_stall(dev);
+                               goto end;
+                       }
+
+                       if (wValue != 0 || wLength != 0
+                                       || epn->ep_num > dev->ep_max)
+                               break;
+
+                       spin_unlock(&dev->lock);
+                       rc = langwell_ep_set_halt(&epn->ep,
+                                       (setup->bRequest == USB_REQ_SET_FEATURE)
+                                               ? 1 : 0);
+                       spin_lock(&dev->lock);
+
+               } else if ((setup->bRequestType & (USB_RECIP_MASK
+                               | USB_TYPE_MASK)) == (USB_RECIP_DEVICE
+                               | USB_TYPE_STANDARD)) {
+                       if (!gadget_is_otg(&dev->gadget))
+                               break;
+                       else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) {
+                               dev->gadget.b_hnp_enable = 1;
+#ifdef OTG_TRANSCEIVER
+                               if (!dev->lotg->otg.default_a)
+                                       dev->lotg->hsm.b_hnp_enable = 1;
+#endif
+                       } else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
+                               dev->gadget.a_hnp_support = 1;
+                       else if (setup->bRequest ==
+                                       USB_DEVICE_A_ALT_HNP_SUPPORT)
+                               dev->gadget.a_alt_hnp_support = 1;
+                       else
+                               break;
+                       rc = 0;
+               } else
+                       break;
+
+               if (rc == 0) {
+                       if (prime_status_phase(dev, EP_DIR_IN))
+                               ep0_stall(dev);
+               }
+               goto end;
+       }
+
+       case USB_REQ_GET_DESCRIPTOR:
+               DBG(dev, "SETUP: USB_REQ_GET_DESCRIPTOR\n");
+               goto delegate;
+
+       case USB_REQ_SET_DESCRIPTOR:
+               DBG(dev, "SETUP: USB_REQ_SET_DESCRIPTOR unsupported\n");
+               goto delegate;
+
+       case USB_REQ_GET_CONFIGURATION:
+               DBG(dev, "SETUP: USB_REQ_GET_CONFIGURATION\n");
+               goto delegate;
+
+       case USB_REQ_SET_CONFIGURATION:
+               DBG(dev, "SETUP: USB_REQ_SET_CONFIGURATION\n");
+               goto delegate;
+
+       case USB_REQ_GET_INTERFACE:
+               DBG(dev, "SETUP: USB_REQ_GET_INTERFACE\n");
+               goto delegate;
+
+       case USB_REQ_SET_INTERFACE:
+               DBG(dev, "SETUP: USB_REQ_SET_INTERFACE\n");
+               goto delegate;
+
+       case USB_REQ_SYNCH_FRAME:
+               DBG(dev, "SETUP: USB_REQ_SYNCH_FRAME unsupported\n");
+               goto delegate;
+
+       default:
+               /* delegate USB standard requests to the gadget driver */
+               goto delegate;
+delegate:
+               /* USB requests handled by gadget */
+               if (wLength) {
+                       /* DATA phase from gadget, STATUS phase from udc */
+                       dev->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+                                       ?  USB_DIR_IN : USB_DIR_OUT;
+                       VDBG(dev, "dev->ep0_dir = 0x%x, wLength = %d\n",
+                                       dev->ep0_dir, wLength);
+                       spin_unlock(&dev->lock);
+                       if (dev->driver->setup(&dev->gadget,
+                                       &dev->local_setup_buff) < 0)
+                               ep0_stall(dev);
+                       spin_lock(&dev->lock);
+                       dev->ep0_state = (setup->bRequestType & USB_DIR_IN)
+                                       ?  DATA_STATE_XMIT : DATA_STATE_RECV;
+               } else {
+                       /* no DATA phase, IN STATUS phase from gadget */
+                       dev->ep0_dir = USB_DIR_IN;
+                       VDBG(dev, "dev->ep0_dir = 0x%x, wLength = %d\n",
+                                       dev->ep0_dir, wLength);
+                       spin_unlock(&dev->lock);
+                       if (dev->driver->setup(&dev->gadget,
+                                       &dev->local_setup_buff) < 0)
+                               ep0_stall(dev);
+                       spin_lock(&dev->lock);
+                       dev->ep0_state = WAIT_FOR_OUT_STATUS;
+               }
+               break;
+       }
+end:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* transfer completion, process endpoint request and free the completed dTDs
+ * for this request
+ */
+static int process_ep_req(struct langwell_udc *dev, int index,
+               struct langwell_request *curr_req)
+{
+       struct langwell_dtd     *curr_dtd;
+       struct langwell_dqh     *curr_dqh;
+       int                     td_complete, actual, remaining_length;
+       int                     i, dir;
+       u8                      dtd_status = 0;
+       int                     retval = 0;
+
+       curr_dqh = &dev->ep_dqh[index];
+       dir = index % 2;
+
+       curr_dtd = curr_req->head;
+       td_complete = 0;
+       actual = curr_req->req.length;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       for (i = 0; i < curr_req->dtd_count; i++) {
+               remaining_length = le16_to_cpu(curr_dtd->dtd_total);
+               actual -= remaining_length;
+
+               /* command execution states by dTD */
+               dtd_status = curr_dtd->dtd_status;
+
+               if (!dtd_status) {
+                       /* transfers completed successfully */
+                       if (!remaining_length) {
+                               td_complete++;
+                               VDBG(dev, "dTD transmitted successfully\n");
+                       } else {
+                               if (dir) {
+                                       VDBG(dev, "TX dTD remains data\n");
+                                       retval = -EPROTO;
+                                       break;
+
+                               } else {
+                                       td_complete++;
+                                       break;
+                               }
+                       }
+               } else {
+                       /* transfers completed with errors */
+                       if (dtd_status & DTD_STS_ACTIVE) {
+                               DBG(dev, "request not completed\n");
+                               retval = 1;
+                               return retval;
+                       } else if (dtd_status & DTD_STS_HALTED) {
+                               ERROR(dev, "dTD error %08x dQH[%d]\n",
+                                               dtd_status, index);
+                               /* clear the errors and halt condition */
+                               curr_dqh->dtd_status = 0;
+                               retval = -EPIPE;
+                               break;
+                       } else if (dtd_status & DTD_STS_DBE) {
+                               DBG(dev, "data buffer (overflow) error\n");
+                               retval = -EPROTO;
+                               break;
+                       } else if (dtd_status & DTD_STS_TRE) {
+                               DBG(dev, "transaction(ISO) error\n");
+                               retval = -EILSEQ;
+                               break;
+                       } else
+                               ERROR(dev, "unknown error (0x%x)!\n",
+                                               dtd_status);
+               }
+
+               if (i != curr_req->dtd_count - 1)
+                       curr_dtd = (struct langwell_dtd *)
+                               curr_dtd->next_dtd_virt;
+       }
+
+       if (retval)
+               return retval;
+
+       curr_req->req.actual = actual;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* complete DATA or STATUS phase of ep0 prime status phase if needed */
+static void ep0_req_complete(struct langwell_udc *dev,
+               struct langwell_ep *ep0, struct langwell_request *req)
+{
+       u32     new_addr;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->usb_state == USB_STATE_ADDRESS) {
+               /* set the new address */
+               new_addr = (u32)dev->dev_addr;
+               writel(new_addr << USBADR_SHIFT, &dev->op_regs->deviceaddr);
+
+               new_addr = USBADR(readl(&dev->op_regs->deviceaddr));
+               VDBG(dev, "new_addr = %d\n", new_addr);
+       }
+
+       done(ep0, req, 0);
+
+       switch (dev->ep0_state) {
+       case DATA_STATE_XMIT:
+               /* receive status phase */
+               if (prime_status_phase(dev, EP_DIR_OUT))
+                       ep0_stall(dev);
+               break;
+       case DATA_STATE_RECV:
+               /* send status phase */
+               if (prime_status_phase(dev, EP_DIR_IN))
+                       ep0_stall(dev);
+               break;
+       case WAIT_FOR_OUT_STATUS:
+               dev->ep0_state = WAIT_FOR_SETUP;
+               break;
+       case WAIT_FOR_SETUP:
+               ERROR(dev, "unexpect ep0 packets\n");
+               break;
+       default:
+               ep0_stall(dev);
+               break;
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* USB transfer completion interrupt */
+static void handle_trans_complete(struct langwell_udc *dev)
+{
+       u32                     complete_bits;
+       int                     i, ep_num, dir, bit_mask, status;
+       struct langwell_ep      *epn;
+       struct langwell_request *curr_req, *temp_req;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       complete_bits = readl(&dev->op_regs->endptcomplete);
+       VDBG(dev, "endptcomplete register: 0x%08x\n", complete_bits);
+
+       /* Write-Clear the bits in endptcomplete register */
+       writel(complete_bits, &dev->op_regs->endptcomplete);
+
+       if (!complete_bits) {
+               DBG(dev, "complete_bits = 0\n");
+               goto done;
+       }
+
+       for (i = 0; i < dev->ep_max; i++) {
+               ep_num = i / 2;
+               dir = i % 2;
+
+               bit_mask = 1 << (ep_num + 16 * dir);
+
+               if (!(complete_bits & bit_mask))
+                       continue;
+
+               /* ep0 */
+               if (i == 1)
+                       epn = &dev->ep[0];
+               else
+                       epn = &dev->ep[i];
+
+               if (epn->name == NULL) {
+                       WARNING(dev, "invalid endpoint\n");
+                       continue;
+               }
+
+               if (i < 2)
+                       /* ep0 in and out */
+                       DBG(dev, "%s-%s transfer completed\n",
+                                       epn->name,
+                                       is_in(epn) ? "in" : "out");
+               else
+                       DBG(dev, "%s transfer completed\n", epn->name);
+
+               /* process the req queue until an uncomplete request */
+               list_for_each_entry_safe(curr_req, temp_req,
+                               &epn->queue, queue) {
+                       status = process_ep_req(dev, i, curr_req);
+                       VDBG(dev, "%s req status: %d\n", epn->name, status);
+
+                       if (status)
+                               break;
+
+                       /* write back status to req */
+                       curr_req->req.status = status;
+
+                       /* ep0 request completion */
+                       if (ep_num == 0) {
+                               ep0_req_complete(dev, epn, curr_req);
+                               break;
+                       } else {
+                               done(epn, curr_req, status);
+                       }
+               }
+       }
+done:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* port change detect interrupt handler */
+static void handle_port_change(struct langwell_udc *dev)
+{
+       u32     portsc1, devlc;
+       u32     speed;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->bus_reset)
+               dev->bus_reset = 0;
+
+       portsc1 = readl(&dev->op_regs->portsc1);
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "portsc1 = 0x%08x, devlc = 0x%08x\n",
+                       portsc1, devlc);
+
+       /* bus reset is finished */
+       if (!(portsc1 & PORTS_PR)) {
+               /* get the speed */
+               speed = LPM_PSPD(devlc);
+               switch (speed) {
+               case LPM_SPEED_HIGH:
+                       dev->gadget.speed = USB_SPEED_HIGH;
+                       break;
+               case LPM_SPEED_FULL:
+                       dev->gadget.speed = USB_SPEED_FULL;
+                       break;
+               case LPM_SPEED_LOW:
+                       dev->gadget.speed = USB_SPEED_LOW;
+                       break;
+               default:
+                       dev->gadget.speed = USB_SPEED_UNKNOWN;
+                       break;
+               }
+               VDBG(dev, "speed = %d, dev->gadget.speed = %d\n",
+                               speed, dev->gadget.speed);
+       }
+
+       /* LPM L0 to L1 */
+       if (dev->lpm && dev->lpm_state == LPM_L0)
+               if (portsc1 & PORTS_SUSP && portsc1 & PORTS_SLP) {
+                               INFO(dev, "LPM L0 to L1\n");
+                               dev->lpm_state = LPM_L1;
+               }
+
+       /* LPM L1 to L0, force resume or remote wakeup finished */
+       if (dev->lpm && dev->lpm_state == LPM_L1)
+               if (!(portsc1 & PORTS_SUSP)) {
+                       if (portsc1 & PORTS_SLP)
+                               INFO(dev, "LPM L1 to L0, force resume\n");
+                       else
+                               INFO(dev, "LPM L1 to L0, remote wakeup\n");
+
+                       dev->lpm_state = LPM_L0;
+               }
+
+       /* update USB state */
+       if (!dev->resume_state)
+               dev->usb_state = USB_STATE_DEFAULT;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* USB reset interrupt handler */
+static void handle_usb_reset(struct langwell_udc *dev)
+{
+       u32             deviceaddr,
+                       endptsetupstat,
+                       endptcomplete;
+       unsigned long   timeout;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* Write-Clear the device address */
+       deviceaddr = readl(&dev->op_regs->deviceaddr);
+       writel(deviceaddr & ~USBADR_MASK, &dev->op_regs->deviceaddr);
+
+       dev->dev_addr = 0;
+
+       /* clear usb state */
+       dev->resume_state = 0;
+
+       /* LPM L1 to L0, reset */
+       if (dev->lpm)
+               dev->lpm_state = LPM_L0;
+
+       dev->ep0_dir = USB_DIR_OUT;
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->remote_wakeup = 0;         /* default to 0 on reset */
+       dev->gadget.b_hnp_enable = 0;
+       dev->gadget.a_hnp_support = 0;
+       dev->gadget.a_alt_hnp_support = 0;
+
+       /* Write-Clear all the setup token semaphores */
+       endptsetupstat = readl(&dev->op_regs->endptsetupstat);
+       writel(endptsetupstat, &dev->op_regs->endptsetupstat);
+
+       /* Write-Clear all the endpoint complete status bits */
+       endptcomplete = readl(&dev->op_regs->endptcomplete);
+       writel(endptcomplete, &dev->op_regs->endptcomplete);
+
+       /* wait until all endptprime bits cleared */
+       timeout = jiffies + PRIME_TIMEOUT;
+       while (readl(&dev->op_regs->endptprime)) {
+               if (time_after(jiffies, timeout)) {
+                       ERROR(dev, "USB reset timeout\n");
+                       break;
+               }
+               cpu_relax();
+       }
+
+       /* write 1s to endptflush register to clear any primed buffers */
+       writel((u32) ~0, &dev->op_regs->endptflush);
+
+       if (readl(&dev->op_regs->portsc1) & PORTS_PR) {
+               VDBG(dev, "USB bus reset\n");
+               /* bus is reseting */
+               dev->bus_reset = 1;
+
+               /* reset all the queues, stop all USB activities */
+               stop_activity(dev, dev->driver);
+               dev->usb_state = USB_STATE_DEFAULT;
+       } else {
+               VDBG(dev, "device controller reset\n");
+               /* controller reset */
+               langwell_udc_reset(dev);
+
+               /* reset all the queues, stop all USB activities */
+               stop_activity(dev, dev->driver);
+
+               /* reset ep0 dQH and endptctrl */
+               ep0_reset(dev);
+
+               /* enable interrupt and set controller to run state */
+               langwell_udc_start(dev);
+
+               dev->usb_state = USB_STATE_ATTACHED;
+       }
+
+#ifdef OTG_TRANSCEIVER
+       /* refer to USB OTG 6.6.2.3 b_hnp_en is cleared */
+       if (!dev->lotg->otg.default_a)
+               dev->lotg->hsm.b_hnp_enable = 0;
+#endif
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* USB bus suspend/resume interrupt */
+static void handle_bus_suspend(struct langwell_udc *dev)
+{
+       u32             devlc;
+       DBG(dev, "---> %s()\n", __func__);
+
+       dev->resume_state = dev->usb_state;
+       dev->usb_state = USB_STATE_SUSPENDED;
+
+#ifdef OTG_TRANSCEIVER
+       if (dev->lotg->otg.default_a) {
+               if (dev->lotg->hsm.b_bus_suspend_vld == 1) {
+                       dev->lotg->hsm.b_bus_suspend = 1;
+                       /* notify transceiver the state changes */
+                       if (spin_trylock(&dev->lotg->wq_lock)) {
+                               langwell_update_transceiver();
+                               spin_unlock(&dev->lotg->wq_lock);
+                       }
+               }
+               dev->lotg->hsm.b_bus_suspend_vld++;
+       } else {
+               if (!dev->lotg->hsm.a_bus_suspend) {
+                       dev->lotg->hsm.a_bus_suspend = 1;
+                       /* notify transceiver the state changes */
+                       if (spin_trylock(&dev->lotg->wq_lock)) {
+                               langwell_update_transceiver();
+                               spin_unlock(&dev->lotg->wq_lock);
+                       }
+               }
+       }
+#endif
+
+       /* report suspend to the driver */
+       if (dev->driver) {
+               if (dev->driver->suspend) {
+                       spin_unlock(&dev->lock);
+                       dev->driver->suspend(&dev->gadget);
+                       spin_lock(&dev->lock);
+                       DBG(dev, "suspend %s\n", dev->driver->driver.name);
+               }
+       }
+
+       /* enter PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc |= LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       DBG(dev, "<--- %s()\n", __func__);
+}
+
+
+static void handle_bus_resume(struct langwell_udc *dev)
+{
+       u32             devlc;
+       DBG(dev, "---> %s()\n", __func__);
+
+       dev->usb_state = dev->resume_state;
+       dev->resume_state = 0;
+
+       /* exit PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc &= ~LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+#ifdef OTG_TRANSCEIVER
+       if (dev->lotg->otg.default_a == 0)
+               dev->lotg->hsm.a_bus_suspend = 0;
+#endif
+
+       /* report resume to the driver */
+       if (dev->driver) {
+               if (dev->driver->resume) {
+                       spin_unlock(&dev->lock);
+                       dev->driver->resume(&dev->gadget);
+                       spin_lock(&dev->lock);
+                       DBG(dev, "resume %s\n", dev->driver->driver.name);
+               }
+       }
+
+       DBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* USB device controller interrupt handler */
+static irqreturn_t langwell_irq(int irq, void *_dev)
+{
+       struct langwell_udc     *dev = _dev;
+       u32                     usbsts,
+                               usbintr,
+                               irq_sts,
+                               portsc1;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->stopped) {
+               VDBG(dev, "handle IRQ_NONE\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               return IRQ_NONE;
+       }
+
+       spin_lock(&dev->lock);
+
+       /* USB status */
+       usbsts = readl(&dev->op_regs->usbsts);
+
+       /* USB interrupt enable */
+       usbintr = readl(&dev->op_regs->usbintr);
+
+       irq_sts = usbsts & usbintr;
+       VDBG(dev, "usbsts = 0x%08x, usbintr = 0x%08x, irq_sts = 0x%08x\n",
+                       usbsts, usbintr, irq_sts);
+
+       if (!irq_sts) {
+               VDBG(dev, "handle IRQ_NONE\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               spin_unlock(&dev->lock);
+               return IRQ_NONE;
+       }
+
+       /* Write-Clear interrupt status bits */
+       writel(irq_sts, &dev->op_regs->usbsts);
+
+       /* resume from suspend */
+       portsc1 = readl(&dev->op_regs->portsc1);
+       if (dev->usb_state == USB_STATE_SUSPENDED)
+               if (!(portsc1 & PORTS_SUSP))
+                       handle_bus_resume(dev);
+
+       /* USB interrupt */
+       if (irq_sts & STS_UI) {
+               VDBG(dev, "USB interrupt\n");
+
+               /* setup packet received from ep0 */
+               if (readl(&dev->op_regs->endptsetupstat)
+                               & EP0SETUPSTAT_MASK) {
+                       VDBG(dev, "USB SETUP packet received interrupt\n");
+                       /* setup tripwire semaphone */
+                       setup_tripwire(dev);
+                       handle_setup_packet(dev, &dev->local_setup_buff);
+               }
+
+               /* USB transfer completion */
+               if (readl(&dev->op_regs->endptcomplete)) {
+                       VDBG(dev, "USB transfer completion interrupt\n");
+                       handle_trans_complete(dev);
+               }
+       }
+
+       /* SOF received interrupt (for ISO transfer) */
+       if (irq_sts & STS_SRI) {
+               /* FIXME */
+               /* VDBG(dev, "SOF received interrupt\n"); */
+       }
+
+       /* port change detect interrupt */
+       if (irq_sts & STS_PCI) {
+               VDBG(dev, "port change detect interrupt\n");
+               handle_port_change(dev);
+       }
+
+       /* suspend interrrupt */
+       if (irq_sts & STS_SLI) {
+               VDBG(dev, "suspend interrupt\n");
+               handle_bus_suspend(dev);
+       }
+
+       /* USB reset interrupt */
+       if (irq_sts & STS_URI) {
+               VDBG(dev, "USB reset interrupt\n");
+               handle_usb_reset(dev);
+       }
+
+       /* USB error or system error interrupt */
+       if (irq_sts & (STS_UEI | STS_SEI)) {
+               /* FIXME */
+               WARNING(dev, "error IRQ, irq_sts: %x\n", irq_sts);
+       }
+
+       spin_unlock(&dev->lock);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return IRQ_HANDLED;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* release device structure */
+static void gadget_release(struct device *_dev)
+{
+       struct langwell_udc     *dev = the_controller;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       complete(dev->done);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       kfree(dev);
+}
+
+
+/* tear down the binding between this driver and the pci device */
+static void langwell_udc_remove(struct pci_dev *pdev)
+{
+       struct langwell_udc     *dev = the_controller;
+
+       DECLARE_COMPLETION(done);
+
+       BUG_ON(dev->driver);
+       DBG(dev, "---> %s()\n", __func__);
+
+       dev->done = &done;
+
+       /* free memory allocated in probe */
+       if (dev->dtd_pool)
+               dma_pool_destroy(dev->dtd_pool);
+
+       if (dev->status_req) {
+               kfree(dev->status_req->req.buf);
+               kfree(dev->status_req);
+       }
+
+       if (dev->ep_dqh)
+               dma_free_coherent(&pdev->dev, dev->ep_dqh_size,
+                       dev->ep_dqh, dev->ep_dqh_dma);
+
+       kfree(dev->ep);
+
+       /* diable IRQ handler */
+       if (dev->got_irq)
+               free_irq(pdev->irq, dev);
+
+#ifndef        OTG_TRANSCEIVER
+       if (dev->cap_regs)
+               iounmap(dev->cap_regs);
+
+       if (dev->region)
+               release_mem_region(pci_resource_start(pdev, 0),
+                               pci_resource_len(pdev, 0));
+
+       if (dev->enabled)
+               pci_disable_device(pdev);
+#else
+       if (dev->transceiver) {
+               otg_put_transceiver(dev->transceiver);
+               dev->transceiver = NULL;
+               dev->lotg = NULL;
+       }
+#endif
+
+       dev->cap_regs = NULL;
+
+       INFO(dev, "unbind\n");
+       DBG(dev, "<--- %s()\n", __func__);
+
+       device_unregister(&dev->gadget.dev);
+       device_remove_file(&pdev->dev, &dev_attr_langwell_udc);
+
+#ifndef        OTG_TRANSCEIVER
+       pci_set_drvdata(pdev, NULL);
+#endif
+
+       /* free dev, wait for the release() finished */
+       wait_for_completion(&done);
+
+       the_controller = NULL;
+}
+
+
+/*
+ * wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+static int langwell_udc_probe(struct pci_dev *pdev,
+               const struct pci_device_id *id)
+{
+       struct langwell_udc     *dev;
+#ifndef        OTG_TRANSCEIVER
+       unsigned long           resource, len;
+#endif
+       void                    __iomem *base = NULL;
+       size_t                  size;
+       int                     retval;
+
+       if (the_controller) {
+               dev_warn(&pdev->dev, "ignoring\n");
+               return -EBUSY;
+       }
+
+       /* alloc, and start init */
+       dev = kzalloc(sizeof *dev, GFP_KERNEL);
+       if (dev == NULL) {
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       /* initialize device spinlock */
+       spin_lock_init(&dev->lock);
+
+       dev->pdev = pdev;
+       DBG(dev, "---> %s()\n", __func__);
+
+#ifdef OTG_TRANSCEIVER
+       /* PCI device is already enabled by otg_transceiver driver */
+       dev->enabled = 1;
+
+       /* mem region and register base */
+       dev->region = 1;
+       dev->transceiver = otg_get_transceiver();
+       dev->lotg = otg_to_langwell(dev->transceiver);
+       base = dev->lotg->regs;
+#else
+       pci_set_drvdata(pdev, dev);
+
+       /* now all the pci goodies ... */
+       if (pci_enable_device(pdev) < 0) {
+               retval = -ENODEV;
+               goto error;
+       }
+       dev->enabled = 1;
+
+       /* control register: BAR 0 */
+       resource = pci_resource_start(pdev, 0);
+       len = pci_resource_len(pdev, 0);
+       if (!request_mem_region(resource, len, driver_name)) {
+               ERROR(dev, "controller already in use\n");
+               retval = -EBUSY;
+               goto error;
+       }
+       dev->region = 1;
+
+       base = ioremap_nocache(resource, len);
+#endif
+       if (base == NULL) {
+               ERROR(dev, "can't map memory\n");
+               retval = -EFAULT;
+               goto error;
+       }
+
+       dev->cap_regs = (struct langwell_cap_regs __iomem *) base;
+       VDBG(dev, "dev->cap_regs: %p\n", dev->cap_regs);
+       dev->op_regs = (struct langwell_op_regs __iomem *)
+               (base + OP_REG_OFFSET);
+       VDBG(dev, "dev->op_regs: %p\n", dev->op_regs);
+
+       /* irq setup after old hardware is cleaned up */
+       if (!pdev->irq) {
+               ERROR(dev, "No IRQ. Check PCI setup!\n");
+               retval = -ENODEV;
+               goto error;
+       }
+
+#ifndef        OTG_TRANSCEIVER
+       INFO(dev, "irq %d, io mem: 0x%08lx, len: 0x%08lx, pci mem 0x%p\n",
+                       pdev->irq, resource, len, base);
+       /* enables bus-mastering for device dev */
+       pci_set_master(pdev);
+
+       if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED,
+                               driver_name, dev) != 0) {
+               ERROR(dev, "request interrupt %d failed\n", pdev->irq);
+               retval = -EBUSY;
+               goto error;
+       }
+       dev->got_irq = 1;
+#endif
+
+       /* set stopped bit */
+       dev->stopped = 1;
+
+       /* capabilities and endpoint number */
+       dev->lpm = (readl(&dev->cap_regs->hccparams) & HCC_LEN) ? 1 : 0;
+       dev->dciversion = readw(&dev->cap_regs->dciversion);
+       dev->devcap = (readl(&dev->cap_regs->dccparams) & DEVCAP) ? 1 : 0;
+       VDBG(dev, "dev->lpm: %d\n", dev->lpm);
+       VDBG(dev, "dev->dciversion: 0x%04x\n", dev->dciversion);
+       VDBG(dev, "dccparams: 0x%08x\n", readl(&dev->cap_regs->dccparams));
+       VDBG(dev, "dev->devcap: %d\n", dev->devcap);
+       if (!dev->devcap) {
+               ERROR(dev, "can't support device mode\n");
+               retval = -ENODEV;
+               goto error;
+       }
+
+       /* a pair of endpoints (out/in) for each address */
+       dev->ep_max = DEN(readl(&dev->cap_regs->dccparams)) * 2;
+       VDBG(dev, "dev->ep_max: %d\n", dev->ep_max);
+
+       /* allocate endpoints memory */
+       dev->ep = kzalloc(sizeof(struct langwell_ep) * dev->ep_max,
+                       GFP_KERNEL);
+       if (!dev->ep) {
+               ERROR(dev, "allocate endpoints memory failed\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       /* allocate device dQH memory */
+       size = dev->ep_max * sizeof(struct langwell_dqh);
+       VDBG(dev, "orig size = %d\n", size);
+       if (size < DQH_ALIGNMENT)
+               size = DQH_ALIGNMENT;
+       else if ((size % DQH_ALIGNMENT) != 0) {
+               size += DQH_ALIGNMENT + 1;
+               size &= ~(DQH_ALIGNMENT - 1);
+       }
+       dev->ep_dqh = dma_alloc_coherent(&pdev->dev, size,
+                                       &dev->ep_dqh_dma, GFP_KERNEL);
+       if (!dev->ep_dqh) {
+               ERROR(dev, "allocate dQH memory failed\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+       dev->ep_dqh_size = size;
+       VDBG(dev, "ep_dqh_size = %d\n", dev->ep_dqh_size);
+
+       /* initialize ep0 status request structure */
+       dev->status_req = kzalloc(sizeof(struct langwell_request), GFP_KERNEL);
+       if (!dev->status_req) {
+               ERROR(dev, "allocate status_req memory failed\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+       INIT_LIST_HEAD(&dev->status_req->queue);
+
+       /* allocate a small amount of memory to get valid address */
+       dev->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+       dev->status_req->req.dma = virt_to_phys(dev->status_req->req.buf);
+
+       dev->resume_state = USB_STATE_NOTATTACHED;
+       dev->usb_state = USB_STATE_POWERED;
+       dev->ep0_dir = USB_DIR_OUT;
+       dev->remote_wakeup = 0; /* default to 0 on reset */
+
+#ifndef        OTG_TRANSCEIVER
+       /* reset device controller */
+       langwell_udc_reset(dev);
+#endif
+
+       /* initialize gadget structure */
+       dev->gadget.ops = &langwell_ops;        /* usb_gadget_ops */
+       dev->gadget.ep0 = &dev->ep[0].ep;       /* gadget ep0 */
+       INIT_LIST_HEAD(&dev->gadget.ep_list);   /* ep_list */
+       dev->gadget.speed = USB_SPEED_UNKNOWN;  /* speed */
+       dev->gadget.is_dualspeed = 1;           /* support dual speed */
+#ifdef OTG_TRANSCEIVER
+       dev->gadget.is_otg = 1;                 /* support otg mode */
+#endif
+
+       /* the "gadget" abstracts/virtualizes the controller */
+       dev_set_name(&dev->gadget.dev, "gadget");
+       dev->gadget.dev.parent = &pdev->dev;
+       dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+       dev->gadget.dev.release = gadget_release;
+       dev->gadget.name = driver_name;         /* gadget name */
+
+       /* controller endpoints reinit */
+       eps_reinit(dev);
+
+#ifndef        OTG_TRANSCEIVER
+       /* reset ep0 dQH and endptctrl */
+       ep0_reset(dev);
+#endif
+
+       /* create dTD dma_pool resource */
+       dev->dtd_pool = dma_pool_create("langwell_dtd",
+                       &dev->pdev->dev,
+                       sizeof(struct langwell_dtd),
+                       DTD_ALIGNMENT,
+                       DMA_BOUNDARY);
+
+       if (!dev->dtd_pool) {
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       /* done */
+       INFO(dev, "%s\n", driver_desc);
+       INFO(dev, "irq %d, pci mem %p\n", pdev->irq, base);
+       INFO(dev, "Driver version: " DRIVER_VERSION "\n");
+       INFO(dev, "Support (max) %d endpoints\n", dev->ep_max);
+       INFO(dev, "Device interface version: 0x%04x\n", dev->dciversion);
+       INFO(dev, "Controller mode: %s\n", dev->devcap ? "Device" : "Host");
+       INFO(dev, "Support USB LPM: %s\n", dev->lpm ? "Yes" : "No");
+
+       VDBG(dev, "After langwell_udc_probe(), print all registers:\n");
+#ifdef VERBOSE
+       print_all_registers(dev);
+#endif
+
+       the_controller = dev;
+
+       retval = device_register(&dev->gadget.dev);
+       if (retval)
+               goto error;
+
+       retval = device_create_file(&pdev->dev, &dev_attr_langwell_udc);
+       if (retval)
+               goto error;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+
+error:
+       if (dev) {
+               DBG(dev, "<--- %s()\n", __func__);
+               langwell_udc_remove(pdev);
+       }
+
+       return retval;
+}
+
+
+/* device controller suspend */
+static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct langwell_udc     *dev = the_controller;
+       u32                     devlc;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* disable interrupt and set controller to stop state */
+       langwell_udc_stop(dev);
+
+       /* diable IRQ handler */
+       if (dev->got_irq)
+               free_irq(pdev->irq, dev);
+       dev->got_irq = 0;
+
+
+       /* save PCI state */
+       pci_save_state(pdev);
+
+       /* set device power state */
+       pci_set_power_state(pdev, PCI_D3hot);
+
+       /* enter PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc |= LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* device controller resume */
+static int langwell_udc_resume(struct pci_dev *pdev)
+{
+       struct langwell_udc     *dev = the_controller;
+       u32                     devlc;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* exit PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc &= ~LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       /* set device D0 power state */
+       pci_set_power_state(pdev, PCI_D0);
+
+       /* restore PCI state */
+       pci_restore_state(pdev);
+
+       /* enable IRQ handler */
+       if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED, driver_name, dev)
+                       != 0) {
+               ERROR(dev, "request interrupt %d failed\n", pdev->irq);
+               return -1;
+       }
+       dev->got_irq = 1;
+
+       /* reset and start controller to run state */
+       if (dev->stopped) {
+               /* reset device controller */
+               langwell_udc_reset(dev);
+
+               /* reset ep0 dQH and endptctrl */
+               ep0_reset(dev);
+
+               /* start device if gadget is loaded */
+               if (dev->driver)
+                       langwell_udc_start(dev);
+       }
+
+       /* reset USB status */
+       dev->usb_state = USB_STATE_ATTACHED;
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->ep0_dir = USB_DIR_OUT;
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* pci driver shutdown */
+static void langwell_udc_shutdown(struct pci_dev *pdev)
+{
+       struct langwell_udc     *dev = the_controller;
+       u32                     usbmode;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* reset controller mode to IDLE */
+       usbmode = readl(&dev->op_regs->usbmode);
+       DBG(dev, "usbmode = 0x%08x\n", usbmode);
+       usbmode &= (~3 | MODE_IDLE);
+       writel(usbmode, &dev->op_regs->usbmode);
+
+       DBG(dev, "<--- %s()\n", __func__);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id pci_ids[] = { {
+       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class_mask =   ~0,
+       .vendor =       0x8086,
+       .device =       0x0811,
+       .subvendor =    PCI_ANY_ID,
+       .subdevice =    PCI_ANY_ID,
+}, { /* end: all zeroes */ }
+};
+
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+
+static struct pci_driver langwell_pci_driver = {
+       .name =         (char *) driver_name,
+       .id_table =     pci_ids,
+
+       .probe =        langwell_udc_probe,
+       .remove =       langwell_udc_remove,
+
+       /* device controller suspend/resume */
+       .suspend =      langwell_udc_suspend,
+       .resume =       langwell_udc_resume,
+
+       .shutdown =     langwell_udc_shutdown,
+};
+
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Xiaochen Shen <xiaochen.shen@intel.com>");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+
+static int __init init(void)
+{
+#ifdef OTG_TRANSCEIVER
+       return langwell_register_peripheral(&langwell_pci_driver);
+#else
+       return pci_register_driver(&langwell_pci_driver);
+#endif
+}
+module_init(init);
+
+
+static void __exit cleanup(void)
+{
+#ifdef OTG_TRANSCEIVER
+       return langwell_unregister_peripheral(&langwell_pci_driver);
+#else
+       pci_unregister_driver(&langwell_pci_driver);
+#endif
+}
+module_exit(cleanup);
+
diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h
new file mode 100644 (file)
index 0000000..9719934
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Intel Langwell USB Device Controller driver
+ * Copyright (C) 2008-2009, Intel Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/usb/langwell_udc.h>
+
+#if defined(CONFIG_USB_LANGWELL_OTG)
+#include <linux/usb/langwell_otg.h>
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+/* driver data structures and utilities */
+
+/*
+ * dTD: Device Endpoint Transfer Descriptor
+ * describe to the device controller the location and quantity of
+ * data to be send/received for given transfer
+ */
+struct langwell_dtd {
+       u32     dtd_next;
+/* bits 31:5, next transfer element pointer */
+#define        DTD_NEXT(d)     (((d)>>5)&0x7ffffff)
+#define        DTD_NEXT_MASK   (0x7ffffff << 5)
+/* terminate */
+#define        DTD_TERM        BIT(0)
+       /* bits 7:0, execution back states */
+       u32     dtd_status:8;
+#define        DTD_STATUS(d)   (((d)>>0)&0xff)
+#define        DTD_STS_ACTIVE  BIT(7)  /* active */
+#define        DTD_STS_HALTED  BIT(6)  /* halted */
+#define        DTD_STS_DBE     BIT(5)  /* data buffer error */
+#define        DTD_STS_TRE     BIT(3)  /* transaction error  */
+       /* bits 9:8 */
+       u32     dtd_res0:2;
+       /* bits 11:10, multipier override */
+       u32     dtd_multo:2;
+#define        DTD_MULTO       (BIT(11) | BIT(10))
+       /* bits 14:12 */
+       u32     dtd_res1:3;
+       /* bit 15, interrupt on complete */
+       u32     dtd_ioc:1;
+#define        DTD_IOC         BIT(15)
+       /* bits 30:16, total bytes */
+       u32     dtd_total:15;
+#define        DTD_TOTAL(d)    (((d)>>16)&0x7fff)
+#define        DTD_MAX_TRANSFER_LENGTH 0x4000
+       /* bit 31 */
+       u32     dtd_res2:1;
+       /* dTD buffer pointer page 0 to 4 */
+       u32     dtd_buf[5];
+#define        DTD_OFFSET_MASK 0xfff
+/* bits 31:12, buffer pointer */
+#define        DTD_BUFFER(d)   (((d)>>12)&0x3ff)
+/* bits 11:0, current offset */
+#define        DTD_C_OFFSET(d) (((d)>>0)&0xfff)
+/* bits 10:0, frame number */
+#define        DTD_FRAME(d)    (((d)>>0)&0x7ff)
+
+       /* driver-private parts */
+
+       /* dtd dma address */
+       dma_addr_t              dtd_dma;
+       /* next dtd virtual address */
+       struct langwell_dtd     *next_dtd_virt;
+};
+
+
+/*
+ * dQH: Device Endpoint Queue Head
+ * describe where all transfers are managed
+ * 48-byte data structure, aligned on 64-byte boundary
+ *
+ * These are associated with dTD structure
+ */
+struct langwell_dqh {
+       /* endpoint capabilities and characteristics */
+       u32     dqh_res0:15;    /* bits 14:0 */
+       u32     dqh_ios:1;      /* bit 15, interrupt on setup */
+#define        DQH_IOS         BIT(15)
+       u32     dqh_mpl:11;     /* bits 26:16, maximum packet length */
+#define        DQH_MPL         (0x7ff << 16)
+       u32     dqh_res1:2;     /* bits 28:27 */
+       u32     dqh_zlt:1;      /* bit 29, zero length termination */
+#define        DQH_ZLT         BIT(29)
+       u32     dqh_mult:2;     /* bits 31:30 */
+#define        DQH_MULT        (BIT(30) | BIT(31))
+
+       /* current dTD pointer */
+       u32     dqh_current;    /* locate the transfer in progress */
+#define DQH_C_DTD(e)   \
+       (((e)>>5)&0x7ffffff)    /* bits 31:5, current dTD pointer */
+
+       /* transfer overlay, hardware parts of a struct langwell_dtd */
+       u32     dtd_next;
+       u32     dtd_status:8;   /* bits 7:0, execution back states */
+       u32     dtd_res0:2;     /* bits 9:8 */
+       u32     dtd_multo:2;    /* bits 11:10, multipier override */
+       u32     dtd_res1:3;     /* bits 14:12 */
+       u32     dtd_ioc:1;      /* bit 15, interrupt on complete */
+       u32     dtd_total:15;   /* bits 30:16, total bytes */
+       u32     dtd_res2:1;     /* bit 31 */
+       u32     dtd_buf[5];     /* dTD buffer pointer page 0 to 4 */
+
+       u32     dqh_res2;
+       struct usb_ctrlrequest  dqh_setup;      /* setup packet buffer */
+} __attribute__ ((aligned(64)));
+
+
+/* endpoint data structure */
+struct langwell_ep {
+       struct usb_ep           ep;
+       dma_addr_t              dma;
+       struct langwell_udc     *dev;
+       unsigned long           irqs;
+       struct list_head        queue;
+       struct langwell_dqh     *dqh;
+       const struct usb_endpoint_descriptor    *desc;
+       char                    name[14];
+       unsigned                stopped:1,
+                               ep_type:2,
+                               ep_num:8;
+};
+
+
+/* request data structure */
+struct langwell_request {
+       struct usb_request      req;
+       struct langwell_dtd     *dtd, *head, *tail;
+       struct langwell_ep      *ep;
+       dma_addr_t              dtd_dma;
+       struct list_head        queue;
+       unsigned                dtd_count;
+       unsigned                mapped:1;
+};
+
+
+/* ep0 transfer state */
+enum ep0_state {
+       WAIT_FOR_SETUP,
+       DATA_STATE_XMIT,
+       DATA_STATE_NEED_ZLP,
+       WAIT_FOR_OUT_STATUS,
+       DATA_STATE_RECV,
+};
+
+
+/* device suspend state */
+enum lpm_state {
+       LPM_L0, /* on */
+       LPM_L1, /* LPM L1 sleep */
+       LPM_L2, /* suspend */
+       LPM_L3, /* off */
+};
+
+
+/* device data structure */
+struct langwell_udc {
+       /* each pci device provides one gadget, several endpoints */
+       struct usb_gadget       gadget;
+       spinlock_t              lock;   /* device lock */
+       struct langwell_ep      *ep;
+       struct usb_gadget_driver        *driver;
+       struct otg_transceiver  *transceiver;
+       u8                      dev_addr;
+       u32                     usb_state;
+       u32                     resume_state;
+       u32                     bus_reset;
+       enum lpm_state          lpm_state;
+       enum ep0_state          ep0_state;
+       u32                     ep0_dir;
+       u16                     dciversion;
+       unsigned                ep_max;
+       unsigned                devcap:1,
+                               enabled:1,
+                               region:1,
+                               got_irq:1,
+                               powered:1,
+                               remote_wakeup:1,
+                               rate:1,
+                               is_reset:1,
+                               softconnected:1,
+                               vbus_active:1,
+                               suspended:1,
+                               stopped:1,
+                               lpm:1;  /* LPM capability */
+
+       /* pci state used to access those endpoints */
+       struct pci_dev          *pdev;
+
+       /* Langwell otg transceiver */
+       struct langwell_otg     *lotg;
+
+       /* control registers */
+       struct langwell_cap_regs        __iomem *cap_regs;
+       struct langwell_op_regs         __iomem *op_regs;
+
+       struct usb_ctrlrequest  local_setup_buff;
+       struct langwell_dqh     *ep_dqh;
+       size_t                  ep_dqh_size;
+       dma_addr_t              ep_dqh_dma;
+
+       /* ep0 status request */
+       struct langwell_request *status_req;
+
+       /* dma pool */
+       struct dma_pool         *dtd_pool;
+
+       /* make sure release() is done */
+       struct completion       *done;
+};
+
index 8cc676e..1937d8c 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/usb.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
-#include <mach/pxa2xx-regs.h> /* FIXME: for PSSR */
 #include <mach/udc.h>
 
 #include "pxa27x_udc.h"
@@ -474,6 +473,23 @@ static inline void udc_clear_mask_UDCCR(struct pxa_udc *udc, int mask)
 }
 
 /**
+ * ep_write_UDCCSR - set bits in UDCCSR
+ * @udc: udc device
+ * @mask: bits to set in UDCCR
+ *
+ * Sets bits in UDCCSR (UDCCSR0 and UDCCSR*).
+ *
+ * A specific case is applied to ep0 : the ACM bit is always set to 1, for
+ * SET_INTERFACE and SET_CONFIGURATION.
+ */
+static inline void ep_write_UDCCSR(struct pxa_ep *ep, int mask)
+{
+       if (is_ep0(ep))
+               mask |= UDCCSR0_ACM;
+       udc_ep_writel(ep, UDCCSR, mask);
+}
+
+/**
  * ep_count_bytes_remain - get how many bytes in udc endpoint
  * @ep: udc endpoint
  *
@@ -861,7 +877,7 @@ static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req)
                *buf++ = udc_ep_readl(ep, UDCDR);
        req->req.actual += count;
 
-       udc_ep_writel(ep, UDCCSR, UDCCSR_PC);
+       ep_write_UDCCSR(ep, UDCCSR_PC);
 
        return count;
 }
@@ -969,12 +985,12 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
                if (udccsr & UDCCSR_PC) {
                        ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n",
                                udccsr);
-                       udc_ep_writel(ep, UDCCSR, UDCCSR_PC);
+                       ep_write_UDCCSR(ep, UDCCSR_PC);
                }
                if (udccsr & UDCCSR_TRN) {
                        ep_vdbg(ep, "Clearing Underrun on, udccsr=%x\n",
                                udccsr);
-                       udc_ep_writel(ep, UDCCSR, UDCCSR_TRN);
+                       ep_write_UDCCSR(ep, UDCCSR_TRN);
                }
 
                count = write_packet(ep, req, max);
@@ -996,7 +1012,7 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
                }
 
                if (is_short)
-                       udc_ep_writel(ep, UDCCSR, UDCCSR_SP);
+                       ep_write_UDCCSR(ep, UDCCSR_SP);
 
                /* requests complete when all IN data is in the FIFO */
                if (is_last) {
@@ -1029,7 +1045,7 @@ static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
 
        while (epout_has_pkt(ep)) {
                count = read_packet(ep, req);
-               udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC);
+               ep_write_UDCCSR(ep, UDCCSR0_OPC);
                inc_ep_stats_bytes(ep, count, !USB_DIR_IN);
 
                is_short = (count < ep->fifo_size);
@@ -1074,7 +1090,7 @@ static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
 
        /* Sends either a short packet or a 0 length packet */
        if (unlikely(is_short))
-               udc_ep_writel(ep, UDCCSR, UDCCSR0_IPR);
+               ep_write_UDCCSR(ep, UDCCSR0_IPR);
 
        ep_dbg(ep, "in %d bytes%s%s, %d left, req=%p, udccsr0=0x%03x\n",
                count, is_short ? "/S" : "", is_last ? "/L" : "",
@@ -1277,7 +1293,7 @@ static int pxa_ep_set_halt(struct usb_ep *_ep, int value)
 
        /* FST, FEF bits are the same for control and non control endpoints */
        rc = 0;
-       udc_ep_writel(ep, UDCCSR, UDCCSR_FST | UDCCSR_FEF);
+       ep_write_UDCCSR(ep, UDCCSR_FST | UDCCSR_FEF);
        if (is_ep0(ep))
                set_ep0state(ep->dev, STALL);
 
@@ -1343,7 +1359,7 @@ static void pxa_ep_fifo_flush(struct usb_ep *_ep)
                        udc_ep_readl(ep, UDCDR);
        } else {
                /* most IN status is the same, but ISO can't stall */
-               udc_ep_writel(ep, UDCCSR,
+               ep_write_UDCCSR(ep,
                                UDCCSR_PC | UDCCSR_FEF | UDCCSR_TRN
                                | (EPXFERTYPE_is_ISO(ep) ? 0 : UDCCSR_SST));
        }
@@ -1728,6 +1744,7 @@ static void udc_enable(struct pxa_udc *udc)
        memset(&udc->stats, 0, sizeof(udc->stats));
 
        udc_set_mask_UDCCR(udc, UDCCR_UDE);
+       ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM);
        udelay(2);
        if (udc_readl(udc, UDCCR) & UDCCR_EMCE)
                dev_err(udc->dev, "Configuration errors, udc disabled\n");
@@ -1893,6 +1910,15 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc,
 
        nuke(ep, -EPROTO);
 
+       /*
+        * In the PXA320 manual, in the section about Back-to-Back setup
+        * packets, it describes this situation.  The solution is to set OPC to
+        * get rid of the status packet, and then continue with the setup
+        * packet. Generalize to pxa27x CPUs.
+        */
+       if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0))
+               ep_write_UDCCSR(ep, UDCCSR0_OPC);
+
        /* read SETUP packet */
        for (i = 0; i < 2; i++) {
                if (unlikely(ep_is_empty(ep)))
@@ -1919,7 +1945,7 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc,
                set_ep0state(udc, OUT_DATA_STAGE);
 
        /* Tell UDC to enter Data Stage */
-       udc_ep_writel(ep, UDCCSR, UDCCSR0_SA | UDCCSR0_OPC);
+       ep_write_UDCCSR(ep, UDCCSR0_SA | UDCCSR0_OPC);
 
        i = udc->driver->setup(&udc->gadget, &u.r);
        if (i < 0)
@@ -1929,7 +1955,7 @@ out:
 stall:
        ep_dbg(ep, "protocol STALL, udccsr0=%03x err %d\n",
                udc_ep_readl(ep, UDCCSR), i);
-       udc_ep_writel(ep, UDCCSR, UDCCSR0_FST | UDCCSR0_FTF);
+       ep_write_UDCCSR(ep, UDCCSR0_FST | UDCCSR0_FTF);
        set_ep0state(udc, STALL);
        goto out;
 }
@@ -1966,6 +1992,8 @@ stall:
  *     cleared by software.
  *   - clearing UDCCSR0_OPC always flushes ep0. If in setup stage, never do it
  *     before reading ep0.
+ *     This is true only for PXA27x. This is not true anymore for PXA3xx family
+ *     (check Back-to-Back setup packet in developers guide).
  *   - irq can be called on a "packet complete" event (opc_irq=1), while
  *     UDCCSR0_OPC is not yet raised (delta can be as big as 100ms
  *     from experimentation).
@@ -1998,7 +2026,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
        if (udccsr0 & UDCCSR0_SST) {
                ep_dbg(ep, "clearing stall status\n");
                nuke(ep, -EPIPE);
-               udc_ep_writel(ep, UDCCSR, UDCCSR0_SST);
+               ep_write_UDCCSR(ep, UDCCSR0_SST);
                ep0_idle(udc);
        }
 
@@ -2023,7 +2051,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
                break;
        case IN_DATA_STAGE:                     /* GET_DESCRIPTOR */
                if (epout_has_pkt(ep))
-                       udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC);
+                       ep_write_UDCCSR(ep, UDCCSR0_OPC);
                if (req && !ep_is_full(ep))
                        completed = write_ep0_fifo(ep, req);
                if (completed)
@@ -2036,7 +2064,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
                        ep0_end_out_req(ep, req);
                break;
        case STALL:
-               udc_ep_writel(ep, UDCCSR, UDCCSR0_FST);
+               ep_write_UDCCSR(ep, UDCCSR0_FST);
                break;
        case IN_STATUS_STAGE:
                /*
@@ -2131,6 +2159,7 @@ static void pxa27x_change_configuration(struct pxa_udc *udc, int config)
 
        set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
        udc->driver->setup(&udc->gadget, &req);
+       ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
 }
 
 /**
@@ -2159,6 +2188,7 @@ static void pxa27x_change_interface(struct pxa_udc *udc, int iface, int alt)
 
        set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
        udc->driver->setup(&udc->gadget, &req);
+       ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
 }
 
 /*
@@ -2280,7 +2310,7 @@ static void irq_udc_reset(struct pxa_udc *udc)
        memset(&udc->stats, 0, sizeof udc->stats);
 
        nuke(ep, -EPROTO);
-       udc_ep_writel(ep, UDCCSR, UDCCSR0_FTF | UDCCSR0_OPC);
+       ep_write_UDCCSR(ep, UDCCSR0_FTF | UDCCSR0_OPC);
        ep0_idle(udc);
 }
 
@@ -2479,6 +2509,12 @@ static void pxa_udc_shutdown(struct platform_device *_dev)
                udc_disable(udc);
 }
 
+#ifdef CONFIG_CPU_PXA27x
+extern void pxa27x_clear_otgph(void);
+#else
+#define pxa27x_clear_otgph()   do {} while (0)
+#endif
+
 #ifdef CONFIG_PM
 /**
  * pxa_udc_suspend - Suspend udc device
@@ -2546,8 +2582,7 @@ static int pxa_udc_resume(struct platform_device *_dev)
         * Software must configure the USB OTG pad, UDC, and UHC
         * to the state they were in before entering sleep mode.
         */
-       if (cpu_is_pxa27x())
-               PSSR |= PSSR_OTGPH;
+       pxa27x_clear_otgph();
 
        return 0;
 }
@@ -2571,7 +2606,7 @@ static struct platform_driver udc_driver = {
 
 static int __init udc_init(void)
 {
-       if (!cpu_is_pxa27x())
+       if (!cpu_is_pxa27x() && !cpu_is_pxa3xx())
                return -ENODEV;
 
        printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
index db58125..e25225e 100644 (file)
 #define UP2OCR_HXOE    (1 << 17)       /* Transceiver Output Enable */
 #define UP2OCR_SEOS    (1 << 24)       /* Single-Ended Output Select */
 
+#define UDCCSR0_ACM    (1 << 9)        /* Ack Control Mode */
+#define UDCCSR0_AREN   (1 << 8)        /* Ack Response Enable */
 #define UDCCSR0_SA     (1 << 7)        /* Setup Active */
 #define UDCCSR0_RNE    (1 << 6)        /* Receive FIFO Not Empty */
 #define UDCCSR0_FST    (1 << 5)        /* Force Stall */
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
new file mode 100644 (file)
index 0000000..50c71aa
--- /dev/null
@@ -0,0 +1,3269 @@
+/* linux/drivers/usb/gadget/s3c-hsotg.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ *      Ben Dooks <ben@simtec.co.uk>
+ *      http://armlinux.simtec.co.uk/
+ *
+ * S3C USB2.0 High-speed / OtG driver
+ *
+ * This program 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/map.h>
+
+#include <plat/regs-usb-hsotg-phy.h>
+#include <plat/regs-usb-hsotg.h>
+#include <plat/regs-sys.h>
+#include <plat/udc-hs.h>
+
+#define DMA_ADDR_INVALID (~((dma_addr_t)0))
+
+/* EP0_MPS_LIMIT
+ *
+ * Unfortunately there seems to be a limit of the amount of data that can
+ * be transfered by IN transactions on EP0. This is either 127 bytes or 3
+ * packets (which practially means 1 packet and 63 bytes of data) when the
+ * MPS is set to 64.
+ *
+ * This means if we are wanting to move >127 bytes of data, we need to
+ * split the transactions up, but just doing one packet at a time does
+ * not work (this may be an implicit DATA0 PID on first packet of the
+ * transaction) and doing 2 packets is outside the controller's limits.
+ *
+ * If we try to lower the MPS size for EP0, then no transfers work properly
+ * for EP0, and the system will fail basic enumeration. As no cause for this
+ * has currently been found, we cannot support any large IN transfers for
+ * EP0.
+ */
+#define EP0_MPS_LIMIT  64
+
+struct s3c_hsotg;
+struct s3c_hsotg_req;
+
+/**
+ * struct s3c_hsotg_ep - driver endpoint definition.
+ * @ep: The gadget layer representation of the endpoint.
+ * @name: The driver generated name for the endpoint.
+ * @queue: Queue of requests for this endpoint.
+ * @parent: Reference back to the parent device structure.
+ * @req: The current request that the endpoint is processing. This is
+ *       used to indicate an request has been loaded onto the endpoint
+ *       and has yet to be completed (maybe due to data move, or simply
+ *      awaiting an ack from the core all the data has been completed).
+ * @debugfs: File entry for debugfs file for this endpoint.
+ * @lock: State lock to protect contents of endpoint.
+ * @dir_in: Set to true if this endpoint is of the IN direction, which
+ *         means that it is sending data to the Host.
+ * @index: The index for the endpoint registers.
+ * @name: The name array passed to the USB core.
+ * @halted: Set if the endpoint has been halted.
+ * @periodic: Set if this is a periodic ep, such as Interrupt
+ * @sent_zlp: Set if we've sent a zero-length packet.
+ * @total_data: The total number of data bytes done.
+ * @fifo_size: The size of the FIFO (for periodic IN endpoints)
+ * @fifo_load: The amount of data loaded into the FIFO (periodic IN)
+ * @last_load: The offset of data for the last start of request.
+ * @size_loaded: The last loaded size for DxEPTSIZE for periodic IN
+ *
+ * This is the driver's state for each registered enpoint, allowing it
+ * to keep track of transactions that need doing. Each endpoint has a
+ * lock to protect the state, to try and avoid using an overall lock
+ * for the host controller as much as possible.
+ *
+ * For periodic IN endpoints, we have fifo_size and fifo_load to try
+ * and keep track of the amount of data in the periodic FIFO for each
+ * of these as we don't have a status register that tells us how much
+ * is in each of them.
+ */
+struct s3c_hsotg_ep {
+       struct usb_ep           ep;
+       struct list_head        queue;
+       struct s3c_hsotg        *parent;
+       struct s3c_hsotg_req    *req;
+       struct dentry           *debugfs;
+
+       spinlock_t              lock;
+
+       unsigned long           total_data;
+       unsigned int            size_loaded;
+       unsigned int            last_load;
+       unsigned int            fifo_load;
+       unsigned short          fifo_size;
+
+       unsigned char           dir_in;
+       unsigned char           index;
+
+       unsigned int            halted:1;
+       unsigned int            periodic:1;
+       unsigned int            sent_zlp:1;
+
+       char                    name[10];
+};
+
+#define S3C_HSOTG_EPS  (8+1)   /* limit to 9 for the moment */
+
+/**
+ * struct s3c_hsotg - driver state.
+ * @dev: The parent device supplied to the probe function
+ * @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
+ * @debug_root: root directrory for debugfs.
+ * @debug_file: main status file for debugfs.
+ * @debug_fifo: FIFO status file for debugfs.
+ * @ep0_reply: Request used for ep0 reply.
+ * @ep0_buff: Buffer for EP0 reply data, if needed.
+ * @ctrl_buff: Buffer for EP0 control requests.
+ * @ctrl_req: Request for EP0 control packets.
+ * @eps: The endpoints being supplied to the gadget framework
+ */
+struct s3c_hsotg {
+       struct device            *dev;
+       struct usb_gadget_driver *driver;
+       struct s3c_hsotg_plat    *plat;
+
+       void __iomem            *regs;
+       struct resource         *regs_res;
+       int                     irq;
+
+       struct dentry           *debug_root;
+       struct dentry           *debug_file;
+       struct dentry           *debug_fifo;
+
+       struct usb_request      *ep0_reply;
+       struct usb_request      *ctrl_req;
+       u8                      ep0_buff[8];
+       u8                      ctrl_buff[8];
+
+       struct usb_gadget       gadget;
+       struct s3c_hsotg_ep     eps[];
+};
+
+/**
+ * struct s3c_hsotg_req - data transfer request
+ * @req: The USB gadget request
+ * @queue: The list of requests for the endpoint this is queued for.
+ * @in_progress: Has already had size/packets written to core
+ * @mapped: DMA buffer for this request has been mapped via dma_map_single().
+ */
+struct s3c_hsotg_req {
+       struct usb_request      req;
+       struct list_head        queue;
+       unsigned char           in_progress;
+       unsigned char           mapped;
+};
+
+/* conversion functions */
+static inline struct s3c_hsotg_req *our_req(struct usb_request *req)
+{
+       return container_of(req, struct s3c_hsotg_req, req);
+}
+
+static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep)
+{
+       return container_of(ep, struct s3c_hsotg_ep, ep);
+}
+
+static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget)
+{
+       return container_of(gadget, struct s3c_hsotg, gadget);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+       writel(readl(ptr) | val, ptr);
+}
+
+static inline void __bic32(void __iomem *ptr, u32 val)
+{
+       writel(readl(ptr) & ~val, ptr);
+}
+
+/* forward decleration of functions */
+static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
+
+/**
+ * using_dma - return the DMA status of the driver.
+ * @hsotg: The driver state.
+ *
+ * Return true if we're using DMA.
+ *
+ * Currently, we have the DMA support code worked into everywhere
+ * that needs it, but the AMBA DMA implementation in the hardware can
+ * only DMA from 32bit aligned addresses. This means that gadgets such
+ * as the CDC Ethernet cannot work as they often pass packets which are
+ * not 32bit aligned.
+ *
+ * Unfortunately the choice to use DMA or not is global to the controller
+ * and seems to be only settable when the controller is being put through
+ * a core reset. This means we either need to fix the gadgets to take
+ * account of DMA alignment, or add bounce buffers (yuerk).
+ *
+ * Until this issue is sorted out, we always return 'false'.
+ */
+static inline bool using_dma(struct s3c_hsotg *hsotg)
+{
+       return false;   /* support is not complete */
+}
+
+/**
+ * s3c_hsotg_en_gsint - enable one or more of the general interrupt
+ * @hsotg: The device state
+ * @ints: A bitmask of the interrupts to enable
+ */
+static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
+{
+       u32 gsintmsk = readl(hsotg->regs + S3C_GINTMSK);
+       u32 new_gsintmsk;
+
+       new_gsintmsk = gsintmsk | ints;
+
+       if (new_gsintmsk != gsintmsk) {
+               dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk);
+               writel(new_gsintmsk, hsotg->regs + S3C_GINTMSK);
+       }
+}
+
+/**
+ * s3c_hsotg_disable_gsint - disable one or more of the general interrupt
+ * @hsotg: The device state
+ * @ints: A bitmask of the interrupts to enable
+ */
+static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
+{
+       u32 gsintmsk = readl(hsotg->regs + S3C_GINTMSK);
+       u32 new_gsintmsk;
+
+       new_gsintmsk = gsintmsk & ~ints;
+
+       if (new_gsintmsk != gsintmsk)
+               writel(new_gsintmsk, hsotg->regs + S3C_GINTMSK);
+}
+
+/**
+ * s3c_hsotg_ctrl_epint - enable/disable an endpoint irq
+ * @hsotg: The device state
+ * @ep: The endpoint index
+ * @dir_in: True if direction is in.
+ * @en: The enable value, true to enable
+ *
+ * Set or clear the mask for an individual endpoint's interrupt
+ * request.
+ */
+static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
+                                unsigned int ep, unsigned int dir_in,
+                                unsigned int en)
+{
+       unsigned long flags;
+       u32 bit = 1 << ep;
+       u32 daint;
+
+       if (!dir_in)
+               bit <<= 16;
+
+       local_irq_save(flags);
+       daint = readl(hsotg->regs + S3C_DAINTMSK);
+       if (en)
+               daint |= bit;
+       else
+               daint &= ~bit;
+       writel(daint, hsotg->regs + S3C_DAINTMSK);
+       local_irq_restore(flags);
+}
+
+/**
+ * s3c_hsotg_init_fifo - initialise non-periodic FIFOs
+ * @hsotg: The device instance.
+ */
+static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
+{
+       /* the ryu 2.6.24 release ahs
+          writel(0x1C0, hsotg->regs + S3C_GRXFSIZ);
+          writel(S3C_GNPTXFSIZ_NPTxFStAddr(0x200) |
+               S3C_GNPTXFSIZ_NPTxFDep(0x1C0),
+               hsotg->regs + S3C_GNPTXFSIZ);
+       */
+
+       /* set FIFO sizes to 2048/0x1C0 */
+
+       writel(2048, hsotg->regs + S3C_GRXFSIZ);
+       writel(S3C_GNPTXFSIZ_NPTxFStAddr(2048) |
+              S3C_GNPTXFSIZ_NPTxFDep(0x1C0),
+              hsotg->regs + S3C_GNPTXFSIZ);
+}
+
+/**
+ * @ep: USB endpoint to allocate request for.
+ * @flags: Allocation flags
+ *
+ * Allocate a new USB request structure appropriate for the specified endpoint
+ */
+struct usb_request *s3c_hsotg_ep_alloc_request(struct usb_ep *ep, gfp_t flags)
+{
+       struct s3c_hsotg_req *req;
+
+       req = kzalloc(sizeof(struct s3c_hsotg_req), flags);
+       if (!req)
+               return NULL;
+
+       INIT_LIST_HEAD(&req->queue);
+
+       req->req.dma = DMA_ADDR_INVALID;
+       return &req->req;
+}
+
+/**
+ * is_ep_periodic - return true if the endpoint is in periodic mode.
+ * @hs_ep: The endpoint to query.
+ *
+ * Returns true if the endpoint is in periodic mode, meaning it is being
+ * used for an Interrupt or ISO transfer.
+ */
+static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep)
+{
+       return hs_ep->periodic;
+}
+
+/**
+ * s3c_hsotg_unmap_dma - unmap the DMA memory being used for the request
+ * @hsotg: The device state.
+ * @hs_ep: The endpoint for the request
+ * @hs_req: The request being processed.
+ *
+ * This is the reverse of s3c_hsotg_map_dma(), called for the completion
+ * of a request to ensure the buffer is ready for access by the caller.
+*/
+static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
+                               struct s3c_hsotg_ep *hs_ep,
+                               struct s3c_hsotg_req *hs_req)
+{
+       struct usb_request *req = &hs_req->req;
+       enum dma_data_direction dir;
+
+       dir = hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+       /* ignore this if we're not moving any data */
+       if (hs_req->req.length == 0)
+               return;
+
+       if (hs_req->mapped) {
+               /* we mapped this, so unmap and remove the dma */
+
+               dma_unmap_single(hsotg->dev, req->dma, req->length, dir);
+
+               req->dma = DMA_ADDR_INVALID;
+               hs_req->mapped = 0;
+       } else {
+               dma_sync_single(hsotg->dev, req->dma, req->length, dir);
+       }
+}
+
+/**
+ * s3c_hsotg_write_fifo - write packet Data to the TxFIFO
+ * @hsotg: The controller state.
+ * @hs_ep: The endpoint we're going to write for.
+ * @hs_req: The request to write data for.
+ *
+ * This is called when the TxFIFO has some space in it to hold a new
+ * transmission and we have something to give it. The actual setup of
+ * the data size is done elsewhere, so all we have to do is to actually
+ * write the data.
+ *
+ * The return value is zero if there is more space (or nothing was done)
+ * otherwise -ENOSPC is returned if the FIFO space was used up.
+ *
+ * This routine is only needed for PIO
+*/
+static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
+                               struct s3c_hsotg_ep *hs_ep,
+                               struct s3c_hsotg_req *hs_req)
+{
+       bool periodic = is_ep_periodic(hs_ep);
+       u32 gnptxsts = readl(hsotg->regs + S3C_GNPTXSTS);
+       int buf_pos = hs_req->req.actual;
+       int to_write = hs_ep->size_loaded;
+       void *data;
+       int can_write;
+       int pkt_round;
+
+       to_write -= (buf_pos - hs_ep->last_load);
+
+       /* if there's nothing to write, get out early */
+       if (to_write == 0)
+               return 0;
+
+       if (periodic) {
+               u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index));
+               int size_left;
+               int size_done;
+
+               /* work out how much data was loaded so we can calculate
+                * how much data is left in the fifo. */
+
+               size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
+
+               dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n",
+                       __func__, size_left,
+                       hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size);
+
+               /* how much of the data has moved */
+               size_done = hs_ep->size_loaded - size_left;
+
+               /* how much data is left in the fifo */
+               can_write = hs_ep->fifo_load - size_done;
+               dev_dbg(hsotg->dev, "%s: => can_write1=%d\n",
+                       __func__, can_write);
+
+               can_write = hs_ep->fifo_size - can_write;
+               dev_dbg(hsotg->dev, "%s: => can_write2=%d\n",
+                       __func__, can_write);
+
+               if (can_write <= 0) {
+                       s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
+                       return -ENOSPC;
+               }
+       } else {
+               if (S3C_GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) {
+                       dev_dbg(hsotg->dev,
+                               "%s: no queue slots available (0x%08x)\n",
+                               __func__, gnptxsts);
+
+                       s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_NPTxFEmp);
+                       return -ENOSPC;
+               }
+
+               can_write = S3C_GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts);
+       }
+
+       dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
+                __func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket);
+
+       /* limit to 512 bytes of data, it seems at least on the non-periodic
+        * FIFO, requests of >512 cause the endpoint to get stuck with a
+        * fragment of the end of the transfer in it.
+        */
+       if (can_write > 512)
+               can_write = 512;
+
+       /* see if we can write data */
+
+       if (to_write > can_write) {
+               to_write = can_write;
+               pkt_round = to_write % hs_ep->ep.maxpacket;
+
+               /* Not sure, but we probably shouldn't be writing partial
+                * packets into the FIFO, so round the write down to an
+                * exact number of packets.
+                *
+                * Note, we do not currently check to see if we can ever
+                * write a full packet or not to the FIFO.
+                */
+
+               if (pkt_round)
+                       to_write -= pkt_round;
+
+               /* enable correct FIFO interrupt to alert us when there
+                * is more room left. */
+
+               s3c_hsotg_en_gsint(hsotg,
+                                  periodic ? S3C_GINTSTS_PTxFEmp :
+                                  S3C_GINTSTS_NPTxFEmp);
+       }
+
+       dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n",
+                to_write, hs_req->req.length, can_write, buf_pos);
+
+       if (to_write <= 0)
+               return -ENOSPC;
+
+       hs_req->req.actual = buf_pos + to_write;
+       hs_ep->total_data += to_write;
+
+       if (periodic)
+               hs_ep->fifo_load += to_write;
+
+       to_write = DIV_ROUND_UP(to_write, 4);
+       data = hs_req->req.buf + buf_pos;
+
+       writesl(hsotg->regs + S3C_EPFIFO(hs_ep->index), data, to_write);
+
+       return (to_write >= can_write) ? -ENOSPC : 0;
+}
+
+/**
+ * get_ep_limit - get the maximum data legnth for this endpoint
+ * @hs_ep: The endpoint
+ *
+ * Return the maximum data that can be queued in one go on a given endpoint
+ * so that transfers that are too long can be split.
+ */
+static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
+{
+       int index = hs_ep->index;
+       unsigned maxsize;
+       unsigned maxpkt;
+
+       if (index != 0) {
+               maxsize = S3C_DxEPTSIZ_XferSize_LIMIT + 1;
+               maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1;
+       } else {
+               if (hs_ep->dir_in) {
+                       /* maxsize = S3C_DIEPTSIZ0_XferSize_LIMIT + 1; */
+                       maxsize = 64+64+1;
+                       maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1;
+               } else {
+                       maxsize = 0x3f;
+                       maxpkt = 2;
+               }
+       }
+
+       /* we made the constant loading easier above by using +1 */
+       maxpkt--;
+       maxsize--;
+
+       /* constrain by packet count if maxpkts*pktsize is greater
+        * than the length register size. */
+
+       if ((maxpkt * hs_ep->ep.maxpacket) < maxsize)
+               maxsize = maxpkt * hs_ep->ep.maxpacket;
+
+       return maxsize;
+}
+
+/**
+ * s3c_hsotg_start_req - start a USB request from an endpoint's queue
+ * @hsotg: The controller state.
+ * @hs_ep: The endpoint to process a request for
+ * @hs_req: The request to start.
+ * @continuing: True if we are doing more for the current request.
+ *
+ * Start the given request running by setting the endpoint registers
+ * appropriately, and writing any data to the FIFOs.
+ */
+static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
+                               struct s3c_hsotg_ep *hs_ep,
+                               struct s3c_hsotg_req *hs_req,
+                               bool continuing)
+{
+       struct usb_request *ureq = &hs_req->req;
+       int index = hs_ep->index;
+       int dir_in = hs_ep->dir_in;
+       u32 epctrl_reg;
+       u32 epsize_reg;
+       u32 epsize;
+       u32 ctrl;
+       unsigned length;
+       unsigned packets;
+       unsigned maxreq;
+
+       if (index != 0) {
+               if (hs_ep->req && !continuing) {
+                       dev_err(hsotg->dev, "%s: active request\n", __func__);
+                       WARN_ON(1);
+                       return;
+               } else if (hs_ep->req != hs_req && continuing) {
+                       dev_err(hsotg->dev,
+                               "%s: continue different req\n", __func__);
+                       WARN_ON(1);
+                       return;
+               }
+       }
+
+       epctrl_reg = dir_in ? S3C_DIEPCTL(index) : S3C_DOEPCTL(index);
+       epsize_reg = dir_in ? S3C_DIEPTSIZ(index) : S3C_DOEPTSIZ(index);
+
+       dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n",
+               __func__, readl(hsotg->regs + epctrl_reg), index,
+               hs_ep->dir_in ? "in" : "out");
+
+       length = ureq->length - ureq->actual;
+
+       if (0)
+               dev_dbg(hsotg->dev,
+                       "REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n",
+                       ureq->buf, length, ureq->dma,
+                       ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
+
+       maxreq = get_ep_limit(hs_ep);
+       if (length > maxreq) {
+               int round = maxreq % hs_ep->ep.maxpacket;
+
+               dev_dbg(hsotg->dev, "%s: length %d, max-req %d, r %d\n",
+                       __func__, length, maxreq, round);
+
+               /* round down to multiple of packets */
+               if (round)
+                       maxreq -= round;
+
+               length = maxreq;
+       }
+
+       if (length)
+               packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket);
+       else
+               packets = 1;    /* send one packet if length is zero. */
+
+       if (dir_in && index != 0)
+               epsize = S3C_DxEPTSIZ_MC(1);
+       else
+               epsize = 0;
+
+       if (index != 0 && ureq->zero) {
+               /* test for the packets being exactly right for the
+                * transfer */
+
+               if (length == (packets * hs_ep->ep.maxpacket))
+                       packets++;
+       }
+
+       epsize |= S3C_DxEPTSIZ_PktCnt(packets);
+       epsize |= S3C_DxEPTSIZ_XferSize(length);
+
+       dev_dbg(hsotg->dev, "%s: %d@%d/%d, 0x%08x => 0x%08x\n",
+               __func__, packets, length, ureq->length, epsize, epsize_reg);
+
+       /* store the request as the current one we're doing */
+       hs_ep->req = hs_req;
+
+       /* write size / packets */
+       writel(epsize, hsotg->regs + epsize_reg);
+
+       ctrl = readl(hsotg->regs + epctrl_reg);
+
+       if (ctrl & S3C_DxEPCTL_Stall) {
+               dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index);
+
+               /* not sure what we can do here, if it is EP0 then we should
+                * get this cleared once the endpoint has transmitted the
+                * STALL packet, otherwise it needs to be cleared by the
+                * host.
+                */
+       }
+
+       if (using_dma(hsotg)) {
+               unsigned int dma_reg;
+
+               /* write DMA address to control register, buffer already
+                * synced by s3c_hsotg_ep_queue().  */
+
+               dma_reg = dir_in ? S3C_DIEPDMA(index) : S3C_DOEPDMA(index);
+               writel(ureq->dma, hsotg->regs + dma_reg);
+
+               dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n",
+                       __func__, ureq->dma, dma_reg);
+       }
+
+       ctrl |= S3C_DxEPCTL_EPEna;      /* ensure ep enabled */
+       ctrl |= S3C_DxEPCTL_USBActEp;
+       ctrl |= S3C_DxEPCTL_CNAK;       /* clear NAK set by core */
+
+       dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
+       writel(ctrl, hsotg->regs + epctrl_reg);
+
+       /* set these, it seems that DMA support increments past the end
+        * of the packet buffer so we need to calculate the length from
+        * this information. */
+       hs_ep->size_loaded = length;
+       hs_ep->last_load = ureq->actual;
+
+       if (dir_in && !using_dma(hsotg)) {
+               /* set these anyway, we may need them for non-periodic in */
+               hs_ep->fifo_load = 0;
+
+               s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req);
+       }
+
+       /* clear the INTknTXFEmpMsk when we start request, more as a aide
+        * to debugging to see what is going on. */
+       if (dir_in)
+               writel(S3C_DIEPMSK_INTknTXFEmpMsk,
+                      hsotg->regs + S3C_DIEPINT(index));
+
+       /* Note, trying to clear the NAK here causes problems with transmit
+        * on the S3C6400 ending up with the TXFIFO becomming full. */
+
+       /* check ep is enabled */
+       if (!(readl(hsotg->regs + epctrl_reg) & S3C_DxEPCTL_EPEna))
+               dev_warn(hsotg->dev,
+                        "ep%d: failed to become enabled (DxEPCTL=0x%08x)?\n",
+                        index, readl(hsotg->regs + epctrl_reg));
+
+       dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n",
+               __func__, readl(hsotg->regs + epctrl_reg));
+}
+
+/**
+ * s3c_hsotg_map_dma - map the DMA memory being used for the request
+ * @hsotg: The device state.
+ * @hs_ep: The endpoint the request is on.
+ * @req: The request being processed.
+ *
+ * We've been asked to queue a request, so ensure that the memory buffer
+ * is correctly setup for DMA. If we've been passed an extant DMA address
+ * then ensure the buffer has been synced to memory. If our buffer has no
+ * DMA memory, then we map the memory and mark our request to allow us to
+ * cleanup on completion.
+*/
+static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg,
+                            struct s3c_hsotg_ep *hs_ep,
+                            struct usb_request *req)
+{
+       enum dma_data_direction dir;
+       struct s3c_hsotg_req *hs_req = our_req(req);
+
+       dir = hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+       /* if the length is zero, ignore the DMA data */
+       if (hs_req->req.length == 0)
+               return 0;
+
+       if (req->dma == DMA_ADDR_INVALID) {
+               dma_addr_t dma;
+
+               dma = dma_map_single(hsotg->dev, req->buf, req->length, dir);
+
+               if (unlikely(dma_mapping_error(hsotg->dev, dma)))
+                       goto dma_error;
+
+               if (dma & 3) {
+                       dev_err(hsotg->dev, "%s: unaligned dma buffer\n",
+                               __func__);
+
+                       dma_unmap_single(hsotg->dev, dma, req->length, dir);
+                       return -EINVAL;
+               }
+
+               hs_req->mapped = 1;
+               req->dma = dma;
+       } else {
+               dma_sync_single(hsotg->dev, req->dma, req->length, dir);
+               hs_req->mapped = 0;
+       }
+
+       return 0;
+
+dma_error:
+       dev_err(hsotg->dev, "%s: failed to map buffer %p, %d bytes\n",
+               __func__, req->buf, req->length);
+
+       return -EIO;
+}
+
+static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
+                             gfp_t gfp_flags)
+{
+       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",
+               ep->name, req, req->length, req->buf, req->no_interrupt,
+               req->zero, req->short_not_ok);
+
+       /* initialise status of the request */
+       INIT_LIST_HEAD(&hs_req->queue);
+       req->actual = 0;
+       req->status = -EINPROGRESS;
+
+       /* if we're using DMA, sync the buffers as necessary */
+       if (using_dma(hs)) {
+               int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
+               if (ret)
+                       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 void s3c_hsotg_ep_free_request(struct usb_ep *ep,
+                                     struct usb_request *req)
+{
+       struct s3c_hsotg_req *hs_req = our_req(req);
+
+       kfree(hs_req);
+}
+
+/**
+ * s3c_hsotg_complete_oursetup - setup completion callback
+ * @ep: The endpoint the request was on.
+ * @req: The request completed.
+ *
+ * Called on completion of any requests the driver itself
+ * submitted that need cleaning up.
+ */
+static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
+                                       struct usb_request *req)
+{
+       struct s3c_hsotg_ep *hs_ep = our_ep(ep);
+       struct s3c_hsotg *hsotg = hs_ep->parent;
+
+       dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req);
+
+       s3c_hsotg_ep_free_request(ep, req);
+}
+
+/**
+ * ep_from_windex - convert control wIndex value to endpoint
+ * @hsotg: The driver state.
+ * @windex: The control request wIndex field (in host order).
+ *
+ * Convert the given wIndex into a pointer to an driver endpoint
+ * structure, or return NULL if it is not a valid endpoint.
+*/
+static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
+                                          u32 windex)
+{
+       struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F];
+       int dir = (windex & USB_DIR_IN) ? 1 : 0;
+       int idx = windex & 0x7F;
+
+       if (windex >= 0x100)
+               return NULL;
+
+       if (idx > S3C_HSOTG_EPS)
+               return NULL;
+
+       if (idx && ep->dir_in != dir)
+               return NULL;
+
+       return ep;
+}
+
+/**
+ * s3c_hsotg_send_reply - send reply to control request
+ * @hsotg: The device state
+ * @ep: Endpoint 0
+ * @buff: Buffer for request
+ * @length: Length of reply.
+ *
+ * Create a request and queue it on the given endpoint. This is useful as
+ * an internal method of sending replies to certain control requests, etc.
+ */
+static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
+                               struct s3c_hsotg_ep *ep,
+                               void *buff,
+                               int length)
+{
+       struct usb_request *req;
+       int ret;
+
+       dev_dbg(hsotg->dev, "%s: buff %p, len %d\n", __func__, buff, length);
+
+       req = s3c_hsotg_ep_alloc_request(&ep->ep, GFP_ATOMIC);
+       hsotg->ep0_reply = req;
+       if (!req) {
+               dev_warn(hsotg->dev, "%s: cannot alloc req\n", __func__);
+               return -ENOMEM;
+       }
+
+       req->buf = hsotg->ep0_buff;
+       req->length = length;
+       req->zero = 1; /* always do zero-length final transfer */
+       req->complete = s3c_hsotg_complete_oursetup;
+
+       if (length)
+               memcpy(req->buf, buff, length);
+       else
+               ep->sent_zlp = 1;
+
+       ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
+       if (ret) {
+               dev_warn(hsotg->dev, "%s: cannot queue req\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+/**
+ * s3c_hsotg_process_req_status - process request GET_STATUS
+ * @hsotg: The device state
+ * @ctrl: USB control request
+ */
+static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg,
+                                       struct usb_ctrlrequest *ctrl)
+{
+       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       struct s3c_hsotg_ep *ep;
+       __le16 reply;
+       int ret;
+
+       dev_dbg(hsotg->dev, "%s: USB_REQ_GET_STATUS\n", __func__);
+
+       if (!ep0->dir_in) {
+               dev_warn(hsotg->dev, "%s: direction out?\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (ctrl->bRequestType & USB_RECIP_MASK) {
+       case USB_RECIP_DEVICE:
+               reply = cpu_to_le16(0); /* bit 0 => self powered,
+                                        * bit 1 => remote wakeup */
+               break;
+
+       case USB_RECIP_INTERFACE:
+               /* currently, the data result should be zero */
+               reply = cpu_to_le16(0);
+               break;
+
+       case USB_RECIP_ENDPOINT:
+               ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
+               if (!ep)
+                       return -ENOENT;
+
+               reply = cpu_to_le16(ep->halted ? 1 : 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (le16_to_cpu(ctrl->wLength) != 2)
+               return -EINVAL;
+
+       ret = s3c_hsotg_send_reply(hsotg, ep0, &reply, 2);
+       if (ret) {
+               dev_err(hsotg->dev, "%s: failed to send reply\n", __func__);
+               return ret;
+       }
+
+       return 1;
+}
+
+static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value);
+
+/**
+ * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE
+ * @hsotg: The device state
+ * @ctrl: USB control request
+ */
+static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
+                                        struct usb_ctrlrequest *ctrl)
+{
+       bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+       struct s3c_hsotg_ep *ep;
+
+       dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
+               __func__, set ? "SET" : "CLEAR");
+
+       if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+               ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
+               if (!ep) {
+                       dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
+                               __func__, le16_to_cpu(ctrl->wIndex));
+                       return -ENOENT;
+               }
+
+               switch (le16_to_cpu(ctrl->wValue)) {
+               case USB_ENDPOINT_HALT:
+                       s3c_hsotg_ep_sethalt(&ep->ep, set);
+                       break;
+
+               default:
+                       return -ENOENT;
+               }
+       } else
+               return -ENOENT;  /* currently only deal with endpoint */
+
+       return 1;
+}
+
+/**
+ * s3c_hsotg_process_control - process a control request
+ * @hsotg: The device state
+ * @ctrl: The control request received
+ *
+ * The controller has received the SETUP phase of a control request, and
+ * needs to work out what to do next (and whether to pass it on to the
+ * gadget driver).
+ */
+static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
+                                     struct usb_ctrlrequest *ctrl)
+{
+       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       int ret = 0;
+       u32 dcfg;
+
+       ep0->sent_zlp = 0;
+
+       dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n",
+                ctrl->bRequest, ctrl->bRequestType,
+                ctrl->wValue, ctrl->wLength);
+
+       /* record the direction of the request, for later use when enquing
+        * packets onto EP0. */
+
+       ep0->dir_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0;
+       dev_dbg(hsotg->dev, "ctrl: dir_in=%d\n", ep0->dir_in);
+
+       /* if we've no data with this request, then the last part of the
+        * transaction is going to implicitly be IN. */
+       if (ctrl->wLength == 0)
+               ep0->dir_in = 1;
+
+       if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+               switch (ctrl->bRequest) {
+               case USB_REQ_SET_ADDRESS:
+                       dcfg = readl(hsotg->regs + S3C_DCFG);
+                       dcfg &= ~S3C_DCFG_DevAddr_MASK;
+                       dcfg |= ctrl->wValue << S3C_DCFG_DevAddr_SHIFT;
+                       writel(dcfg, hsotg->regs + S3C_DCFG);
+
+                       dev_info(hsotg->dev, "new address %d\n", ctrl->wValue);
+
+                       ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
+                       return;
+
+               case USB_REQ_GET_STATUS:
+                       ret = s3c_hsotg_process_req_status(hsotg, ctrl);
+                       break;
+
+               case USB_REQ_CLEAR_FEATURE:
+               case USB_REQ_SET_FEATURE:
+                       ret = s3c_hsotg_process_req_feature(hsotg, ctrl);
+                       break;
+               }
+       }
+
+       /* as a fallback, try delivering it to the driver to deal with */
+
+       if (ret == 0 && hsotg->driver) {
+               ret = hsotg->driver->setup(&hsotg->gadget, ctrl);
+               if (ret < 0)
+                       dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret);
+       }
+
+       if (ret > 0) {
+               if (!ep0->dir_in) {
+                       /* need to generate zlp in reply or take data */
+                       /* todo - deal with any data we might be sent? */
+                       ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
+               }
+       }
+
+       /* the request is either unhandlable, or is not formatted correctly
+        * so respond with a STALL for the status stage to indicate failure.
+        */
+
+       if (ret < 0) {
+               u32 reg;
+               u32 ctrl;
+
+               dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
+               reg = (ep0->dir_in) ? S3C_DIEPCTL0 : S3C_DOEPCTL0;
+
+               /* S3C_DxEPCTL_Stall will be cleared by EP once it has
+                * taken effect, so no need to clear later. */
+
+               ctrl = readl(hsotg->regs + reg);
+               ctrl |= S3C_DxEPCTL_Stall;
+               ctrl |= S3C_DxEPCTL_CNAK;
+               writel(ctrl, hsotg->regs + reg);
+
+               dev_dbg(hsotg->dev,
+                       "writen DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
+                       ctrl, reg, readl(hsotg->regs + reg));
+
+               /* don't belive we need to anything more to get the EP
+                * to reply with a STALL packet */
+       }
+}
+
+static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
+
+/**
+ * s3c_hsotg_complete_setup - completion of a setup transfer
+ * @ep: The endpoint the request was on.
+ * @req: The request completed.
+ *
+ * Called on completion of any requests the driver itself submitted for
+ * EP0 setup packets
+ */
+static void s3c_hsotg_complete_setup(struct usb_ep *ep,
+                                    struct usb_request *req)
+{
+       struct s3c_hsotg_ep *hs_ep = our_ep(ep);
+       struct s3c_hsotg *hsotg = hs_ep->parent;
+
+       if (req->status < 0) {
+               dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status);
+               return;
+       }
+
+       if (req->actual == 0)
+               s3c_hsotg_enqueue_setup(hsotg);
+       else
+               s3c_hsotg_process_control(hsotg, req->buf);
+}
+
+/**
+ * s3c_hsotg_enqueue_setup - start a request for EP0 packets
+ * @hsotg: The device state.
+ *
+ * Enqueue a request on EP0 if necessary to received any SETUP packets
+ * received from the host.
+ */
+static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
+{
+       struct usb_request *req = hsotg->ctrl_req;
+       struct s3c_hsotg_req *hs_req = our_req(req);
+       int ret;
+
+       dev_dbg(hsotg->dev, "%s: queueing setup request\n", __func__);
+
+       req->zero = 0;
+       req->length = 8;
+       req->buf = hsotg->ctrl_buff;
+       req->complete = s3c_hsotg_complete_setup;
+
+       if (!list_empty(&hs_req->queue)) {
+               dev_dbg(hsotg->dev, "%s already queued???\n", __func__);
+               return;
+       }
+
+       hsotg->eps[0].dir_in = 0;
+
+       ret = s3c_hsotg_ep_queue(&hsotg->eps[0].ep, req, GFP_ATOMIC);
+       if (ret < 0) {
+               dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret);
+               /* Don't think there's much we can do other than watch the
+                * driver fail. */
+       }
+}
+
+/**
+ * get_ep_head - return the first request on the endpoint
+ * @hs_ep: The controller endpoint to get
+ *
+ * Get the first request on the endpoint.
+*/
+static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
+{
+       if (list_empty(&hs_ep->queue))
+               return NULL;
+
+       return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue);
+}
+
+/**
+ * s3c_hsotg_complete_request - complete a request given to us
+ * @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)
+ *
+ * The given request has finished, so call the necessary completion
+ * if it has one and then look to see if we can start a new request
+ * on the endpoint.
+ *
+ * Note, expects the ep to already be locked as appropriate.
+*/
+static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
+                                      struct s3c_hsotg_ep *hs_ep,
+                                      struct s3c_hsotg_req *hs_req,
+                                      int result)
+{
+       bool restart;
+
+       if (!hs_req) {
+               dev_dbg(hsotg->dev, "%s: nothing to complete?\n", __func__);
+               return;
+       }
+
+       dev_dbg(hsotg->dev, "complete: ep %p %s, req %p, %d => %p\n",
+               hs_ep, hs_ep->ep.name, hs_req, result, hs_req->req.complete);
+
+       /* only replace the status if we've not already set an error
+        * from a previous transaction */
+
+       if (hs_req->req.status == -EINPROGRESS)
+               hs_req->req.status = result;
+
+       hs_ep->req = NULL;
+       list_del_init(&hs_req->queue);
+
+       if (using_dma(hsotg))
+               s3c_hsotg_unmap_dma(hsotg, hs_ep, hs_req);
+
+       /* call the complete request with the locks off, just in case the
+        * request tries to queue more work for this endpoint. */
+
+       if (hs_req->req.complete) {
+               spin_unlock(&hs_ep->lock);
+               hs_req->req.complete(&hs_ep->ep, &hs_req->req);
+               spin_lock(&hs_ep->lock);
+       }
+
+       /* Look to see if there is anything else to do. Note, the completion
+        * of the previous request may have caused a new request to be started
+        * so be careful when doing this. */
+
+       if (!hs_ep->req && result >= 0) {
+               restart = !list_empty(&hs_ep->queue);
+               if (restart) {
+                       hs_req = get_ep_head(hs_ep);
+                       s3c_hsotg_start_req(hsotg, hs_ep, hs_req, false);
+               }
+       }
+}
+
+/**
+ * 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
+ * @size: The size of data in the fifo, in bytes
+ *
+ * The FIFO status shows there is data to read from the FIFO for a given
+ * endpoint, so sort out whether we need to read the data into a request
+ * that has been made for that endpoint.
+ */
+static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
+{
+       struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx];
+       struct s3c_hsotg_req *hs_req = hs_ep->req;
+       void __iomem *fifo = hsotg->regs + S3C_EPFIFO(ep_idx);
+       int to_read;
+       int max_req;
+       int read_ptr;
+
+       if (!hs_req) {
+               u32 epctl = readl(hsotg->regs + S3C_DOEPCTL(ep_idx));
+               int ptr;
+
+               dev_warn(hsotg->dev,
+                        "%s: FIFO %d bytes on ep%d but no req (DxEPCTl=0x%08x)\n",
+                        __func__, size, ep_idx, epctl);
+
+               /* dump the data from the FIFO, we've nothing we can do */
+               for (ptr = 0; ptr < size; ptr += 4)
+                       (void)readl(fifo);
+
+               return;
+       }
+
+       spin_lock(&hs_ep->lock);
+
+       to_read = size;
+       read_ptr = hs_req->req.actual;
+       max_req = hs_req->req.length - read_ptr;
+
+       if (to_read > max_req) {
+               /* more data appeared than we where willing
+                * to deal with in this request.
+                */
+
+               /* currently we don't deal this */
+               WARN_ON_ONCE(1);
+       }
+
+       dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
+               __func__, to_read, max_req, read_ptr, hs_req->req.length);
+
+       hs_ep->total_data += to_read;
+       hs_req->req.actual += to_read;
+       to_read = DIV_ROUND_UP(to_read, 4);
+
+       /* note, we might over-write the buffer end by 3 bytes depending on
+        * alignment of the data. */
+       readsl(fifo, hs_req->req.buf + read_ptr, to_read);
+
+       spin_unlock(&hs_ep->lock);
+}
+
+/**
+ * s3c_hsotg_send_zlp - send zero-length packet on control endpoint
+ * @hsotg: The device instance
+ * @req: The request currently on this endpoint
+ *
+ * Generate a zero-length IN packet request for terminating a SETUP
+ * transaction.
+ *
+ * Note, since we don't write any data to the TxFIFO, then it is
+ * currently belived that we do not need to wait for any space in
+ * the TxFIFO.
+ */
+static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
+                              struct s3c_hsotg_req *req)
+{
+       u32 ctrl;
+
+       if (!req) {
+               dev_warn(hsotg->dev, "%s: no request?\n", __func__);
+               return;
+       }
+
+       if (req->req.length == 0) {
+               hsotg->eps[0].sent_zlp = 1;
+               s3c_hsotg_enqueue_setup(hsotg);
+               return;
+       }
+
+       hsotg->eps[0].dir_in = 1;
+       hsotg->eps[0].sent_zlp = 1;
+
+       dev_dbg(hsotg->dev, "sending zero-length packet\n");
+
+       /* issue a zero-sized packet to terminate this */
+       writel(S3C_DxEPTSIZ_MC(1) | S3C_DxEPTSIZ_PktCnt(1) |
+              S3C_DxEPTSIZ_XferSize(0), hsotg->regs + S3C_DIEPTSIZ(0));
+
+       ctrl = readl(hsotg->regs + S3C_DIEPCTL0);
+       ctrl |= S3C_DxEPCTL_CNAK;  /* clear NAK set by core */
+       ctrl |= S3C_DxEPCTL_EPEna; /* ensure ep enabled */
+       ctrl |= S3C_DxEPCTL_USBActEp;
+       writel(ctrl, hsotg->regs + S3C_DIEPCTL0);
+}
+
+/**
+ * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
+ * @hsotg: The device instance
+ * @epnum: The endpoint received from
+ * @was_setup: Set if processing a SetupDone event.
+ *
+ * The RXFIFO has delivered an OutDone event, which means that the data
+ * transfer for an OUT endpoint has been completed, either by a short
+ * packet or by the finish of a transfer.
+*/
+static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
+                                    int epnum, bool was_setup)
+{
+       struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum];
+       struct s3c_hsotg_req *hs_req = hs_ep->req;
+       struct usb_request *req = &hs_req->req;
+       int result = 0;
+
+       if (!hs_req) {
+               dev_dbg(hsotg->dev, "%s: no request active\n", __func__);
+               return;
+       }
+
+       if (using_dma(hsotg)) {
+               u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum));
+               unsigned size_done;
+               unsigned size_left;
+
+               /* Calculate the size of the transfer by checking how much
+                * is left in the endpoint size register and then working it
+                * out from the amount we loaded for the transfer.
+                *
+                * We need to do this as DMA pointers are always 32bit aligned
+                * so may overshoot/undershoot the transfer.
+                */
+
+               size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
+
+               size_done = hs_ep->size_loaded - size_left;
+               size_done += hs_ep->last_load;
+
+               req->actual = size_done;
+       }
+
+       if (req->actual < req->length && req->short_not_ok) {
+               dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n",
+                       __func__, req->actual, req->length);
+
+               /* todo - what should we return here? there's no one else
+                * even bothering to check the status. */
+       }
+
+       if (epnum == 0) {
+               if (!was_setup && req->complete != s3c_hsotg_complete_setup)
+                       s3c_hsotg_send_zlp(hsotg, hs_req);
+       }
+
+       s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, result);
+}
+
+/**
+ * s3c_hsotg_read_frameno - read current frame number
+ * @hsotg: The device instance
+ *
+ * Return the current frame number
+*/
+static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
+{
+       u32 dsts;
+
+       dsts = readl(hsotg->regs + S3C_DSTS);
+       dsts &= S3C_DSTS_SOFFN_MASK;
+       dsts >>= S3C_DSTS_SOFFN_SHIFT;
+
+       return dsts;
+}
+
+/**
+ * s3c_hsotg_handle_rx - RX FIFO has data
+ * @hsotg: The device instance
+ *
+ * The IRQ handler has detected that the RX FIFO has some data in it
+ * that requires processing, so find out what is in there and do the
+ * appropriate read.
+ *
+ * The RXFIFO is a true FIFO, the packets comming out are still in packet
+ * chunks, so if you have x packets received on an endpoint you'll get x
+ * FIFO events delivered, each with a packet's worth of data in it.
+ *
+ * When using DMA, we should not be processing events from the RXFIFO
+ * as the actual data should be sent to the memory directly and we turn
+ * on the completion interrupts to get notifications of transfer completion.
+ */
+void s3c_hsotg_handle_rx(struct s3c_hsotg *hsotg)
+{
+       u32 grxstsr = readl(hsotg->regs + S3C_GRXSTSP);
+       u32 epnum, status, size;
+
+       WARN_ON(using_dma(hsotg));
+
+       epnum = grxstsr & S3C_GRXSTS_EPNum_MASK;
+       status = grxstsr & S3C_GRXSTS_PktSts_MASK;
+
+       size = grxstsr & S3C_GRXSTS_ByteCnt_MASK;
+       size >>= S3C_GRXSTS_ByteCnt_SHIFT;
+
+       if (1)
+               dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
+                       __func__, grxstsr, size, epnum);
+
+#define __status(x) ((x) >> S3C_GRXSTS_PktSts_SHIFT)
+
+       switch (status >> S3C_GRXSTS_PktSts_SHIFT) {
+       case __status(S3C_GRXSTS_PktSts_GlobalOutNAK):
+               dev_dbg(hsotg->dev, "GlobalOutNAK\n");
+               break;
+
+       case __status(S3C_GRXSTS_PktSts_OutDone):
+               dev_dbg(hsotg->dev, "OutDone (Frame=0x%08x)\n",
+                       s3c_hsotg_read_frameno(hsotg));
+
+               if (!using_dma(hsotg))
+                       s3c_hsotg_handle_outdone(hsotg, epnum, false);
+               break;
+
+       case __status(S3C_GRXSTS_PktSts_SetupDone):
+               dev_dbg(hsotg->dev,
+                       "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
+                       s3c_hsotg_read_frameno(hsotg),
+                       readl(hsotg->regs + S3C_DOEPCTL(0)));
+
+               s3c_hsotg_handle_outdone(hsotg, epnum, true);
+               break;
+
+       case __status(S3C_GRXSTS_PktSts_OutRX):
+               s3c_hsotg_rx_data(hsotg, epnum, size);
+               break;
+
+       case __status(S3C_GRXSTS_PktSts_SetupRX):
+               dev_dbg(hsotg->dev,
+                       "SetupRX (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
+                       s3c_hsotg_read_frameno(hsotg),
+                       readl(hsotg->regs + S3C_DOEPCTL(0)));
+
+               s3c_hsotg_rx_data(hsotg, epnum, size);
+               break;
+
+       default:
+               dev_warn(hsotg->dev, "%s: unknown status %08x\n",
+                        __func__, grxstsr);
+
+               s3c_hsotg_dump(hsotg);
+               break;
+       }
+}
+
+/**
+ * s3c_hsotg_ep0_mps - turn max packet size into register setting
+ * @mps: The maximum packet size in bytes.
+*/
+static u32 s3c_hsotg_ep0_mps(unsigned int mps)
+{
+       switch (mps) {
+       case 64:
+               return S3C_D0EPCTL_MPS_64;
+       case 32:
+               return S3C_D0EPCTL_MPS_32;
+       case 16:
+               return S3C_D0EPCTL_MPS_16;
+       case 8:
+               return S3C_D0EPCTL_MPS_8;
+       }
+
+       /* bad max packet size, warn and return invalid result */
+       WARN_ON(1);
+       return (u32)-1;
+}
+
+/**
+ * s3c_hsotg_set_ep_maxpacket - set endpoint's max-packet field
+ * @hsotg: The driver state.
+ * @ep: The index number of the endpoint
+ * @mps: The maximum packet size in bytes
+ *
+ * Configure the maximum packet size for the given endpoint, updating
+ * the hardware control registers to reflect this.
+ */
+static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
+                                      unsigned int ep, unsigned int mps)
+{
+       struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
+       void __iomem *regs = hsotg->regs;
+       u32 mpsval;
+       u32 reg;
+
+       if (ep == 0) {
+               /* EP0 is a special case */
+               mpsval = s3c_hsotg_ep0_mps(mps);
+               if (mpsval > 3)
+                       goto bad_mps;
+       } else {
+               if (mps >= S3C_DxEPCTL_MPS_LIMIT+1)
+                       goto bad_mps;
+
+               mpsval = mps;
+       }
+
+       hs_ep->ep.maxpacket = mps;
+
+       /* update both the in and out endpoint controldir_ registers, even
+        * if one of the directions may not be in use. */
+
+       reg = readl(regs + S3C_DIEPCTL(ep));
+       reg &= ~S3C_DxEPCTL_MPS_MASK;
+       reg |= mpsval;
+       writel(reg, regs + S3C_DIEPCTL(ep));
+
+       reg = readl(regs + S3C_DOEPCTL(ep));
+       reg &= ~S3C_DxEPCTL_MPS_MASK;
+       reg |= mpsval;
+       writel(reg, regs + S3C_DOEPCTL(ep));
+
+       return;
+
+bad_mps:
+       dev_err(hsotg->dev, "ep%d: bad mps of %d\n", ep, mps);
+}
+
+
+/**
+ * s3c_hsotg_trytx - check to see if anything needs transmitting
+ * @hsotg: The driver state
+ * @hs_ep: The driver endpoint to check.
+ *
+ * Check to see if there is a request that has data to send, and if so
+ * make an attempt to write data into the FIFO.
+ */
+static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
+                          struct s3c_hsotg_ep *hs_ep)
+{
+       struct s3c_hsotg_req *hs_req = hs_ep->req;
+
+       if (!hs_ep->dir_in || !hs_req)
+               return 0;
+
+       if (hs_req->req.actual < hs_req->req.length) {
+               dev_dbg(hsotg->dev, "trying to write more for ep%d\n",
+                       hs_ep->index);
+               return s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req);
+       }
+
+       return 0;
+}
+
+/**
+ * s3c_hsotg_complete_in - complete IN transfer
+ * @hsotg: The device state.
+ * @hs_ep: The endpoint that has just completed.
+ *
+ * An IN transfer has been completed, update the transfer's state and then
+ * call the relevant completion routines.
+ */
+static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
+                                 struct s3c_hsotg_ep *hs_ep)
+{
+       struct s3c_hsotg_req *hs_req = hs_ep->req;
+       u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index));
+       int size_left, size_done;
+
+       if (!hs_req) {
+               dev_dbg(hsotg->dev, "XferCompl but no req\n");
+               return;
+       }
+
+       /* Calculate the size of the transfer by checking how much is left
+        * in the endpoint size register and then working it out from
+        * the amount we loaded for the transfer.
+        *
+        * We do this even for DMA, as the transfer may have incremented
+        * past the end of the buffer (DMA transfers are always 32bit
+        * aligned).
+        */
+
+       size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
+
+       size_done = hs_ep->size_loaded - size_left;
+       size_done += hs_ep->last_load;
+
+       if (hs_req->req.actual != size_done)
+               dev_dbg(hsotg->dev, "%s: adjusting size done %d => %d\n",
+                       __func__, hs_req->req.actual, size_done);
+
+       hs_req->req.actual = size_done;
+
+       /* if we did all of the transfer, and there is more data left
+        * around, then try restarting the rest of the request */
+
+       if (!size_left && hs_req->req.actual < hs_req->req.length) {
+               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_epint - handle an in/out endpoint interrupt
+ * @hsotg: The driver state
+ * @idx: The index for the endpoint (0..15)
+ * @dir_in: Set if this is an IN endpoint
+ *
+ * Process and clear any interrupt pending for an individual endpoint
+*/
+static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
+                           int dir_in)
+{
+       struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx];
+       u32 epint_reg = dir_in ? S3C_DIEPINT(idx) : S3C_DOEPINT(idx);
+       u32 epctl_reg = dir_in ? S3C_DIEPCTL(idx) : S3C_DOEPCTL(idx);
+       u32 epsiz_reg = dir_in ? S3C_DIEPTSIZ(idx) : S3C_DOEPTSIZ(idx);
+       u32 ints;
+       u32 clear = 0;
+
+       ints = readl(hsotg->regs + epint_reg);
+
+       dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n",
+               __func__, idx, dir_in ? "in" : "out", ints);
+
+       if (ints & S3C_DxEPINT_XferCompl) {
+               dev_dbg(hsotg->dev,
+                       "%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n",
+                       __func__, readl(hsotg->regs + epctl_reg),
+                       readl(hsotg->regs + epsiz_reg));
+
+               /* we get OutDone from the FIFO, so we only need to look
+                * at completing IN requests here */
+               if (dir_in) {
+                       s3c_hsotg_complete_in(hsotg, hs_ep);
+
+                       if (idx == 0)
+                               s3c_hsotg_enqueue_setup(hsotg);
+               } else if (using_dma(hsotg)) {
+                       /* We're using DMA, we need to fire an OutDone here
+                        * as we ignore the RXFIFO. */
+
+                       s3c_hsotg_handle_outdone(hsotg, idx, false);
+               }
+
+               clear |= S3C_DxEPINT_XferCompl;
+       }
+
+       if (ints & S3C_DxEPINT_EPDisbld) {
+               dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
+               clear |= S3C_DxEPINT_EPDisbld;
+       }
+
+       if (ints & S3C_DxEPINT_AHBErr) {
+               dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__);
+               clear |= S3C_DxEPINT_AHBErr;
+       }
+
+       if (ints & S3C_DxEPINT_Setup) {  /* Setup or Timeout */
+               dev_dbg(hsotg->dev, "%s: Setup/Timeout\n",  __func__);
+
+               if (using_dma(hsotg) && idx == 0) {
+                       /* this is the notification we've received a
+                        * setup packet. In non-DMA mode we'd get this
+                        * from the RXFIFO, instead we need to process
+                        * the setup here. */
+
+                       if (dir_in)
+                               WARN_ON_ONCE(1);
+                       else
+                               s3c_hsotg_handle_outdone(hsotg, 0, true);
+               }
+
+               clear |= S3C_DxEPINT_Setup;
+       }
+
+       if (ints & S3C_DxEPINT_Back2BackSetup) {
+               dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__);
+               clear |= S3C_DxEPINT_Back2BackSetup;
+       }
+
+       if (dir_in) {
+               /* not sure if this is important, but we'll clear it anyway
+                */
+               if (ints & S3C_DIEPMSK_INTknTXFEmpMsk) {
+                       dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n",
+                               __func__, idx);
+                       clear |= S3C_DIEPMSK_INTknTXFEmpMsk;
+               }
+
+               /* this probably means something bad is happening */
+               if (ints & S3C_DIEPMSK_INTknEPMisMsk) {
+                       dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n",
+                                __func__, idx);
+                       clear |= S3C_DIEPMSK_INTknEPMisMsk;
+               }
+       }
+
+       writel(clear, hsotg->regs + epint_reg);
+}
+
+/**
+ * s3c_hsotg_irq_enumdone - Handle EnumDone interrupt (enumeration done)
+ * @hsotg: The device state.
+ *
+ * Handle updating the device settings after the enumeration phase has
+ * been completed.
+*/
+static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
+{
+       u32 dsts = readl(hsotg->regs + S3C_DSTS);
+       int ep0_mps = 0, ep_mps;
+
+       /* This should signal the finish of the enumeration phase
+        * of the USB handshaking, so we should now know what rate
+        * we connected at. */
+
+       dev_dbg(hsotg->dev, "EnumDone (DSTS=0x%08x)\n", dsts);
+
+       /* note, since we're limited by the size of transfer on EP0, and
+        * it seems IN transfers must be a even number of packets we do
+        * not advertise a 64byte MPS on EP0. */
+
+       /* catch both EnumSpd_FS and EnumSpd_FS48 */
+       switch (dsts & S3C_DSTS_EnumSpd_MASK) {
+       case S3C_DSTS_EnumSpd_FS:
+       case S3C_DSTS_EnumSpd_FS48:
+               hsotg->gadget.speed = USB_SPEED_FULL;
+               dev_info(hsotg->dev, "new device is full-speed\n");
+
+               ep0_mps = EP0_MPS_LIMIT;
+               ep_mps = 64;
+               break;
+
+       case S3C_DSTS_EnumSpd_HS:
+               dev_info(hsotg->dev, "new device is high-speed\n");
+               hsotg->gadget.speed = USB_SPEED_HIGH;
+
+               ep0_mps = EP0_MPS_LIMIT;
+               ep_mps = 512;
+               break;
+
+       case S3C_DSTS_EnumSpd_LS:
+               hsotg->gadget.speed = USB_SPEED_LOW;
+               dev_info(hsotg->dev, "new device is low-speed\n");
+
+               /* note, we don't actually support LS in this driver at the
+                * moment, and the documentation seems to imply that it isn't
+                * supported by the PHYs on some of the devices.
+                */
+               break;
+       }
+
+       /* we should now know the maximum packet size for an
+        * endpoint, so set the endpoints to a default value. */
+
+       if (ep0_mps) {
+               int i;
+               s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps);
+               for (i = 1; i < S3C_HSOTG_EPS; i++)
+                       s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps);
+       }
+
+       /* ensure after enumeration our EP0 is active */
+
+       s3c_hsotg_enqueue_setup(hsotg);
+
+       dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
+               readl(hsotg->regs + S3C_DIEPCTL0),
+               readl(hsotg->regs + S3C_DOEPCTL0));
+}
+
+/**
+ * kill_all_requests - remove all requests from the endpoint's queue
+ * @hsotg: The device state.
+ * @ep: The endpoint the requests may be on.
+ * @result: The result code to use.
+ * @force: Force removal of any current requests
+ *
+ * Go through the requests on the given endpoint and mark them
+ * completed with the given result code.
+ */
+static void kill_all_requests(struct s3c_hsotg *hsotg,
+                             struct s3c_hsotg_ep *ep,
+                             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) {
+               /* currently, we can't do much about an already
+                * running request on an in endpoint */
+
+               if (ep->req == req && ep->dir_in && !force)
+                       continue;
+
+               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);
+
+/**
+ * s3c_hsotg_disconnect_irq - disconnect irq service
+ * @hsotg: The device state.
+ *
+ * A disconnect IRQ has been received, meaning that the host has
+ * lost contact with the bus. Remove all current transactions
+ * and signal the gadget driver that this has happened.
+*/
+static void s3c_hsotg_disconnect_irq(struct s3c_hsotg *hsotg)
+{
+       unsigned ep;
+
+       for (ep = 0; ep < S3C_HSOTG_EPS; ep++)
+               kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true);
+
+       call_gadget(hsotg, disconnect);
+}
+
+/**
+ * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
+ * @hsotg: The device state:
+ * @periodic: True if this is a periodic FIFO interrupt
+ */
+static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
+{
+       struct s3c_hsotg_ep *ep;
+       int epno, ret;
+
+       /* look through for any more data to transmit */
+
+       for (epno = 0; epno < S3C_HSOTG_EPS; epno++) {
+               ep = &hsotg->eps[epno];
+
+               if (!ep->dir_in)
+                       continue;
+
+               if ((periodic && !ep->periodic) ||
+                   (!periodic && ep->periodic))
+                       continue;
+
+               ret = s3c_hsotg_trytx(hsotg, ep);
+               if (ret < 0)
+                       break;
+       }
+}
+
+static struct s3c_hsotg *our_hsotg;
+
+/* IRQ flags which will trigger a retry around the IRQ loop */
+#define IRQ_RETRY_MASK (S3C_GINTSTS_NPTxFEmp | \
+                       S3C_GINTSTS_PTxFEmp |  \
+                       S3C_GINTSTS_RxFLvl)
+
+/**
+ * s3c_hsotg_irq - handle device interrupt
+ * @irq: The IRQ number triggered
+ * @pw: The pw value when registered the handler.
+ */
+static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
+{
+       struct s3c_hsotg *hsotg = pw;
+       int retry_count = 8;
+       u32 gintsts;
+       u32 gintmsk;
+
+irq_retry:
+       gintsts = readl(hsotg->regs + S3C_GINTSTS);
+       gintmsk = readl(hsotg->regs + S3C_GINTMSK);
+
+       dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n",
+               __func__, gintsts, gintsts & gintmsk, gintmsk, retry_count);
+
+       gintsts &= gintmsk;
+
+       if (gintsts & S3C_GINTSTS_OTGInt) {
+               u32 otgint = readl(hsotg->regs + S3C_GOTGINT);
+
+               dev_info(hsotg->dev, "OTGInt: %08x\n", otgint);
+
+               writel(otgint, hsotg->regs + S3C_GOTGINT);
+               writel(S3C_GINTSTS_OTGInt, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_DisconnInt) {
+               dev_dbg(hsotg->dev, "%s: DisconnInt\n", __func__);
+               writel(S3C_GINTSTS_DisconnInt, hsotg->regs + S3C_GINTSTS);
+
+               s3c_hsotg_disconnect_irq(hsotg);
+       }
+
+       if (gintsts & S3C_GINTSTS_SessReqInt) {
+               dev_dbg(hsotg->dev, "%s: SessReqInt\n", __func__);
+               writel(S3C_GINTSTS_SessReqInt, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_EnumDone) {
+               s3c_hsotg_irq_enumdone(hsotg);
+               writel(S3C_GINTSTS_EnumDone, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_ConIDStsChng) {
+               dev_dbg(hsotg->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n",
+                       readl(hsotg->regs + S3C_DSTS),
+                       readl(hsotg->regs + S3C_GOTGCTL));
+
+               writel(S3C_GINTSTS_ConIDStsChng, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & (S3C_GINTSTS_OEPInt | S3C_GINTSTS_IEPInt)) {
+               u32 daint = readl(hsotg->regs + S3C_DAINT);
+               u32 daint_out = daint >> S3C_DAINT_OutEP_SHIFT;
+               u32 daint_in = daint & ~(daint_out << S3C_DAINT_OutEP_SHIFT);
+               int ep;
+
+               dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
+
+               for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) {
+                       if (daint_out & 1)
+                               s3c_hsotg_epint(hsotg, ep, 0);
+               }
+
+               for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) {
+                       if (daint_in & 1)
+                               s3c_hsotg_epint(hsotg, ep, 1);
+               }
+
+               writel(daint, hsotg->regs + S3C_DAINT);
+               writel(gintsts & (S3C_GINTSTS_OEPInt | S3C_GINTSTS_IEPInt),
+                      hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_USBRst) {
+               dev_info(hsotg->dev, "%s: USBRst\n", __func__);
+               dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n",
+                       readl(hsotg->regs + S3C_GNPTXSTS));
+
+               kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true);
+
+               /* it seems after a reset we can end up with a situation
+                * where the TXFIFO still has data in it... try flushing
+                * it to remove anything that may still be in it.
+                */
+
+               if (1) {
+                       writel(S3C_GRSTCTL_TxFNum(0) | S3C_GRSTCTL_TxFFlsh,
+                              hsotg->regs + S3C_GRSTCTL);
+
+                       dev_info(hsotg->dev, "GNPTXSTS=%08x\n",
+                                readl(hsotg->regs + S3C_GNPTXSTS));
+               }
+
+               s3c_hsotg_enqueue_setup(hsotg);
+
+               writel(S3C_GINTSTS_USBRst, hsotg->regs + S3C_GINTSTS);
+       }
+
+       /* check both FIFOs */
+
+       if (gintsts & S3C_GINTSTS_NPTxFEmp) {
+               dev_dbg(hsotg->dev, "NPTxFEmp\n");
+
+               /* Disable the interrupt to stop it happening again
+                * unless one of these endpoint routines decides that
+                * it needs re-enabling */
+
+               s3c_hsotg_disable_gsint(hsotg, S3C_GINTSTS_NPTxFEmp);
+               s3c_hsotg_irq_fifoempty(hsotg, false);
+
+               writel(S3C_GINTSTS_NPTxFEmp, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_PTxFEmp) {
+               dev_dbg(hsotg->dev, "PTxFEmp\n");
+
+               /* See note in S3C_GINTSTS_NPTxFEmp */
+
+               s3c_hsotg_disable_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
+               s3c_hsotg_irq_fifoempty(hsotg, true);
+
+               writel(S3C_GINTSTS_PTxFEmp, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_RxFLvl) {
+               /* note, since GINTSTS_RxFLvl doubles as FIFO-not-empty,
+                * we need to retry s3c_hsotg_handle_rx if this is still
+                * set. */
+
+               s3c_hsotg_handle_rx(hsotg);
+               writel(S3C_GINTSTS_RxFLvl, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_ModeMis) {
+               dev_warn(hsotg->dev, "warning, mode mismatch triggered\n");
+               writel(S3C_GINTSTS_ModeMis, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_USBSusp) {
+               dev_info(hsotg->dev, "S3C_GINTSTS_USBSusp\n");
+               writel(S3C_GINTSTS_USBSusp, hsotg->regs + S3C_GINTSTS);
+
+               call_gadget(hsotg, suspend);
+       }
+
+       if (gintsts & S3C_GINTSTS_WkUpInt) {
+               dev_info(hsotg->dev, "S3C_GINTSTS_WkUpIn\n");
+               writel(S3C_GINTSTS_WkUpInt, hsotg->regs + S3C_GINTSTS);
+
+               call_gadget(hsotg, resume);
+       }
+
+       if (gintsts & S3C_GINTSTS_ErlySusp) {
+               dev_dbg(hsotg->dev, "S3C_GINTSTS_ErlySusp\n");
+               writel(S3C_GINTSTS_ErlySusp, hsotg->regs + S3C_GINTSTS);
+       }
+
+       /* these next two seem to crop-up occasionally causing the core
+        * to shutdown the USB transfer, so try clearing them and logging
+        * the occurence. */
+
+       if (gintsts & S3C_GINTSTS_GOUTNakEff) {
+               dev_info(hsotg->dev, "GOUTNakEff triggered\n");
+
+               s3c_hsotg_dump(hsotg);
+
+               writel(S3C_DCTL_CGOUTNak, hsotg->regs + S3C_DCTL);
+               writel(S3C_GINTSTS_GOUTNakEff, hsotg->regs + S3C_GINTSTS);
+       }
+
+       if (gintsts & S3C_GINTSTS_GINNakEff) {
+               dev_info(hsotg->dev, "GINNakEff triggered\n");
+
+               s3c_hsotg_dump(hsotg);
+
+               writel(S3C_DCTL_CGNPInNAK, hsotg->regs + S3C_DCTL);
+               writel(S3C_GINTSTS_GINNakEff, hsotg->regs + S3C_GINTSTS);
+       }
+
+       /* if we've had fifo events, we should try and go around the
+        * loop again to see if there's any point in returning yet. */
+
+       if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
+                       goto irq_retry;
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * s3c_hsotg_ep_enable - enable the given endpoint
+ * @ep: The USB endpint to configure
+ * @desc: The USB endpoint descriptor to configure with.
+ *
+ * This is called from the USB gadget code's usb_ep_enable().
+*/
+static int s3c_hsotg_ep_enable(struct usb_ep *ep,
+                              const struct usb_endpoint_descriptor *desc)
+{
+       struct s3c_hsotg_ep *hs_ep = our_ep(ep);
+       struct s3c_hsotg *hsotg = hs_ep->parent;
+       unsigned long flags;
+       int index = hs_ep->index;
+       u32 epctrl_reg;
+       u32 epctrl;
+       u32 mps;
+       int dir_in;
+
+       dev_dbg(hsotg->dev,
+               "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n",
+               __func__, ep->name, desc->bEndpointAddress, desc->bmAttributes,
+               desc->wMaxPacketSize, desc->bInterval);
+
+       /* not to be called for EP0 */
+       WARN_ON(index == 0);
+
+       dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
+       if (dir_in != hs_ep->dir_in) {
+               dev_err(hsotg->dev, "%s: direction mismatch!\n", __func__);
+               return -EINVAL;
+       }
+
+       mps = le16_to_cpu(desc->wMaxPacketSize);
+
+       /* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */
+
+       epctrl_reg = dir_in ? S3C_DIEPCTL(index) : S3C_DOEPCTL(index);
+       epctrl = readl(hsotg->regs + epctrl_reg);
+
+       dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
+               __func__, epctrl, epctrl_reg);
+
+       spin_lock_irqsave(&hs_ep->lock, flags);
+
+       epctrl &= ~(S3C_DxEPCTL_EPType_MASK | S3C_DxEPCTL_MPS_MASK);
+       epctrl |= S3C_DxEPCTL_MPS(mps);
+
+       /* mark the endpoint as active, otherwise the core may ignore
+        * transactions entirely for this endpoint */
+       epctrl |= S3C_DxEPCTL_USBActEp;
+
+       /* set the NAK status on the endpoint, otherwise we might try and
+        * do something with data that we've yet got a request to process
+        * since the RXFIFO will take data for an endpoint even if the
+        * size register hasn't been set.
+        */
+
+       epctrl |= S3C_DxEPCTL_SNAK;
+
+       /* update the endpoint state */
+       hs_ep->ep.maxpacket = mps;
+
+       /* default, set to non-periodic */
+       hs_ep->periodic = 0;
+
+       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       case USB_ENDPOINT_XFER_ISOC:
+               dev_err(hsotg->dev, "no current ISOC support\n");
+               return -EINVAL;
+
+       case USB_ENDPOINT_XFER_BULK:
+               epctrl |= S3C_DxEPCTL_EPType_Bulk;
+               break;
+
+       case USB_ENDPOINT_XFER_INT:
+               if (dir_in) {
+                       /* Allocate our TxFNum by simply using the index
+                        * of the endpoint for the moment. We could do
+                        * something better if the host indicates how
+                        * many FIFOs we are expecting to use. */
+
+                       hs_ep->periodic = 1;
+                       epctrl |= S3C_DxEPCTL_TxFNum(index);
+               }
+
+               epctrl |= S3C_DxEPCTL_EPType_Intterupt;
+               break;
+
+       case USB_ENDPOINT_XFER_CONTROL:
+               epctrl |= S3C_DxEPCTL_EPType_Control;
+               break;
+       }
+
+       /* for non control endpoints, set PID to D0 */
+       if (index)
+               epctrl |= S3C_DxEPCTL_SetD0PID;
+
+       dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n",
+               __func__, epctrl);
+
+       writel(epctrl, hsotg->regs + epctrl_reg);
+       dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n",
+               __func__, readl(hsotg->regs + epctrl_reg));
+
+       /* enable the endpoint interrupt */
+       s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
+
+       spin_unlock_irqrestore(&hs_ep->lock, flags);
+       return 0;
+}
+
+static int s3c_hsotg_ep_disable(struct usb_ep *ep)
+{
+       struct s3c_hsotg_ep *hs_ep = our_ep(ep);
+       struct s3c_hsotg *hsotg = hs_ep->parent;
+       int dir_in = hs_ep->dir_in;
+       int index = hs_ep->index;
+       unsigned long flags;
+       u32 epctrl_reg;
+       u32 ctrl;
+
+       dev_info(hsotg->dev, "%s(ep %p)\n", __func__, ep);
+
+       if (ep == &hsotg->eps[0].ep) {
+               dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
+               return -EINVAL;
+       }
+
+       epctrl_reg = dir_in ? S3C_DIEPCTL(index) : S3C_DOEPCTL(index);
+
+       /* 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 &= ~S3C_DxEPCTL_EPEna;
+       ctrl &= ~S3C_DxEPCTL_USBActEp;
+       ctrl |= S3C_DxEPCTL_SNAK;
+
+       dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
+       writel(ctrl, hsotg->regs + epctrl_reg);
+
+       /* disable endpoint interrupts */
+       s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
+
+       spin_unlock_irqrestore(&hs_ep->lock, flags);
+       return 0;
+}
+
+/**
+ * on_list - check request is on the given endpoint
+ * @ep: The endpoint to check.
+ * @test: The request to test if it is on the endpoint.
+*/
+static bool on_list(struct s3c_hsotg_ep *ep, struct s3c_hsotg_req *test)
+{
+       struct s3c_hsotg_req *req, *treq;
+
+       list_for_each_entry_safe(req, treq, &ep->queue, queue) {
+               if (req == test)
+                       return true;
+       }
+
+       return false;
+}
+
+static int s3c_hsotg_ep_dequeue(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 flags;
+
+       dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
+
+       if (hs_req == hs_ep->req) {
+               dev_dbg(hs->dev, "%s: already in progress\n", __func__);
+               return -EINPROGRESS;
+       }
+
+       spin_lock_irqsave(&hs_ep->lock, flags);
+
+       if (!on_list(hs_ep, hs_req)) {
+               spin_unlock_irqrestore(&hs_ep->lock, flags);
+               return -EINVAL;
+       }
+
+       s3c_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET);
+       spin_unlock_irqrestore(&hs_ep->lock, flags);
+
+       return 0;
+}
+
+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;
+
+       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 = S3C_DIEPCTL(index);
+       epctl = readl(hs->regs + epreg);
+
+       if (value)
+               epctl |= S3C_DxEPCTL_Stall;
+       else
+               epctl &= ~S3C_DxEPCTL_Stall;
+
+       writel(epctl, hs->regs + epreg);
+
+       epreg = S3C_DOEPCTL(index);
+       epctl = readl(hs->regs + epreg);
+
+       if (value)
+               epctl |= S3C_DxEPCTL_Stall;
+       else
+               epctl &= ~S3C_DxEPCTL_Stall;
+
+       writel(epctl, hs->regs + epreg);
+
+       spin_unlock_irqrestore(&hs_ep->lock, irqflags);
+
+       return 0;
+}
+
+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,
+       .dequeue        = s3c_hsotg_ep_dequeue,
+       .set_halt       = s3c_hsotg_ep_sethalt,
+       /* note, don't belive we have any call for the fifo routines */
+};
+
+/**
+ * s3c_hsotg_corereset - issue softreset to the core
+ * @hsotg: The device state
+ *
+ * Issue a soft reset to the core, and await the core finishing it.
+*/
+static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
+{
+       int timeout;
+       u32 grstctl;
+
+       dev_dbg(hsotg->dev, "resetting core\n");
+
+       /* issue soft reset */
+       writel(S3C_GRSTCTL_CSftRst, hsotg->regs + S3C_GRSTCTL);
+
+       timeout = 1000;
+       do {
+               grstctl = readl(hsotg->regs + S3C_GRSTCTL);
+       } while (!(grstctl & S3C_GRSTCTL_CSftRst) && timeout-- > 0);
+
+       if (!grstctl & S3C_GRSTCTL_CSftRst) {
+               dev_err(hsotg->dev, "Failed to get CSftRst asserted\n");
+               return -EINVAL;
+       }
+
+       timeout = 1000;
+
+       while (1) {
+               u32 grstctl = readl(hsotg->regs + S3C_GRSTCTL);
+
+               if (timeout-- < 0) {
+                       dev_info(hsotg->dev,
+                                "%s: reset failed, GRSTCTL=%08x\n",
+                                __func__, grstctl);
+                       return -ETIMEDOUT;
+               }
+
+               if (grstctl & S3C_GRSTCTL_CSftRst)
+                       continue;
+
+               if (!(grstctl & S3C_GRSTCTL_AHBIdle))
+                       continue;
+
+               break;          /* reset done */
+       }
+
+       dev_dbg(hsotg->dev, "reset successful\n");
+       return 0;
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+       struct s3c_hsotg *hsotg = our_hsotg;
+       int ret;
+
+       if (!hsotg) {
+               printk(KERN_ERR "%s: called with no device\n", __func__);
+               return -ENODEV;
+       }
+
+       if (!driver) {
+               dev_err(hsotg->dev, "%s: no driver\n", __func__);
+               return -EINVAL;
+       }
+
+       if (driver->speed != USB_SPEED_HIGH &&
+           driver->speed != USB_SPEED_FULL) {
+               dev_err(hsotg->dev, "%s: bad speed\n", __func__);
+       }
+
+       if (!driver->bind || !driver->setup) {
+               dev_err(hsotg->dev, "%s: missing entry points\n", __func__);
+               return -EINVAL;
+       }
+
+       WARN_ON(hsotg->driver);
+
+       driver->driver.bus = NULL;
+       hsotg->driver = driver;
+       hsotg->gadget.dev.driver = &driver->driver;
+       hsotg->gadget.dev.dma_mask = hsotg->dev->dma_mask;
+       hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+
+       ret = device_add(&hsotg->gadget.dev);
+       if (ret) {
+               dev_err(hsotg->dev, "failed to register gadget device\n");
+               goto err;
+       }
+
+       ret = driver->bind(&hsotg->gadget);
+       if (ret) {
+               dev_err(hsotg->dev, "failed bind %s\n", driver->driver.name);
+
+               hsotg->gadget.dev.driver = NULL;
+               hsotg->driver = NULL;
+               goto err;
+       }
+
+       /* we must now enable ep0 ready for host detection and then
+        * set configuration. */
+
+       s3c_hsotg_corereset(hsotg);
+
+       /* set the PLL on, remove the HNP/SRP and set the PHY */
+       writel(S3C_GUSBCFG_PHYIf16 | S3C_GUSBCFG_TOutCal(7) |
+              (0x5 << 10), hsotg->regs + S3C_GUSBCFG);
+
+       /* looks like soft-reset changes state of FIFOs */
+       s3c_hsotg_init_fifo(hsotg);
+
+       __orr32(hsotg->regs + S3C_DCTL, S3C_DCTL_SftDiscon);
+
+       writel(1 << 18 | S3C_DCFG_DevSpd_HS,  hsotg->regs + S3C_DCFG);
+
+       writel(S3C_GINTSTS_DisconnInt | S3C_GINTSTS_SessReqInt |
+              S3C_GINTSTS_ConIDStsChng | S3C_GINTSTS_USBRst |
+              S3C_GINTSTS_EnumDone | S3C_GINTSTS_OTGInt |
+              S3C_GINTSTS_USBSusp | S3C_GINTSTS_WkUpInt |
+              S3C_GINTSTS_GOUTNakEff | S3C_GINTSTS_GINNakEff |
+              S3C_GINTSTS_ErlySusp,
+              hsotg->regs + S3C_GINTMSK);
+
+       if (using_dma(hsotg))
+               writel(S3C_GAHBCFG_GlblIntrEn | S3C_GAHBCFG_DMAEn |
+                      S3C_GAHBCFG_HBstLen_Incr4,
+                      hsotg->regs + S3C_GAHBCFG);
+       else
+               writel(S3C_GAHBCFG_GlblIntrEn, hsotg->regs + S3C_GAHBCFG);
+
+       /* Enabling INTknTXFEmpMsk here seems to be a big mistake, we end
+        * up being flooded with interrupts if the host is polling the
+        * endpoint to try and read data. */
+
+       writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
+              S3C_DIEPMSK_INTknEPMisMsk |
+              S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk,
+              hsotg->regs + S3C_DIEPMSK);
+
+       /* don't need XferCompl, we get that from RXFIFO in slave mode. In
+        * DMA mode we may need this. */
+       writel(S3C_DOEPMSK_SetupMsk | S3C_DOEPMSK_AHBErrMsk |
+              S3C_DOEPMSK_EPDisbldMsk |
+              using_dma(hsotg) ? (S3C_DIEPMSK_XferComplMsk |
+                                  S3C_DIEPMSK_TimeOUTMsk) : 0,
+              hsotg->regs + S3C_DOEPMSK);
+
+       writel(0, hsotg->regs + S3C_DAINTMSK);
+
+       dev_info(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
+                readl(hsotg->regs + S3C_DIEPCTL0),
+                readl(hsotg->regs + S3C_DOEPCTL0));
+
+       /* enable in and out endpoint interrupts */
+       s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_OEPInt | S3C_GINTSTS_IEPInt);
+
+       /* Enable the RXFIFO when in slave mode, as this is how we collect
+        * the data. In DMA mode, we get events from the FIFO but also
+        * things we cannot process, so do not use it. */
+       if (!using_dma(hsotg))
+               s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_RxFLvl);
+
+       /* Enable interrupts for EP0 in and out */
+       s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1);
+       s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1);
+
+       __orr32(hsotg->regs + S3C_DCTL, S3C_DCTL_PWROnPrgDone);
+       udelay(10);  /* see openiboot */
+       __bic32(hsotg->regs + S3C_DCTL, S3C_DCTL_PWROnPrgDone);
+
+       dev_info(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + S3C_DCTL));
+
+       /* S3C_DxEPCTL_USBActEp says RO in manual, but seems to be set by
+          writing to the EPCTL register.. */
+
+       /* set to read 1 8byte packet */
+       writel(S3C_DxEPTSIZ_MC(1) | S3C_DxEPTSIZ_PktCnt(1) |
+              S3C_DxEPTSIZ_XferSize(8), hsotg->regs + DOEPTSIZ0);
+
+       writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
+              S3C_DxEPCTL_CNAK | S3C_DxEPCTL_EPEna |
+              S3C_DxEPCTL_USBActEp,
+              hsotg->regs + S3C_DOEPCTL0);
+
+       /* enable, but don't activate EP0in */
+       writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
+              S3C_DxEPCTL_USBActEp, hsotg->regs + S3C_DIEPCTL0);
+
+       s3c_hsotg_enqueue_setup(hsotg);
+
+       dev_info(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
+                readl(hsotg->regs + S3C_DIEPCTL0),
+                readl(hsotg->regs + S3C_DOEPCTL0));
+
+       /* clear global NAKs */
+       writel(S3C_DCTL_CGOUTNak | S3C_DCTL_CGNPInNAK,
+              hsotg->regs + S3C_DCTL);
+
+       /* remove the soft-disconnect and let's go */
+       __bic32(hsotg->regs + S3C_DCTL, S3C_DCTL_SftDiscon);
+
+       /* report to the user, and return */
+
+       dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
+       return 0;
+
+err:
+       hsotg->driver = NULL;
+       hsotg->gadget.dev.driver = NULL;
+       return ret;
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+       struct s3c_hsotg *hsotg = our_hsotg;
+       int ep;
+
+       if (!hsotg)
+               return -ENODEV;
+
+       if (!driver || driver != hsotg->driver || !driver->unbind)
+               return -EINVAL;
+
+       /* all endpoints should be shutdown */
+       for (ep = 0; ep < S3C_HSOTG_EPS; ep++)
+               s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
+
+       call_gadget(hsotg, disconnect);
+
+       driver->unbind(&hsotg->gadget);
+       hsotg->driver = NULL;
+       hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+
+       device_del(&hsotg->gadget.dev);
+
+       dev_info(hsotg->dev, "unregistered gadget driver '%s'\n",
+                driver->driver.name);
+
+       return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
+{
+       return s3c_hsotg_read_frameno(to_hsotg(gadget));
+}
+
+static struct usb_gadget_ops s3c_hsotg_gadget_ops = {
+       .get_frame      = s3c_hsotg_gadget_getframe,
+};
+
+/**
+ * s3c_hsotg_initep - initialise a single endpoint
+ * @hsotg: The device state.
+ * @hs_ep: The endpoint to be initialised.
+ * @epnum: The endpoint number
+ *
+ * Initialise the given endpoint (as part of the probe and device state
+ * creation) to give to the gadget driver. Setup the endpoint name, any
+ * direction information and other state that may be required.
+ */
+static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
+                                      struct s3c_hsotg_ep *hs_ep,
+                                      int epnum)
+{
+       u32 ptxfifo;
+       char *dir;
+
+       if (epnum == 0)
+               dir = "";
+       else if ((epnum % 2) == 0) {
+               dir = "out";
+       } else {
+               dir = "in";
+               hs_ep->dir_in = 1;
+       }
+
+       hs_ep->index = epnum;
+
+       snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir);
+
+       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);
+
+       hs_ep->parent = hsotg;
+       hs_ep->ep.name = hs_ep->name;
+       hs_ep->ep.maxpacket = epnum ? 512 : EP0_MPS_LIMIT;
+       hs_ep->ep.ops = &s3c_hsotg_ep_ops;
+
+       /* Read the FIFO size for the Periodic TX FIFO, even if we're
+        * an OUT endpoint, we may as well do this if in future the
+        * code is changed to make each endpoint's direction changeable.
+        */
+
+       ptxfifo = readl(hsotg->regs + S3C_DPTXFSIZn(epnum));
+       hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo);
+
+       /* if we're using dma, we need to set the next-endpoint pointer
+        * to be something valid.
+        */
+
+       if (using_dma(hsotg)) {
+               u32 next = S3C_DxEPCTL_NextEp((epnum + 1) % 15);
+               writel(next, hsotg->regs + S3C_DIEPCTL(epnum));
+               writel(next, hsotg->regs + S3C_DOEPCTL(epnum));
+       }
+}
+
+/**
+ * s3c_hsotg_otgreset - reset the OtG phy block
+ * @hsotg: The host state.
+ *
+ * Power up the phy, set the basic configuration and start the PHY.
+ */
+static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg)
+{
+       u32 osc;
+
+       writel(0, S3C_PHYPWR);
+       mdelay(1);
+
+       osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0;
+
+       writel(osc | 0x10, S3C_PHYCLK);
+
+       /* issue a full set of resets to the otg and core */
+
+       writel(S3C_RSTCON_PHY, S3C_RSTCON);
+       udelay(20);     /* at-least 10uS */
+       writel(0, S3C_RSTCON);
+}
+
+
+static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
+{
+       /* unmask subset of endpoint interrupts */
+
+       writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
+              S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk,
+              hsotg->regs + S3C_DIEPMSK);
+
+       writel(S3C_DOEPMSK_SetupMsk | S3C_DOEPMSK_AHBErrMsk |
+              S3C_DOEPMSK_EPDisbldMsk | S3C_DOEPMSK_XferComplMsk,
+              hsotg->regs + S3C_DOEPMSK);
+
+       writel(0, hsotg->regs + S3C_DAINTMSK);
+
+       if (0) {
+               /* post global nak until we're ready */
+               writel(S3C_DCTL_SGNPInNAK | S3C_DCTL_SGOUTNak,
+                      hsotg->regs + S3C_DCTL);
+       }
+
+       /* setup fifos */
+
+       dev_info(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
+                readl(hsotg->regs + S3C_GRXFSIZ),
+                readl(hsotg->regs + S3C_GNPTXFSIZ));
+
+       s3c_hsotg_init_fifo(hsotg);
+
+       /* set the PLL on, remove the HNP/SRP and set the PHY */
+       writel(S3C_GUSBCFG_PHYIf16 | S3C_GUSBCFG_TOutCal(7) | (0x5 << 10),
+              hsotg->regs + S3C_GUSBCFG);
+
+       writel(using_dma(hsotg) ? S3C_GAHBCFG_DMAEn : 0x0,
+              hsotg->regs + S3C_GAHBCFG);
+}
+
+static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
+{
+       struct device *dev = hsotg->dev;
+       void __iomem *regs = hsotg->regs;
+       u32 val;
+       int idx;
+
+       dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n",
+                readl(regs + S3C_DCFG), readl(regs + S3C_DCTL),
+                readl(regs + S3C_DIEPMSK));
+
+       dev_info(dev, "GAHBCFG=0x%08x, 0x44=0x%08x\n",
+                readl(regs + S3C_GAHBCFG), readl(regs + 0x44));
+
+       dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
+                readl(regs + S3C_GRXFSIZ), readl(regs + S3C_GNPTXFSIZ));
+
+       /* show periodic fifo settings */
+
+       for (idx = 1; idx <= 15; idx++) {
+               val = readl(regs + S3C_DPTXFSIZn(idx));
+               dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
+                        val >> S3C_DPTXFSIZn_DPTxFSize_SHIFT,
+                        val & S3C_DPTXFSIZn_DPTxFStAddr_MASK);
+       }
+
+       for (idx = 0; idx < 15; idx++) {
+               dev_info(dev,
+                        "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
+                        readl(regs + S3C_DIEPCTL(idx)),
+                        readl(regs + S3C_DIEPTSIZ(idx)),
+                        readl(regs + S3C_DIEPDMA(idx)));
+
+               val = readl(regs + S3C_DOEPCTL(idx));
+               dev_info(dev,
+                        "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n",
+                        idx, readl(regs + S3C_DOEPCTL(idx)),
+                        readl(regs + S3C_DOEPTSIZ(idx)),
+                        readl(regs + S3C_DOEPDMA(idx)));
+
+       }
+
+       dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n",
+                readl(regs + S3C_DVBUSDIS), readl(regs + S3C_DVBUSPULSE));
+}
+
+
+/**
+ * state_show - debugfs: show overall driver and device state.
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry shows the overall state of the hardware and
+ * some general information about each of the endpoints available
+ * to the system.
+ */
+static int state_show(struct seq_file *seq, void *v)
+{
+       struct s3c_hsotg *hsotg = seq->private;
+       void __iomem *regs = hsotg->regs;
+       int idx;
+
+       seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n",
+                readl(regs + S3C_DCFG),
+                readl(regs + S3C_DCTL),
+                readl(regs + S3C_DSTS));
+
+       seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n",
+                  readl(regs + S3C_DIEPMSK), readl(regs + S3C_DOEPMSK));
+
+       seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n",
+                  readl(regs + S3C_GINTMSK),
+                  readl(regs + S3C_GINTSTS));
+
+       seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n",
+                  readl(regs + S3C_DAINTMSK),
+                  readl(regs + S3C_DAINT));
+
+       seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n",
+                  readl(regs + S3C_GNPTXSTS),
+                  readl(regs + S3C_GRXSTSR));
+
+       seq_printf(seq, "\nEndpoint status:\n");
+
+       for (idx = 0; idx < 15; idx++) {
+               u32 in, out;
+
+               in = readl(regs + S3C_DIEPCTL(idx));
+               out = readl(regs + S3C_DOEPCTL(idx));
+
+               seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x",
+                          idx, in, out);
+
+               in = readl(regs + S3C_DIEPTSIZ(idx));
+               out = readl(regs + S3C_DOEPTSIZ(idx));
+
+               seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
+                          in, out);
+
+               seq_printf(seq, "\n");
+       }
+
+       return 0;
+}
+
+static int state_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, state_show, inode->i_private);
+}
+
+static const struct file_operations state_fops = {
+       .owner          = THIS_MODULE,
+       .open           = state_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/**
+ * fifo_show - debugfs: show the fifo information
+ * @seq: The seq_file to write data to.
+ * @v: Unused parameter.
+ *
+ * Show the FIFO information for the overall fifo and all the
+ * periodic transmission FIFOs.
+*/
+static int fifo_show(struct seq_file *seq, void *v)
+{
+       struct s3c_hsotg *hsotg = seq->private;
+       void __iomem *regs = hsotg->regs;
+       u32 val;
+       int idx;
+
+       seq_printf(seq, "Non-periodic FIFOs:\n");
+       seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + S3C_GRXFSIZ));
+
+       val = readl(regs + S3C_GNPTXFSIZ);
+       seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
+                  val >> S3C_GNPTXFSIZ_NPTxFDep_SHIFT,
+                  val & S3C_GNPTXFSIZ_NPTxFStAddr_MASK);
+
+       seq_printf(seq, "\nPeriodic TXFIFOs:\n");
+
+       for (idx = 1; idx <= 15; idx++) {
+               val = readl(regs + S3C_DPTXFSIZn(idx));
+
+               seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
+                          val >> S3C_DPTXFSIZn_DPTxFSize_SHIFT,
+                          val & S3C_DPTXFSIZn_DPTxFStAddr_MASK);
+       }
+
+       return 0;
+}
+
+static int fifo_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, fifo_show, inode->i_private);
+}
+
+static const struct file_operations fifo_fops = {
+       .owner          = THIS_MODULE,
+       .open           = fifo_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+
+static const char *decode_direction(int is_in)
+{
+       return is_in ? "in" : "out";
+}
+
+/**
+ * ep_show - debugfs: show the state of an endpoint.
+ * @seq: The seq_file to write data to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry shows the state of the given endpoint (one is
+ * registered for each available).
+*/
+static int ep_show(struct seq_file *seq, void *v)
+{
+       struct s3c_hsotg_ep *ep = seq->private;
+       struct s3c_hsotg *hsotg = ep->parent;
+       struct s3c_hsotg_req *req;
+       void __iomem *regs = hsotg->regs;
+       int index = ep->index;
+       int show_limit = 15;
+       unsigned long flags;
+
+       seq_printf(seq, "Endpoint index %d, named %s,  dir %s:\n",
+                  ep->index, ep->ep.name, decode_direction(ep->dir_in));
+
+       /* first show the register state */
+
+       seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n",
+                  readl(regs + S3C_DIEPCTL(index)),
+                  readl(regs + S3C_DOEPCTL(index)));
+
+       seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n",
+                  readl(regs + S3C_DIEPDMA(index)),
+                  readl(regs + S3C_DOEPDMA(index)));
+
+       seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n",
+                  readl(regs + S3C_DIEPINT(index)),
+                  readl(regs + S3C_DOEPINT(index)));
+
+       seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n",
+                  readl(regs + S3C_DIEPTSIZ(index)),
+                  readl(regs + S3C_DOEPTSIZ(index)));
+
+       seq_printf(seq, "\n");
+       seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
+       seq_printf(seq, "total_data=%ld\n", ep->total_data);
+
+       seq_printf(seq, "request list (%p,%p):\n",
+                  ep->queue.next, ep->queue.prev);
+
+       spin_lock_irqsave(&ep->lock, flags);
+
+       list_for_each_entry(req, &ep->queue, queue) {
+               if (--show_limit < 0) {
+                       seq_printf(seq, "not showing more requests...\n");
+                       break;
+               }
+
+               seq_printf(seq, "%c req %p: %d bytes @%p, ",
+                          req == ep->req ? '*' : ' ',
+                          req, req->req.length, req->req.buf);
+               seq_printf(seq, "%d done, res %d\n",
+                          req->req.actual, req->req.status);
+       }
+
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       return 0;
+}
+
+static int ep_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ep_show, inode->i_private);
+}
+
+static const struct file_operations ep_fops = {
+       .owner          = THIS_MODULE,
+       .open           = ep_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/**
+ * s3c_hsotg_create_debug - create debugfs directory and files
+ * @hsotg: The driver state
+ *
+ * Create the debugfs files to allow the user to get information
+ * about the state of the system. The directory name is created
+ * with the same name as the device itself, in case we end up
+ * with multiple blocks in future systems.
+*/
+static void __devinit s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
+{
+       struct dentry *root;
+       unsigned epidx;
+
+       root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
+       hsotg->debug_root = root;
+       if (IS_ERR(root)) {
+               dev_err(hsotg->dev, "cannot create debug root\n");
+               return;
+       }
+
+       /* create general state file */
+
+       hsotg->debug_file = debugfs_create_file("state", 0444, root,
+                                               hsotg, &state_fops);
+
+       if (IS_ERR(hsotg->debug_file))
+               dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
+
+       hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root,
+                                               hsotg, &fifo_fops);
+
+       if (IS_ERR(hsotg->debug_fifo))
+               dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
+
+       /* create one file for each endpoint */
+
+       for (epidx = 0; epidx < S3C_HSOTG_EPS; epidx++) {
+               struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
+
+               ep->debugfs = debugfs_create_file(ep->name, 0444,
+                                                 root, ep, &ep_fops);
+
+               if (IS_ERR(ep->debugfs))
+                       dev_err(hsotg->dev, "failed to create %s debug file\n",
+                               ep->name);
+       }
+}
+
+/**
+ * s3c_hsotg_delete_debug - cleanup debugfs entries
+ * @hsotg: The driver state
+ *
+ * Cleanup (remove) the debugfs files for use on module exit.
+*/
+static void __devexit s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
+{
+       unsigned epidx;
+
+       for (epidx = 0; epidx < S3C_HSOTG_EPS; epidx++) {
+               struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
+               debugfs_remove(ep->debugfs);
+       }
+
+       debugfs_remove(hsotg->debug_file);
+       debugfs_remove(hsotg->debug_fifo);
+       debugfs_remove(hsotg->debug_root);
+}
+
+/**
+ * s3c_hsotg_gate - set the hardware gate for the block
+ * @pdev: The device we bound to
+ * @on: On or off.
+ *
+ * Set the hardware gate setting into the block. If we end up on
+ * something other than an S3C64XX, then we might need to change this
+ * to using a platform data callback, or some other mechanism.
+ */
+static void s3c_hsotg_gate(struct platform_device *pdev, bool on)
+{
+       unsigned long flags;
+       u32 others;
+
+       local_irq_save(flags);
+
+       others = __raw_readl(S3C64XX_OTHERS);
+       if (on)
+               others |= S3C64XX_OTHERS_USBMASK;
+       else
+               others &= ~S3C64XX_OTHERS_USBMASK;
+       __raw_writel(others, S3C64XX_OTHERS);
+
+       local_irq_restore(flags);
+}
+
+struct s3c_hsotg_plat s3c_hsotg_default_pdata;
+
+static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
+{
+       struct s3c_hsotg_plat *plat = pdev->dev.platform_data;
+       struct device *dev = &pdev->dev;
+       struct s3c_hsotg *hsotg;
+       struct resource *res;
+       int epnum;
+       int ret;
+
+       if (!plat)
+               plat = &s3c_hsotg_default_pdata;
+
+       hsotg = kzalloc(sizeof(struct s3c_hsotg) +
+                       sizeof(struct s3c_hsotg_ep) * S3C_HSOTG_EPS,
+                       GFP_KERNEL);
+       if (!hsotg) {
+               dev_err(dev, "cannot get memory\n");
+               return -ENOMEM;
+       }
+
+       hsotg->dev = dev;
+       hsotg->plat = plat;
+
+       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_mem;
+       }
+
+       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_mem;
+       }
+
+       hsotg->regs = ioremap(res->start, resource_size(res));
+       if (!hsotg->regs) {
+               dev_err(dev, "cannot map registers\n");
+               ret = -ENXIO;
+               goto err_regs_res;
+       }
+
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0) {
+               dev_err(dev, "cannot find IRQ\n");
+               goto err_regs;
+       }
+
+       hsotg->irq = ret;
+
+       ret = request_irq(ret, s3c_hsotg_irq, 0, dev_name(dev), hsotg);
+       if (ret < 0) {
+               dev_err(dev, "cannot claim IRQ\n");
+               goto err_regs;
+       }
+
+       dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
+
+       device_initialize(&hsotg->gadget.dev);
+
+       dev_set_name(&hsotg->gadget.dev, "gadget");
+
+       hsotg->gadget.is_dualspeed = 1;
+       hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
+       hsotg->gadget.name = dev_name(dev);
+
+       hsotg->gadget.dev.parent = dev;
+       hsotg->gadget.dev.dma_mask = dev->dma_mask;
+
+       /* setup endpoint information */
+
+       INIT_LIST_HEAD(&hsotg->gadget.ep_list);
+       hsotg->gadget.ep0 = &hsotg->eps[0].ep;
+
+       /* allocate EP0 request */
+
+       hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps[0].ep,
+                                                    GFP_KERNEL);
+       if (!hsotg->ctrl_req) {
+               dev_err(dev, "failed to allocate ctrl req\n");
+               goto err_regs;
+       }
+
+       /* reset the system */
+
+       s3c_hsotg_gate(pdev, true);
+
+       s3c_hsotg_otgreset(hsotg);
+       s3c_hsotg_corereset(hsotg);
+       s3c_hsotg_init(hsotg);
+
+       /* initialise the endpoints now the core has been initialised */
+       for (epnum = 0; epnum < S3C_HSOTG_EPS; epnum++)
+               s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum);
+
+       s3c_hsotg_create_debug(hsotg);
+
+       s3c_hsotg_dump(hsotg);
+
+       our_hsotg = hsotg;
+       return 0;
+
+err_regs:
+       iounmap(hsotg->regs);
+
+err_regs_res:
+       release_resource(hsotg->regs_res);
+       kfree(hsotg->regs_res);
+
+err_mem:
+       kfree(hsotg);
+       return ret;
+}
+
+static int __devexit s3c_hsotg_remove(struct platform_device *pdev)
+{
+       struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
+
+       s3c_hsotg_delete_debug(hsotg);
+
+       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_gate(pdev, false);
+
+       kfree(hsotg);
+       return 0;
+}
+
+#if 1
+#define s3c_hsotg_suspend NULL
+#define s3c_hsotg_resume NULL
+#endif
+
+static struct platform_driver s3c_hsotg_driver = {
+       .driver         = {
+               .name   = "s3c-hsotg",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = s3c_hsotg_probe,
+       .remove         = __devexit_p(s3c_hsotg_remove),
+       .suspend        = s3c_hsotg_suspend,
+       .resume         = s3c_hsotg_resume,
+};
+
+static int __init s3c_hsotg_modinit(void)
+{
+       return platform_driver_register(&s3c_hsotg_driver);
+}
+
+static void __exit s3c_hsotg_modexit(void)
+{
+       platform_driver_unregister(&s3c_hsotg_driver);
+}
+
+module_init(s3c_hsotg_modinit);
+module_exit(s3c_hsotg_modexit);
+
+MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:s3c-hsotg");
diff --git a/drivers/usb/gadget/u_audio.c b/drivers/usb/gadget/u_audio.c
new file mode 100644 (file)
index 0000000..0f3d22f
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * u_audio.c -- ALSA audio utilities for Gadget stack
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/random.h>
+#include <linux/syscalls.h>
+
+#include "u_audio.h"
+
+/*
+ * This component encapsulates the ALSA devices for USB audio gadget
+ */
+
+#define FILE_PCM_PLAYBACK      "/dev/snd/pcmC0D0p"
+#define FILE_PCM_CAPTURE       "/dev/snd/pcmC0D0c"
+#define FILE_CONTROL           "/dev/snd/controlC0"
+
+static char *fn_play = FILE_PCM_PLAYBACK;
+module_param(fn_play, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
+
+static char *fn_cap = FILE_PCM_CAPTURE;
+module_param(fn_cap, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
+
+static char *fn_cntl = FILE_CONTROL;
+module_param(fn_cntl, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_cntl, "Control device file name");
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Some ALSA internal helper functions
+ */
+static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
+{
+       struct snd_interval t;
+       t.empty = 0;
+       t.min = t.max = val;
+       t.openmin = t.openmax = 0;
+       t.integer = 1;
+       return snd_interval_refine(i, &t);
+}
+
+static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
+                                snd_pcm_hw_param_t var, unsigned int val,
+                                int dir)
+{
+       int changed;
+       if (hw_is_mask(var)) {
+               struct snd_mask *m = hw_param_mask(params, var);
+               if (val == 0 && dir < 0) {
+                       changed = -EINVAL;
+                       snd_mask_none(m);
+               } else {
+                       if (dir > 0)
+                               val++;
+                       else if (dir < 0)
+                               val--;
+                       changed = snd_mask_refine_set(
+                                       hw_param_mask(params, var), val);
+               }
+       } else if (hw_is_interval(var)) {
+               struct snd_interval *i = hw_param_interval(params, var);
+               if (val == 0 && dir < 0) {
+                       changed = -EINVAL;
+                       snd_interval_none(i);
+               } else if (dir == 0)
+                       changed = snd_interval_refine_set(i, val);
+               else {
+                       struct snd_interval t;
+                       t.openmin = 1;
+                       t.openmax = 1;
+                       t.empty = 0;
+                       t.integer = 0;
+                       if (dir < 0) {
+                               t.min = val - 1;
+                               t.max = val;
+                       } else {
+                               t.min = val;
+                               t.max = val+1;
+                       }
+                       changed = snd_interval_refine(i, &t);
+               }
+       } else
+               return -EINVAL;
+       if (changed) {
+               params->cmask |= 1 << var;
+               params->rmask |= 1 << var;
+       }
+       return changed;
+}
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Set default hardware params
+ */
+static int playback_default_hw_params(struct gaudio_snd_dev *snd)
+{
+       struct snd_pcm_substream *substream = snd->substream;
+       struct snd_pcm_hw_params *params;
+       snd_pcm_sframes_t result;
+
+       /*
+       * SNDRV_PCM_ACCESS_RW_INTERLEAVED,
+       * SNDRV_PCM_FORMAT_S16_LE
+       * CHANNELS: 2
+       * RATE: 48000
+       */
+       snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+       snd->format = SNDRV_PCM_FORMAT_S16_LE;
+       snd->channels = 2;
+       snd->rate = 48000;
+
+       params = kzalloc(sizeof(*params), GFP_KERNEL);
+       if (!params)
+               return -ENOMEM;
+
+       _snd_pcm_hw_params_any(params);
+       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
+                       snd->access, 0);
+       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
+                       snd->format, 0);
+       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
+                       snd->channels, 0);
+       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
+                       snd->rate, 0);
+
+       snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+       snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
+
+       result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
+       if (result < 0) {
+               ERROR(snd->card,
+                       "Preparing sound card failed: %d\n", (int)result);
+               kfree(params);
+               return result;
+       }
+
+       /* Store the hardware parameters */
+       snd->access = params_access(params);
+       snd->format = params_format(params);
+       snd->channels = params_channels(params);
+       snd->rate = params_rate(params);
+
+       kfree(params);
+
+       INFO(snd->card,
+               "Hardware params: access %x, format %x, channels %d, rate %d\n",
+               snd->access, snd->format, snd->channels, snd->rate);
+
+       return 0;
+}
+
+/**
+ * Playback audio buffer data by ALSA PCM device
+ */
+static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
+{
+       struct gaudio_snd_dev   *snd = &card->playback;
+       struct snd_pcm_substream *substream = snd->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       mm_segment_t old_fs;
+       ssize_t result;
+       snd_pcm_sframes_t frames;
+
+try_again:
+       if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+               runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+               result = snd_pcm_kernel_ioctl(substream,
+                               SNDRV_PCM_IOCTL_PREPARE, NULL);
+               if (result < 0) {
+                       ERROR(card, "Preparing sound card failed: %d\n",
+                                       (int)result);
+                       return result;
+               }
+       }
+
+       frames = bytes_to_frames(runtime, count);
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       result = snd_pcm_lib_write(snd->substream, buf, frames);
+       if (result != frames) {
+               ERROR(card, "Playback error: %d\n", (int)result);
+               set_fs(old_fs);
+               goto try_again;
+       }
+       set_fs(old_fs);
+
+       return 0;
+}
+
+static int u_audio_get_playback_channels(struct gaudio *card)
+{
+       return card->playback.channels;
+}
+
+static int u_audio_get_playback_rate(struct gaudio *card)
+{
+       return card->playback.rate;
+}
+
+/**
+ * Open ALSA PCM and control device files
+ * Initial the PCM or control device
+ */
+static int gaudio_open_snd_dev(struct gaudio *card)
+{
+       struct snd_pcm_file *pcm_file;
+       struct gaudio_snd_dev *snd;
+
+       if (!card)
+               return -ENODEV;
+
+       /* Open control device */
+       snd = &card->control;
+       snd->filp = filp_open(fn_cntl, O_RDWR, 0);
+       if (IS_ERR(snd->filp)) {
+               int ret = PTR_ERR(snd->filp);
+               ERROR(card, "unable to open sound control device file: %s\n",
+                               fn_cntl);
+               snd->filp = NULL;
+               return ret;
+       }
+       snd->card = card;
+
+       /* Open PCM playback device and setup substream */
+       snd = &card->playback;
+       snd->filp = filp_open(fn_play, O_WRONLY, 0);
+       if (IS_ERR(snd->filp)) {
+               ERROR(card, "No such PCM playback device: %s\n", fn_play);
+               snd->filp = NULL;
+       }
+       pcm_file = snd->filp->private_data;
+       snd->substream = pcm_file->substream;
+       snd->card = card;
+       playback_default_hw_params(snd);
+
+       /* Open PCM capture device and setup substream */
+       snd = &card->capture;
+       snd->filp = filp_open(fn_cap, O_RDONLY, 0);
+       if (IS_ERR(snd->filp)) {
+               ERROR(card, "No such PCM capture device: %s\n", fn_cap);
+               snd->filp = NULL;
+       }
+       pcm_file = snd->filp->private_data;
+       snd->substream = pcm_file->substream;
+       snd->card = card;
+
+       return 0;
+}
+
+/**
+ * Close ALSA PCM and control device files
+ */
+static int gaudio_close_snd_dev(struct gaudio *gau)
+{
+       struct gaudio_snd_dev   *snd;
+
+       /* Close control device */
+       snd = &gau->control;
+       if (!IS_ERR(snd->filp))
+               filp_close(snd->filp, current->files);
+
+       /* Close PCM playback device and setup substream */
+       snd = &gau->playback;
+       if (!IS_ERR(snd->filp))
+               filp_close(snd->filp, current->files);
+
+       /* Close PCM capture device and setup substream */
+       snd = &gau->capture;
+       if (!IS_ERR(snd->filp))
+               filp_close(snd->filp, current->files);
+
+       return 0;
+}
+
+/**
+ * gaudio_setup - setup ALSA interface and preparing for USB transfer
+ *
+ * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
+ *
+ * Returns negative errno, or zero on success
+ */
+int __init gaudio_setup(struct gaudio *card)
+{
+       int     ret;
+
+       ret = gaudio_open_snd_dev(card);
+       if (ret)
+               ERROR(card, "we need at least one control device\n");
+
+       return ret;
+
+}
+
+/**
+ * gaudio_cleanup - remove ALSA device interface
+ *
+ * This is called to free all resources allocated by @gaudio_setup().
+ */
+void gaudio_cleanup(struct gaudio *card)
+{
+       if (card)
+               gaudio_close_snd_dev(card);
+}
+
diff --git a/drivers/usb/gadget/u_audio.h b/drivers/usb/gadget/u_audio.h
new file mode 100644 (file)
index 0000000..cc8d159
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * u_audio.h -- interface to USB gadget "ALSA AUDIO" utilities
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __U_AUDIO_H
+#define __U_AUDIO_H
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/composite.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "gadget_chips.h"
+
+/*
+ * This represents the USB side of an audio card device, managed by a USB
+ * function which provides control and stream interfaces.
+ */
+
+struct gaudio_snd_dev {
+       struct gaudio                   *card;
+       struct file                     *filp;
+       struct snd_pcm_substream        *substream;
+       int                             access;
+       int                             format;
+       int                             channels;
+       int                             rate;
+};
+
+struct gaudio {
+       struct usb_function             func;
+       struct usb_gadget               *gadget;
+
+       /* ALSA sound device interfaces */
+       struct gaudio_snd_dev           control;
+       struct gaudio_snd_dev           playback;
+       struct gaudio_snd_dev           capture;
+
+       /* TODO */
+};
+
+int gaudio_setup(struct gaudio *card);
+void gaudio_cleanup(struct gaudio *card);
+
+#endif /* __U_AUDIO_H */
index 0a4d99a..fc6e709 100644 (file)
@@ -371,6 +371,7 @@ __acquires(&port->port_lock)
 
                req->length = len;
                list_del(&req->list);
+               req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
 
                pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
                                port->port_num, len, *((u8 *)req->buf),
index 845479f..1576a05 100644 (file)
@@ -17,6 +17,26 @@ config USB_C67X00_HCD
          To compile this driver as a module, choose M here: the
          module will be called c67x00.
 
+config USB_XHCI_HCD
+       tristate "xHCI HCD (USB 3.0) support (EXPERIMENTAL)"
+       depends on USB && PCI && EXPERIMENTAL
+       ---help---
+         The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0
+         "SuperSpeed" host controller hardware.
+
+         To compile this driver as a module, choose M here: the
+         module will be called xhci-hcd.
+
+config USB_XHCI_HCD_DEBUGGING
+       bool "Debugging for the xHCI host controller"
+       depends on USB_XHCI_HCD
+       ---help---
+         Say 'Y' to turn on debugging for the xHCI host controller driver.
+         This will spew debugging output, even in interrupt context.
+         This should only be used for debugging xHCI driver bugs.
+
+         If unsure, say N.
+
 config USB_EHCI_HCD
        tristate "EHCI HCD (USB 2.0) support"
        depends on USB && USB_ARCH_HAS_EHCI
index f163571..289d748 100644 (file)
@@ -12,6 +12,7 @@ fhci-objs := fhci-hcd.o fhci-hub.o fhci-q.o fhci-mem.o \
 ifeq ($(CONFIG_FHCI_DEBUG),y)
 fhci-objs += fhci-dbg.o
 endif
+xhci-objs := xhci-hcd.o xhci-mem.o xhci-pci.o xhci-ring.o xhci-hub.o xhci-dbg.o
 
 obj-$(CONFIG_USB_WHCI_HCD)     += whci/
 
@@ -23,6 +24,7 @@ obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
 obj-$(CONFIG_USB_OHCI_HCD)     += ohci-hcd.o
 obj-$(CONFIG_USB_UHCI_HCD)     += uhci-hcd.o
 obj-$(CONFIG_USB_FHCI_HCD)     += fhci.o
+obj-$(CONFIG_USB_XHCI_HCD)     += xhci.o
 obj-$(CONFIG_USB_SL811_HCD)    += sl811-hcd.o
 obj-$(CONFIG_USB_SL811_CS)     += sl811_cs.o
 obj-$(CONFIG_USB_U132_HCD)     += u132-hcd.o
index bf69f47..c3a778b 100644 (file)
@@ -97,6 +97,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
        .urb_enqueue            = ehci_urb_enqueue,
        .urb_dequeue            = ehci_urb_dequeue,
        .endpoint_disable       = ehci_endpoint_disable,
+       .endpoint_reset         = ehci_endpoint_reset,
 
        /*
         * scheduling support
index 01c3da3..bf86809 100644 (file)
@@ -309,6 +309,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
        .urb_enqueue = ehci_urb_enqueue,
        .urb_dequeue = ehci_urb_dequeue,
        .endpoint_disable = ehci_endpoint_disable,
+       .endpoint_reset = ehci_endpoint_reset,
 
        /*
         * scheduling support
index c637207..2b72473 100644 (file)
@@ -1024,6 +1024,51 @@ done:
        return;
 }
 
+static void
+ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+       struct ehci_qh          *qh;
+       int                     eptype = usb_endpoint_type(&ep->desc);
+
+       if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT)
+               return;
+
+ rescan:
+       spin_lock_irq(&ehci->lock);
+       qh = ep->hcpriv;
+
+       /* For Bulk and Interrupt endpoints we maintain the toggle state
+        * in the hardware; the toggle bits in udev aren't used at all.
+        * When an endpoint is reset by usb_clear_halt() we must reset
+        * the toggle bit in the QH.
+        */
+       if (qh) {
+               if (!list_empty(&qh->qtd_list)) {
+                       WARN_ONCE(1, "clear_halt for a busy endpoint\n");
+               } else if (qh->qh_state == QH_STATE_IDLE) {
+                       qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
+               } else {
+                       /* It's not safe to write into the overlay area
+                        * while the QH is active.  Unlink it first and
+                        * wait for the unlink to complete.
+                        */
+                       if (qh->qh_state == QH_STATE_LINKED) {
+                               if (eptype == USB_ENDPOINT_XFER_BULK) {
+                                       unlink_async(ehci, qh);
+                               } else {
+                                       intr_deschedule(ehci, qh);
+                                       (void) qh_schedule(ehci, qh);
+                               }
+                       }
+                       spin_unlock_irq(&ehci->lock);
+                       schedule_timeout_uninterruptible(1);
+                       goto rescan;
+               }
+       }
+       spin_unlock_irq(&ehci->lock);
+}
+
 static int ehci_get_frame (struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
@@ -1097,7 +1142,7 @@ static int __init ehci_hcd_init(void)
                 sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
 
 #ifdef DEBUG
-       ehci_debug_root = debugfs_create_dir("ehci", NULL);
+       ehci_debug_root = debugfs_create_dir("ehci", usb_debug_root);
        if (!ehci_debug_root) {
                retval = -ENOENT;
                goto err_debug;
index 97a53a4..f46ad27 100644 (file)
@@ -391,7 +391,7 @@ static inline void create_companion_file(struct ehci_hcd *ehci)
 
        /* with integrated TT there is no companion! */
        if (!ehci_is_TDI(ehci))
-               i = device_create_file(ehci_to_hcd(ehci)->self.dev,
+               i = device_create_file(ehci_to_hcd(ehci)->self.controller,
                                       &dev_attr_companion);
 }
 
@@ -399,7 +399,7 @@ static inline void remove_companion_file(struct ehci_hcd *ehci)
 {
        /* with integrated TT there is no companion! */
        if (!ehci_is_TDI(ehci))
-               device_remove_file(ehci_to_hcd(ehci)->self.dev,
+               device_remove_file(ehci_to_hcd(ehci)->self.controller,
                                   &dev_attr_companion);
 }
 
index 9c32063..a44bb4a 100644 (file)
@@ -51,6 +51,7 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = {
        .urb_enqueue            = ehci_urb_enqueue,
        .urb_dequeue            = ehci_urb_dequeue,
        .endpoint_disable       = ehci_endpoint_disable,
+       .endpoint_reset         = ehci_endpoint_reset,
        .get_frame_number       = ehci_get_frame,
        .hub_status_data        = ehci_hub_status_data,
        .hub_control            = ehci_hub_control,
index 9d48790..770dd9a 100644 (file)
@@ -149,6 +149,7 @@ static const struct hc_driver ehci_orion_hc_driver = {
        .urb_enqueue = ehci_urb_enqueue,
        .urb_dequeue = ehci_urb_dequeue,
        .endpoint_disable = ehci_endpoint_disable,
+       .endpoint_reset = ehci_endpoint_reset,
 
        /*
         * scheduling support
@@ -187,7 +188,7 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd,
        }
 }
 
-static int __init ehci_orion_drv_probe(struct platform_device *pdev)
+static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)
 {
        struct orion_ehci_data *pd = pdev->dev.platform_data;
        struct resource *res;
index 5aa8bce..f3683e1 100644 (file)
@@ -268,7 +268,7 @@ done:
  * Also they depend on separate root hub suspend/resume.
  */
 
-static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
+static int ehci_pci_suspend(struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
        unsigned long           flags;
@@ -293,12 +293,6 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
        ehci_writel(ehci, 0, &ehci->regs->intr_enable);
        (void)ehci_readl(ehci, &ehci->regs->intr_enable);
 
-       /* make sure snapshot being resumed re-enumerates everything */
-       if (message.event == PM_EVENT_PRETHAW) {
-               ehci_halt(ehci);
-               ehci_reset(ehci);
-       }
-
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
  bail:
        spin_unlock_irqrestore (&ehci->lock, flags);
@@ -309,7 +303,7 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
        return rc;
 }
 
-static int ehci_pci_resume(struct usb_hcd *hcd)
+static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
        struct pci_dev          *pdev = to_pci_dev(hcd->self.controller);
@@ -322,10 +316,12 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
        /* 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.
+       /* 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) {
+       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
+                               !hibernated) {
                int     mask = INTR_MASK;
 
                if (!hcd->self.root_hub->do_remote_wakeup)
@@ -335,7 +331,6 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
                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
@@ -393,6 +388,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
        .urb_enqueue =          ehci_urb_enqueue,
        .urb_dequeue =          ehci_urb_dequeue,
        .endpoint_disable =     ehci_endpoint_disable,
+       .endpoint_reset =       ehci_endpoint_reset,
 
        /*
         * scheduling support
@@ -429,10 +425,11 @@ static struct pci_driver ehci_pci_driver = {
 
        .probe =        usb_hcd_pci_probe,
        .remove =       usb_hcd_pci_remove,
+       .shutdown =     usb_hcd_pci_shutdown,
 
-#ifdef CONFIG_PM
-       .suspend =      usb_hcd_pci_suspend,
-       .resume =       usb_hcd_pci_resume,
+#ifdef CONFIG_PM_SLEEP
+       .driver =       {
+               .pm =   &usb_hcd_pci_pm_ops
+       },
 #endif
-       .shutdown =     usb_hcd_pci_shutdown,
 };
index ef732b7..fbd2722 100644 (file)
@@ -61,6 +61,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
        .urb_enqueue            = ehci_urb_enqueue,
        .urb_dequeue            = ehci_urb_dequeue,
        .endpoint_disable       = ehci_endpoint_disable,
+       .endpoint_reset         = ehci_endpoint_reset,
 
        /*
         * scheduling support
index bb870b8..eecd2a0 100644 (file)
@@ -65,6 +65,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
        .urb_enqueue            = ehci_urb_enqueue,
        .urb_dequeue            = ehci_urb_dequeue,
        .endpoint_disable       = ehci_endpoint_disable,
+       .endpoint_reset         = ehci_endpoint_reset,
        .get_frame_number       = ehci_get_frame,
        .hub_status_data        = ehci_hub_status_data,
        .hub_control            = ehci_hub_control,
index 1976b1b..3192f68 100644 (file)
@@ -93,22 +93,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
        qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
        qh->hw_alt_next = EHCI_LIST_END(ehci);
 
-       /* Except for control endpoints, we make hardware maintain data
-        * toggle (like OHCI) ... here (re)initialize the toggle in the QH,
-        * and set the pseudo-toggle in udev. Only usb_clear_halt() will
-        * ever clear it.
-        */
-       if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
-               unsigned        is_out, epnum;
-
-               is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
-               epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
-               if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
-                       qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
-                       usb_settoggle (qh->dev, epnum, is_out, 1);
-               }
-       }
-
        /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
        wmb ();
        qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
@@ -850,7 +834,6 @@ done:
        qh->qh_state = QH_STATE_IDLE;
        qh->hw_info1 = cpu_to_hc32(ehci, info1);
        qh->hw_info2 = cpu_to_hc32(ehci, info2);
-       usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
        qh_refresh (ehci, qh);
        return qh;
 }
@@ -881,7 +864,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
                }
        }
 
-       /* clear halt and/or toggle; and maybe recover from silicon quirk */
+       /* clear halt and maybe recover from silicon quirk */
        if (qh->qh_state == QH_STATE_IDLE)
                qh_refresh (ehci, qh);
 
index 556d0ec..9d1babc 100644 (file)
@@ -760,8 +760,10 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
        if (status) {
                /* "normal" case, uframing flexible except with splits */
                if (qh->period) {
-                       frame = qh->period - 1;
-                       do {
+                       int             i;
+
+                       for (i = qh->period; status && i > 0; --i) {
+                               frame = ++ehci->random_frame % qh->period;
                                for (uframe = 0; uframe < 8; uframe++) {
                                        status = check_intr_schedule (ehci,
                                                        frame, uframe, qh,
@@ -769,7 +771,7 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
                                        if (status == 0)
                                                break;
                                }
-                       } while (status && frame--);
+                       }
 
                /* qh->period == 0 means every uframe */
                } else {
index 6cff195..90ad339 100644 (file)
@@ -116,6 +116,7 @@ struct ehci_hcd {                   /* one per controller */
        struct timer_list       watchdog;
        unsigned long           actions;
        unsigned                stamp;
+       unsigned                random_frame;
        unsigned long           next_statechange;
        u32                     command;
 
index ea8a425..e799f86 100644 (file)
@@ -108,7 +108,7 @@ void fhci_dfs_create(struct fhci_hcd *fhci)
 {
        struct device *dev = fhci_to_hcd(fhci)->self.controller;
 
-       fhci->dfs_root = debugfs_create_dir(dev_name(dev), NULL);
+       fhci->dfs_root = debugfs_create_dir(dev_name(dev), usb_debug_root);
        if (!fhci->dfs_root) {
                WARN_ON(1);
                return;
index cbf30e5..88b0321 100644 (file)
@@ -172,25 +172,6 @@ error_cluster_id_get:
 
 }
 
-static int hwahc_op_suspend(struct usb_hcd *usb_hcd, pm_message_t msg)
-{
-       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
-       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
-       dev_err(wusbhc->dev, "%s (%p [%p], 0x%lx) UNIMPLEMENTED\n", __func__,
-               usb_hcd, hwahc, *(unsigned long *) &msg);
-       return -ENOSYS;
-}
-
-static int hwahc_op_resume(struct usb_hcd *usb_hcd)
-{
-       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
-       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
-
-       dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
-               usb_hcd, hwahc);
-       return -ENOSYS;
-}
-
 /*
  * No need to abort pipes, as when this is called, all the children
  * has been disconnected and that has done it [through
@@ -598,8 +579,6 @@ static struct hc_driver hwahc_hc_driver = {
        .flags = HCD_USB2,              /* FIXME */
        .reset = hwahc_op_reset,
        .start = hwahc_op_start,
-       .pci_suspend = hwahc_op_suspend,
-       .pci_resume = hwahc_op_resume,
        .stop = hwahc_op_stop,
        .get_frame_number = hwahc_op_get_frame_number,
        .urb_enqueue = hwahc_op_urb_enqueue,
index d326965..811f5df 100644 (file)
@@ -431,7 +431,7 @@ static struct dentry *ohci_debug_root;
 
 struct debug_buffer {
        ssize_t (*fill_func)(struct debug_buffer *);    /* fill method */
-       struct device *dev;
+       struct ohci_hcd *ohci;
        struct mutex mutex;     /* protect filling of buffer */
        size_t count;           /* number of characters filled into buffer */
        char *page;
@@ -505,15 +505,11 @@ show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
 
 static ssize_t fill_async_buffer(struct debug_buffer *buf)
 {
-       struct usb_bus          *bus;
-       struct usb_hcd          *hcd;
        struct ohci_hcd         *ohci;
        size_t                  temp;
        unsigned long           flags;
 
-       bus = dev_get_drvdata(buf->dev);
-       hcd = bus_to_hcd(bus);
-       ohci = hcd_to_ohci(hcd);
+       ohci = buf->ohci;
 
        /* display control and bulk lists together, for simplicity */
        spin_lock_irqsave (&ohci->lock, flags);
@@ -529,8 +525,6 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
 
 static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
 {
-       struct usb_bus          *bus;
-       struct usb_hcd          *hcd;
        struct ohci_hcd         *ohci;
        struct ed               **seen, *ed;
        unsigned long           flags;
@@ -542,9 +536,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
                return 0;
        seen_count = 0;
 
-       bus = (struct usb_bus *)dev_get_drvdata(buf->dev);
-       hcd = bus_to_hcd(bus);
-       ohci = hcd_to_ohci(hcd);
+       ohci = buf->ohci;
        next = buf->page;
        size = PAGE_SIZE;
 
@@ -626,7 +618,6 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
 
 static ssize_t fill_registers_buffer(struct debug_buffer *buf)
 {
-       struct usb_bus          *bus;
        struct usb_hcd          *hcd;
        struct ohci_hcd         *ohci;
        struct ohci_regs __iomem *regs;
@@ -635,9 +626,8 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
        char                    *next;
        u32                     rdata;
 
-       bus = (struct usb_bus *)dev_get_drvdata(buf->dev);
-       hcd = bus_to_hcd(bus);
-       ohci = hcd_to_ohci(hcd);
+       ohci = buf->ohci;
+       hcd = ohci_to_hcd(ohci);
        regs = ohci->regs;
        next = buf->page;
        size = PAGE_SIZE;
@@ -710,7 +700,7 @@ done:
        return PAGE_SIZE - size;
 }
 
-static struct debug_buffer *alloc_buffer(struct device *dev,
+static struct debug_buffer *alloc_buffer(struct ohci_hcd *ohci,
                                ssize_t (*fill_func)(struct debug_buffer *))
 {
        struct debug_buffer *buf;
@@ -718,7 +708,7 @@ static struct debug_buffer *alloc_buffer(struct device *dev,
        buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
 
        if (buf) {
-               buf->dev = dev;
+               buf->ohci = ohci;
                buf->fill_func = fill_func;
                mutex_init(&buf->mutex);
        }
@@ -810,26 +800,25 @@ static int debug_registers_open(struct inode *inode, struct file *file)
 static inline void create_debug_files (struct ohci_hcd *ohci)
 {
        struct usb_bus *bus = &ohci_to_hcd(ohci)->self;
-       struct device *dev = bus->dev;
 
        ohci->debug_dir = debugfs_create_dir(bus->bus_name, ohci_debug_root);
        if (!ohci->debug_dir)
                goto dir_error;
 
        ohci->debug_async = debugfs_create_file("async", S_IRUGO,
-                                               ohci->debug_dir, dev,
+                                               ohci->debug_dir, ohci,
                                                &debug_async_fops);
        if (!ohci->debug_async)
                goto async_error;
 
        ohci->debug_periodic = debugfs_create_file("periodic", S_IRUGO,
-                                                  ohci->debug_dir, dev,
+                                                  ohci->debug_dir, ohci,
                                                   &debug_periodic_fops);
        if (!ohci->debug_periodic)
                goto periodic_error;
 
        ohci->debug_registers = debugfs_create_file("registers", S_IRUGO,
-                                                   ohci->debug_dir, dev,
+                                                   ohci->debug_dir, ohci,
                                                    &debug_registers_fops);
        if (!ohci->debug_registers)
                goto registers_error;
index 25db704..5815168 100644 (file)
@@ -571,7 +571,7 @@ static int ohci_init (struct ohci_hcd *ohci)
  */
 static int ohci_run (struct ohci_hcd *ohci)
 {
-       u32                     mask, temp;
+       u32                     mask, val;
        int                     first = ohci->fminterval == 0;
        struct usb_hcd          *hcd = ohci_to_hcd(ohci);
 
@@ -580,8 +580,8 @@ static int ohci_run (struct ohci_hcd *ohci)
        /* boot firmware should have set this up (5.1.1.3.1) */
        if (first) {
 
-               temp = ohci_readl (ohci, &ohci->regs->fminterval);
-               ohci->fminterval = temp & 0x3fff;
+               val = ohci_readl (ohci, &ohci->regs->fminterval);
+               ohci->fminterval = val & 0x3fff;
                if (ohci->fminterval != FI)
                        ohci_dbg (ohci, "fminterval delta %d\n",
                                ohci->fminterval - FI);
@@ -600,25 +600,25 @@ static int ohci_run (struct ohci_hcd *ohci)
 
        switch (ohci->hc_control & OHCI_CTRL_HCFS) {
        case OHCI_USB_OPER:
-               temp = 0;
+               val = 0;
                break;
        case OHCI_USB_SUSPEND:
        case OHCI_USB_RESUME:
                ohci->hc_control &= OHCI_CTRL_RWC;
                ohci->hc_control |= OHCI_USB_RESUME;
-               temp = 10 /* msec wait */;
+               val = 10 /* msec wait */;
                break;
        // case OHCI_USB_RESET:
        default:
                ohci->hc_control &= OHCI_CTRL_RWC;
                ohci->hc_control |= OHCI_USB_RESET;
-               temp = 50 /* msec wait */;
+               val = 50 /* msec wait */;
                break;
        }
        ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
        // flush the writes
        (void) ohci_readl (ohci, &ohci->regs->control);
-       msleep(temp);
+       msleep(val);
 
        memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
 
@@ -628,9 +628,9 @@ static int ohci_run (struct ohci_hcd *ohci)
 retry:
        /* HC Reset requires max 10 us delay */
        ohci_writel (ohci, OHCI_HCR,  &ohci->regs->cmdstatus);
-       temp = 30;      /* ... allow extra time */
+       val = 30;       /* ... allow extra time */
        while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
-               if (--temp == 0) {
+               if (--val == 0) {
                        spin_unlock_irq (&ohci->lock);
                        ohci_err (ohci, "USB HC reset timed out!\n");
                        return -1;
@@ -699,23 +699,23 @@ retry:
        ohci_writel (ohci, mask, &ohci->regs->intrenable);
 
        /* handle root hub init quirks ... */
-       temp = roothub_a (ohci);
-       temp &= ~(RH_A_PSM | RH_A_OCPM);
+       val = roothub_a (ohci);
+       val &= ~(RH_A_PSM | RH_A_OCPM);
        if (ohci->flags & OHCI_QUIRK_SUPERIO) {
                /* NSC 87560 and maybe others */
-               temp |= RH_A_NOCP;
-               temp &= ~(RH_A_POTPGT | RH_A_NPS);
-               ohci_writel (ohci, temp, &ohci->regs->roothub.a);
+               val |= RH_A_NOCP;
+               val &= ~(RH_A_POTPGT | RH_A_NPS);
+               ohci_writel (ohci, val, &ohci->regs->roothub.a);
        } else if ((ohci->flags & OHCI_QUIRK_AMD756) ||
                        (ohci->flags & OHCI_QUIRK_HUB_POWER)) {
                /* hub power always on; required for AMD-756 and some
                 * Mac platforms.  ganged overcurrent reporting, if any.
                 */
-               temp |= RH_A_NPS;
-               ohci_writel (ohci, temp, &ohci->regs->roothub.a);
+               val |= RH_A_NPS;
+               ohci_writel (ohci, val, &ohci->regs->roothub.a);
        }
        ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);
-       ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM,
+       ohci_writel (ohci, (val & RH_A_NPS) ? 0 : RH_B_PPCM,
                                                &ohci->regs->roothub.b);
        // flush those writes
        (void) ohci_readl (ohci, &ohci->regs->control);
@@ -724,7 +724,7 @@ retry:
        spin_unlock_irq (&ohci->lock);
 
        // POTPGT delay is bits 24-31, in 2 ms units.
-       mdelay ((temp >> 23) & 0x1fe);
+       mdelay ((val >> 23) & 0x1fe);
        hcd->state = HC_STATE_RUNNING;
 
        if (quirk_zfmicro(ohci)) {
@@ -1105,7 +1105,7 @@ static int __init ohci_hcd_mod_init(void)
        set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
 
 #ifdef DEBUG
-       ohci_debug_root = debugfs_create_dir("ohci", NULL);
+       ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root);
        if (!ohci_debug_root) {
                retval = -ENOENT;
                goto error_debug;
index f9961b4..d2ba04d 100644 (file)
@@ -372,7 +372,7 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
 
 #ifdef CONFIG_PM
 
-static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
+static int ohci_pci_suspend(struct usb_hcd *hcd)
 {
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
        unsigned long   flags;
@@ -394,10 +394,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
        ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
        (void)ohci_readl(ohci, &ohci->regs->intrdisable);
 
-       /* make sure snapshot being resumed re-enumerates everything */
-       if (message.event == PM_EVENT_PRETHAW)
-               ohci_usb_reset(ohci);
-
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
  bail:
        spin_unlock_irqrestore (&ohci->lock, flags);
@@ -406,9 +402,14 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
 }
 
 
-static int ohci_pci_resume (struct usb_hcd *hcd)
+static int ohci_pci_resume(struct usb_hcd *hcd, bool hibernated)
 {
        set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+       /* Make sure resume from hibernation re-enumerates everything */
+       if (hibernated)
+               ohci_usb_reset(hcd_to_ohci(hcd));
+
        ohci_finish_controller_resume(hcd);
        return 0;
 }
@@ -484,12 +485,11 @@ static struct pci_driver ohci_pci_driver = {
 
        .probe =        usb_hcd_pci_probe,
        .remove =       usb_hcd_pci_remove,
+       .shutdown =     usb_hcd_pci_shutdown,
 
-#ifdef CONFIG_PM
-       .suspend =      usb_hcd_pci_suspend,
-       .resume =       usb_hcd_pci_resume,
+#ifdef CONFIG_PM_SLEEP
+       .driver =       {
+               .pm =   &usb_hcd_pci_pm_ops
+       },
 #endif
-
-       .shutdown =     usb_hcd_pci_shutdown,
 };
-
index 033c284..83b5f9c 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/acpi.h>
 #include "pci-quirks.h"
+#include "xhci-ext-caps.h"
 
 
 #define UHCI_USBLEGSUP         0xc0            /* legacy support */
@@ -341,7 +342,127 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
        return;
 }
 
+/*
+ * handshake - spin reading a register until handshake completes
+ * @ptr: address of hc register to be read
+ * @mask: bits to look at in result of read
+ * @done: value of those bits when handshake succeeds
+ * @wait_usec: timeout in microseconds
+ * @delay_usec: delay in microseconds to wait between polling
+ *
+ * Polls a register every delay_usec microseconds.
+ * Returns 0 when the mask bits have the value done.
+ * Returns -ETIMEDOUT if this condition is not true after
+ * wait_usec microseconds have passed.
+ */
+static int handshake(void __iomem *ptr, u32 mask, u32 done,
+               int wait_usec, int delay_usec)
+{
+       u32     result;
+
+       do {
+               result = readl(ptr);
+               result &= mask;
+               if (result == done)
+                       return 0;
+               udelay(delay_usec);
+               wait_usec -= delay_usec;
+       } while (wait_usec > 0);
+       return -ETIMEDOUT;
+}
+
+/**
+ * PCI Quirks for xHCI.
+ *
+ * Takes care of the handoff between the Pre-OS (i.e. BIOS) and the OS.
+ * It signals to the BIOS that the OS wants control of the host controller,
+ * and then waits 5 seconds for the BIOS to hand over control.
+ * If we timeout, assume the BIOS is broken and take control anyway.
+ */
+static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
+{
+       void __iomem *base;
+       int ext_cap_offset;
+       void __iomem *op_reg_base;
+       u32 val;
+       int timeout;
+
+       if (!mmio_resource_enabled(pdev, 0))
+               return;
+
+       base = ioremap_nocache(pci_resource_start(pdev, 0),
+                               pci_resource_len(pdev, 0));
+       if (base == NULL)
+               return;
 
+       /*
+        * Find the Legacy Support Capability register -
+        * this is optional for xHCI host controllers.
+        */
+       ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
+       do {
+               if (!ext_cap_offset)
+                       /* We've reached the end of the extended capabilities */
+                       goto hc_init;
+               val = readl(base + ext_cap_offset);
+               if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
+                       break;
+               ext_cap_offset = xhci_find_next_cap_offset(base, ext_cap_offset);
+       } while (1);
+
+       /* If the BIOS owns the HC, signal that the OS wants it, and wait */
+       if (val & XHCI_HC_BIOS_OWNED) {
+               writel(val & XHCI_HC_OS_OWNED, base + ext_cap_offset);
+
+               /* Wait for 5 seconds with 10 microsecond polling interval */
+               timeout = handshake(base + ext_cap_offset, XHCI_HC_BIOS_OWNED,
+                               0, 5000, 10);
+
+               /* Assume a buggy BIOS and take HC ownership anyway */
+               if (timeout) {
+                       dev_warn(&pdev->dev, "xHCI BIOS handoff failed"
+                                       " (BIOS bug ?) %08x\n", val);
+                       writel(val & ~XHCI_HC_BIOS_OWNED, base + ext_cap_offset);
+               }
+       }
+
+       /* Disable any BIOS SMIs */
+       writel(XHCI_LEGACY_DISABLE_SMI,
+                       base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
+
+hc_init:
+       op_reg_base = base + XHCI_HC_LENGTH(readl(base));
+
+       /* Wait for the host controller to be ready before writing any
+        * operational or runtime registers.  Wait 5 seconds and no more.
+        */
+       timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_CNR, 0,
+                       5000, 10);
+       /* Assume a buggy HC and start HC initialization anyway */
+       if (timeout) {
+               val = readl(op_reg_base + XHCI_STS_OFFSET);
+               dev_warn(&pdev->dev,
+                               "xHCI HW not ready after 5 sec (HC bug?) "
+                               "status = 0x%x\n", val);
+       }
+
+       /* Send the halt and disable interrupts command */
+       val = readl(op_reg_base + XHCI_CMD_OFFSET);
+       val &= ~(XHCI_CMD_RUN | XHCI_IRQS);
+       writel(val, op_reg_base + XHCI_CMD_OFFSET);
+
+       /* Wait for the HC to halt - poll every 125 usec (one microframe). */
+       timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_HALT, 1,
+                       XHCI_MAX_HALT_USEC, 125);
+       if (timeout) {
+               val = readl(op_reg_base + XHCI_STS_OFFSET);
+               dev_warn(&pdev->dev,
+                               "xHCI HW did not halt within %d usec "
+                               "status = 0x%x\n", XHCI_MAX_HALT_USEC, val);
+       }
+
+       iounmap(base);
+}
 
 static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
 {
@@ -351,5 +472,7 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
                quirk_usb_handoff_ohci(pdev);
        else if (pdev->class == PCI_CLASS_SERIAL_USB_EHCI)
                quirk_usb_disable_ehci(pdev);
+       else if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI)
+               quirk_usb_handoff_xhci(pdev);
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);
index f1626e5..56976cc 100644 (file)
@@ -46,31 +46,10 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Yoshihiro Shimoda");
 MODULE_ALIAS("platform:r8a66597_hcd");
 
-#define DRIVER_VERSION "10 Apr 2008"
+#define DRIVER_VERSION "2009-05-26"
 
 static const char hcd_name[] = "r8a66597_hcd";
 
-/* module parameters */
-#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
-static unsigned short clock = XTAL12;
-module_param(clock, ushort, 0644);
-MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 "
-               "(default=0)");
-#endif
-
-static unsigned short vif = LDRV;
-module_param(vif, ushort, 0644);
-MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0(default=32768)");
-
-static unsigned short endian;
-module_param(endian, ushort, 0644);
-MODULE_PARM_DESC(endian, "data endian: big=256, little=0 (default=0)");
-
-static unsigned short irq_sense = 0xff;
-module_param(irq_sense, ushort, 0644);
-MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=32, falling edge=0 "
-               "(default=32)");
-
 static void packet_write(struct r8a66597 *r8a66597, u16 pipenum);
 static int r8a66597_get_frame(struct usb_hcd *hcd);
 
@@ -136,7 +115,8 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
                }
        } while ((tmp & USBE) != USBE);
        r8a66597_bclr(r8a66597, USBE, SYSCFG0);
-       r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0);
+       r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata), XTAL,
+                       SYSCFG0);
 
        i = 0;
        r8a66597_bset(r8a66597, XCKE, SYSCFG0);
@@ -203,6 +183,9 @@ static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port)
 static int enable_controller(struct r8a66597 *r8a66597)
 {
        int ret, port;
+       u16 vif = r8a66597->pdata->vif ? LDRV : 0;
+       u16 irq_sense = r8a66597->irq_sense_low ? INTL : 0;
+       u16 endian = r8a66597->pdata->endian ? BIGEND : 0;
 
        ret = r8a66597_clock_enable(r8a66597);
        if (ret < 0)
@@ -2373,7 +2356,7 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
        return 0;
 }
 
-static int __init r8a66597_probe(struct platform_device *pdev)
+static int __devinit r8a66597_probe(struct platform_device *pdev)
 {
 #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
        char clk_name[8];
@@ -2418,6 +2401,12 @@ static int __init r8a66597_probe(struct platform_device *pdev)
                goto clean_up;
        }
 
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "no platform data\n");
+               ret = -ENODEV;
+               goto clean_up;
+       }
+
        /* initialize hcd */
        hcd = usb_create_hcd(&r8a66597_hc_driver, &pdev->dev, (char *)hcd_name);
        if (!hcd) {
@@ -2428,6 +2417,8 @@ static int __init r8a66597_probe(struct platform_device *pdev)
        r8a66597 = hcd_to_r8a66597(hcd);
        memset(r8a66597, 0, sizeof(struct r8a66597));
        dev_set_drvdata(&pdev->dev, r8a66597);
+       r8a66597->pdata = pdev->dev.platform_data;
+       r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
 
 #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
        snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
@@ -2458,29 +2449,6 @@ static int __init r8a66597_probe(struct platform_device *pdev)
 
        hcd->rsrc_start = res->start;
 
-       /* irq_sense setting on cmdline takes precedence over resource
-        * settings, so the introduction of irqflags in IRQ resourse
-        * won't disturb existing setups */
-       switch (irq_sense) {
-               case INTL:
-                       irq_trigger = IRQF_TRIGGER_LOW;
-                       break;
-               case 0:
-                       irq_trigger = IRQF_TRIGGER_FALLING;
-                       break;
-               case 0xff:
-                       if (irq_trigger)
-                               irq_sense = (irq_trigger & IRQF_TRIGGER_LOW) ?
-                                           INTL : 0;
-                       else {
-                               irq_sense = INTL;
-                               irq_trigger = IRQF_TRIGGER_LOW;
-                       }
-                       break;
-               default:
-                       dev_err(&pdev->dev, "Unknown irq_sense value.\n");
-       }
-
        ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to add hcd\n");
index f49208f..d72680b 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/clk.h>
 #endif
 
+#include <linux/usb/r8a66597.h>
+
 #define SYSCFG0                0x00
 #define SYSCFG1                0x02
 #define SYSSTS0                0x04
@@ -488,6 +490,7 @@ struct r8a66597 {
 #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
        struct clk *clk;
 #endif
+       struct r8a66597_platdata        *pdata;
        struct r8a66597_device          device0;
        struct r8a66597_root_hub        root_hub[R8A66597_MAX_ROOT_HUB];
        struct list_head                pipe_queue[R8A66597_MAX_NUM_PIPE];
@@ -506,6 +509,7 @@ struct r8a66597 {
        unsigned long child_connect_map[4];
 
        unsigned bus_suspended:1;
+       unsigned irq_sense_low:1;
 };
 
 static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd)
@@ -660,10 +664,36 @@ static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
 {
        unsigned long dvstctr_reg = get_dvstctr_reg(port);
 
-       if (power)
-               r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
-       else
-               r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
+       if (r8a66597->pdata->port_power) {
+               r8a66597->pdata->port_power(port, power);
+       } else {
+               if (power)
+                       r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
+               else
+                       r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
+       }
+}
+
+static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)
+{
+       u16 clock = 0;
+
+       switch (pdata->xtal) {
+       case R8A66597_PLATDATA_XTAL_12MHZ:
+               clock = XTAL12;
+               break;
+       case R8A66597_PLATDATA_XTAL_24MHZ:
+               clock = XTAL24;
+               break;
+       case R8A66597_PLATDATA_XTAL_48MHZ:
+               clock = XTAL48;
+               break;
+       default:
+               printk(KERN_ERR "r8a66597: platdata clock is wrong.\n");
+               break;
+       }
+
+       return clock;
 }
 
 #define get_pipectr_addr(pipenum)      (PIPE1CTR + (pipenum - 1) * 2)
index cf5e4cf..274751b 100644 (file)
@@ -769,7 +769,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
        return rc;
 }
 
-static int uhci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
+static int uhci_pci_suspend(struct usb_hcd *hcd)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
        int rc = 0;
@@ -795,10 +795,6 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
 
        /* FIXME: Enable non-PME# remote wakeup? */
 
-       /* make sure snapshot being resumed re-enumerates everything */
-       if (message.event == PM_EVENT_PRETHAW)
-               uhci_hc_died(uhci);
-
 done_okay:
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
 done:
@@ -806,7 +802,7 @@ done:
        return rc;
 }
 
-static int uhci_pci_resume(struct usb_hcd *hcd)
+static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
@@ -820,6 +816,10 @@ static int uhci_pci_resume(struct usb_hcd *hcd)
 
        spin_lock_irq(&uhci->lock);
 
+       /* Make sure resume from hibernation re-enumerates everything */
+       if (hibernated)
+               uhci_hc_died(uhci);
+
        /* FIXME: Disable non-PME# remote wakeup? */
 
        /* The firmware or a boot kernel may have changed the controller
@@ -940,10 +940,11 @@ static struct pci_driver uhci_pci_driver = {
        .remove =       usb_hcd_pci_remove,
        .shutdown =     uhci_shutdown,
 
-#ifdef CONFIG_PM
-       .suspend =      usb_hcd_pci_suspend,
-       .resume =       usb_hcd_pci_resume,
-#endif /* PM */
+#ifdef CONFIG_PM_SLEEP
+       .driver =       {
+               .pm =   &usb_hcd_pci_pm_ops
+       },
+#endif
 };
  
 static int __init uhci_hcd_init(void)
@@ -961,7 +962,7 @@ static int __init uhci_hcd_init(void)
                errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
                if (!errbuf)
                        goto errbuf_failed;
-               uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
+               uhci_debugfs_root = debugfs_create_dir("uhci", usb_debug_root);
                if (!uhci_debugfs_root)
                        goto debug_failed;
        }
index 3e5807d..64e57bf 100644 (file)
@@ -260,7 +260,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
        INIT_LIST_HEAD(&qh->node);
 
        if (udev) {             /* Normal QH */
-               qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+               qh->type = usb_endpoint_type(&hep->desc);
                if (qh->type != USB_ENDPOINT_XFER_ISOC) {
                        qh->dummy_td = uhci_alloc_td(uhci);
                        if (!qh->dummy_td) {
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
new file mode 100644 (file)
index 0000000..2501c57
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "xhci.h"
+
+#define XHCI_INIT_VALUE 0x0
+
+/* Add verbose debugging later, just print everything for now */
+
+void xhci_dbg_regs(struct xhci_hcd *xhci)
+{
+       u32 temp;
+
+       xhci_dbg(xhci, "// xHCI capability registers at %p:\n",
+                       xhci->cap_regs);
+       temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase);
+       xhci_dbg(xhci, "// @%p = 0x%x (CAPLENGTH AND HCIVERSION)\n",
+                       &xhci->cap_regs->hc_capbase, temp);
+       xhci_dbg(xhci, "//   CAPLENGTH: 0x%x\n",
+                       (unsigned int) HC_LENGTH(temp));
+#if 0
+       xhci_dbg(xhci, "//   HCIVERSION: 0x%x\n",
+                       (unsigned int) HC_VERSION(temp));
+#endif
+
+       xhci_dbg(xhci, "// xHCI operational registers at %p:\n", xhci->op_regs);
+
+       temp = xhci_readl(xhci, &xhci->cap_regs->run_regs_off);
+       xhci_dbg(xhci, "// @%p = 0x%x RTSOFF\n",
+                       &xhci->cap_regs->run_regs_off,
+                       (unsigned int) temp & RTSOFF_MASK);
+       xhci_dbg(xhci, "// xHCI runtime registers at %p:\n", xhci->run_regs);
+
+       temp = xhci_readl(xhci, &xhci->cap_regs->db_off);
+       xhci_dbg(xhci, "// @%p = 0x%x DBOFF\n", &xhci->cap_regs->db_off, temp);
+       xhci_dbg(xhci, "// Doorbell array at %p:\n", xhci->dba);
+}
+
+static void xhci_print_cap_regs(struct xhci_hcd *xhci)
+{
+       u32 temp;
+
+       xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs);
+
+       temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase);
+       xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n",
+                       (unsigned int) temp);
+       xhci_dbg(xhci, "CAPLENGTH: 0x%x\n",
+                       (unsigned int) HC_LENGTH(temp));
+       xhci_dbg(xhci, "HCIVERSION: 0x%x\n",
+                       (unsigned int) HC_VERSION(temp));
+
+       temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params1);
+       xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n",
+                       (unsigned int) temp);
+       xhci_dbg(xhci, "  Max device slots: %u\n",
+                       (unsigned int) HCS_MAX_SLOTS(temp));
+       xhci_dbg(xhci, "  Max interrupters: %u\n",
+                       (unsigned int) HCS_MAX_INTRS(temp));
+       xhci_dbg(xhci, "  Max ports: %u\n",
+                       (unsigned int) HCS_MAX_PORTS(temp));
+
+       temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params2);
+       xhci_dbg(xhci, "HCSPARAMS 2: 0x%x\n",
+                       (unsigned int) temp);
+       xhci_dbg(xhci, "  Isoc scheduling threshold: %u\n",
+                       (unsigned int) HCS_IST(temp));
+       xhci_dbg(xhci, "  Maximum allowed segments in event ring: %u\n",
+                       (unsigned int) HCS_ERST_MAX(temp));
+
+       temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
+       xhci_dbg(xhci, "HCSPARAMS 3 0x%x:\n",
+                       (unsigned int) temp);
+       xhci_dbg(xhci, "  Worst case U1 device exit latency: %u\n",
+                       (unsigned int) HCS_U1_LATENCY(temp));
+       xhci_dbg(xhci, "  Worst case U2 device exit latency: %u\n",
+                       (unsigned int) HCS_U2_LATENCY(temp));
+
+       temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+       xhci_dbg(xhci, "HCC PARAMS 0x%x:\n", (unsigned int) temp);
+       xhci_dbg(xhci, "  HC generates %s bit addresses\n",
+                       HCC_64BIT_ADDR(temp) ? "64" : "32");
+       /* FIXME */
+       xhci_dbg(xhci, "  FIXME: more HCCPARAMS debugging\n");
+
+       temp = xhci_readl(xhci, &xhci->cap_regs->run_regs_off);
+       xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK);
+}
+
+static void xhci_print_command_reg(struct xhci_hcd *xhci)
+{
+       u32 temp;
+
+       temp = xhci_readl(xhci, &xhci->op_regs->command);
+       xhci_dbg(xhci, "USBCMD 0x%x:\n", temp);
+       xhci_dbg(xhci, "  HC is %s\n",
+                       (temp & CMD_RUN) ? "running" : "being stopped");
+       xhci_dbg(xhci, "  HC has %sfinished hard reset\n",
+                       (temp & CMD_RESET) ? "not " : "");
+       xhci_dbg(xhci, "  Event Interrupts %s\n",
+                       (temp & CMD_EIE) ? "enabled " : "disabled");
+       xhci_dbg(xhci, "  Host System Error Interrupts %s\n",
+                       (temp & CMD_EIE) ? "enabled " : "disabled");
+       xhci_dbg(xhci, "  HC has %sfinished light reset\n",
+                       (temp & CMD_LRESET) ? "not " : "");
+}
+
+static void xhci_print_status(struct xhci_hcd *xhci)
+{
+       u32 temp;
+
+       temp = xhci_readl(xhci, &xhci->op_regs->status);
+       xhci_dbg(xhci, "USBSTS 0x%x:\n", temp);
+       xhci_dbg(xhci, "  Event ring is %sempty\n",
+                       (temp & STS_EINT) ? "not " : "");
+       xhci_dbg(xhci, "  %sHost System Error\n",
+                       (temp & STS_FATAL) ? "WARNING: " : "No ");
+       xhci_dbg(xhci, "  HC is %s\n",
+                       (temp & STS_HALT) ? "halted" : "running");
+}
+
+static void xhci_print_op_regs(struct xhci_hcd *xhci)
+{
+       xhci_dbg(xhci, "xHCI operational registers at %p:\n", xhci->op_regs);
+       xhci_print_command_reg(xhci);
+       xhci_print_status(xhci);
+}
+
+static void xhci_print_ports(struct xhci_hcd *xhci)
+{
+       u32 __iomem *addr;
+       int i, j;
+       int ports;
+       char *names[NUM_PORT_REGS] = {
+               "status",
+               "power",
+               "link",
+               "reserved",
+       };
+
+       ports = HCS_MAX_PORTS(xhci->hcs_params1);
+       addr = &xhci->op_regs->port_status_base;
+       for (i = 0; i < ports; i++) {
+               for (j = 0; j < NUM_PORT_REGS; ++j) {
+                       xhci_dbg(xhci, "%p port %s reg = 0x%x\n",
+                                       addr, names[j],
+                                       (unsigned int) xhci_readl(xhci, addr));
+                       addr++;
+               }
+       }
+}
+
+void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int set_num)
+{
+       void *addr;
+       u32 temp;
+
+       addr = &ir_set->irq_pending;
+       temp = xhci_readl(xhci, addr);
+       if (temp == XHCI_INIT_VALUE)
+               return;
+
+       xhci_dbg(xhci, "  %p: ir_set[%i]\n", ir_set, set_num);
+
+       xhci_dbg(xhci, "  %p: ir_set.pending = 0x%x\n", addr,
+                       (unsigned int)temp);
+
+       addr = &ir_set->irq_control;
+       temp = xhci_readl(xhci, addr);
+       xhci_dbg(xhci, "  %p: ir_set.control = 0x%x\n", addr,
+                       (unsigned int)temp);
+
+       addr = &ir_set->erst_size;
+       temp = xhci_readl(xhci, addr);
+       xhci_dbg(xhci, "  %p: ir_set.erst_size = 0x%x\n", addr,
+                       (unsigned int)temp);
+
+       addr = &ir_set->rsvd;
+       temp = xhci_readl(xhci, addr);
+       if (temp != XHCI_INIT_VALUE)
+               xhci_dbg(xhci, "  WARN: %p: ir_set.rsvd = 0x%x\n",
+                               addr, (unsigned int)temp);
+
+       addr = &ir_set->erst_base[0];
+       temp = xhci_readl(xhci, addr);
+       xhci_dbg(xhci, "  %p: ir_set.erst_base[0] = 0x%x\n",
+                       addr, (unsigned int) temp);
+
+       addr = &ir_set->erst_base[1];
+       temp = xhci_readl(xhci, addr);
+       xhci_dbg(xhci, "  %p: ir_set.erst_base[1] = 0x%x\n",
+                       addr, (unsigned int) temp);
+
+       addr = &ir_set->erst_dequeue[0];
+       temp = xhci_readl(xhci, addr);
+       xhci_dbg(xhci, "  %p: ir_set.erst_dequeue[0] = 0x%x\n",
+                       addr, (unsigned int) temp);
+
+       addr = &ir_set->erst_dequeue[1];
+       temp = xhci_readl(xhci, addr);
+       xhci_dbg(xhci, "  %p: ir_set.erst_dequeue[1] = 0x%x\n",
+                       addr, (unsigned int) temp);
+}
+
+void xhci_print_run_regs(struct xhci_hcd *xhci)
+{
+       u32 temp;
+       int i;
+
+       xhci_dbg(xhci, "xHCI runtime registers at %p:\n", xhci->run_regs);
+       temp = xhci_readl(xhci, &xhci->run_regs->microframe_index);
+       xhci_dbg(xhci, "  %p: Microframe index = 0x%x\n",
+                       &xhci->run_regs->microframe_index,
+                       (unsigned int) temp);
+       for (i = 0; i < 7; ++i) {
+               temp = xhci_readl(xhci, &xhci->run_regs->rsvd[i]);
+               if (temp != XHCI_INIT_VALUE)
+                       xhci_dbg(xhci, "  WARN: %p: Rsvd[%i] = 0x%x\n",
+                                       &xhci->run_regs->rsvd[i],
+                                       i, (unsigned int) temp);
+       }
+}
+
+void xhci_print_registers(struct xhci_hcd *xhci)
+{
+       xhci_print_cap_regs(xhci);
+       xhci_print_op_regs(xhci);
+       xhci_print_ports(xhci);
+}
+
+void xhci_print_trb_offsets(struct xhci_hcd *xhci, union xhci_trb *trb)
+{
+       int i;
+       for (i = 0; i < 4; ++i)
+               xhci_dbg(xhci, "Offset 0x%x = 0x%x\n",
+                               i*4, trb->generic.field[i]);
+}
+
+/**
+ * Debug a transfer request block (TRB).
+ */
+void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb)
+{
+       u64     address;
+       u32     type = xhci_readl(xhci, &trb->link.control) & TRB_TYPE_BITMASK;
+
+       switch (type) {
+       case TRB_TYPE(TRB_LINK):
+               xhci_dbg(xhci, "Link TRB:\n");
+               xhci_print_trb_offsets(xhci, trb);
+
+               address = trb->link.segment_ptr[0] +
+                       (((u64) trb->link.segment_ptr[1]) << 32);
+               xhci_dbg(xhci, "Next ring segment DMA address = 0x%llx\n", address);
+
+               xhci_dbg(xhci, "Interrupter target = 0x%x\n",
+                               GET_INTR_TARGET(trb->link.intr_target));
+               xhci_dbg(xhci, "Cycle bit = %u\n",
+                               (unsigned int) (trb->link.control & TRB_CYCLE));
+               xhci_dbg(xhci, "Toggle cycle bit = %u\n",
+                               (unsigned int) (trb->link.control & LINK_TOGGLE));
+               xhci_dbg(xhci, "No Snoop bit = %u\n",
+                               (unsigned int) (trb->link.control & TRB_NO_SNOOP));
+               break;
+       case TRB_TYPE(TRB_TRANSFER):
+               address = trb->trans_event.buffer[0] +
+                       (((u64) trb->trans_event.buffer[1]) << 32);
+               /*
+                * FIXME: look at flags to figure out if it's an address or if
+                * the data is directly in the buffer field.
+                */
+               xhci_dbg(xhci, "DMA address or buffer contents= %llu\n", address);
+               break;
+       case TRB_TYPE(TRB_COMPLETION):
+               address = trb->event_cmd.cmd_trb[0] +
+                       (((u64) trb->event_cmd.cmd_trb[1]) << 32);
+               xhci_dbg(xhci, "Command TRB pointer = %llu\n", address);
+               xhci_dbg(xhci, "Completion status = %u\n",
+                               (unsigned int) GET_COMP_CODE(trb->event_cmd.status));
+               xhci_dbg(xhci, "Flags = 0x%x\n", (unsigned int) trb->event_cmd.flags);
+               break;
+       default:
+               xhci_dbg(xhci, "Unknown TRB with TRB type ID %u\n",
+                               (unsigned int) type>>10);
+               xhci_print_trb_offsets(xhci, trb);
+               break;
+       }
+}
+
+/**
+ * Debug a segment with an xHCI ring.
+ *
+ * @return The Link TRB of the segment, or NULL if there is no Link TRB
+ * (which is a bug, since all segments must have a Link TRB).
+ *
+ * Prints out all TRBs in the segment, even those after the Link TRB.
+ *
+ * XXX: should we print out TRBs that the HC owns?  As long as we don't
+ * write, that should be fine...  We shouldn't expect that the memory pointed to
+ * by the TRB is valid at all.  Do we care about ones the HC owns?  Probably,
+ * for HC debugging.
+ */
+void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg)
+{
+       int i;
+       u32 addr = (u32) seg->dma;
+       union xhci_trb *trb = seg->trbs;
+
+       for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
+               trb = &seg->trbs[i];
+               xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", addr,
+                               (unsigned int) trb->link.segment_ptr[0],
+                               (unsigned int) trb->link.segment_ptr[1],
+                               (unsigned int) trb->link.intr_target,
+                               (unsigned int) trb->link.control);
+               addr += sizeof(*trb);
+       }
+}
+
+void xhci_dbg_ring_ptrs(struct xhci_hcd *xhci, struct xhci_ring *ring)
+{
+       xhci_dbg(xhci, "Ring deq = %p (virt), 0x%llx (dma)\n",
+                       ring->dequeue,
+                       (unsigned long long)xhci_trb_virt_to_dma(ring->deq_seg,
+                                                           ring->dequeue));
+       xhci_dbg(xhci, "Ring deq updated %u times\n",
+                       ring->deq_updates);
+       xhci_dbg(xhci, "Ring enq = %p (virt), 0x%llx (dma)\n",
+                       ring->enqueue,
+                       (unsigned long long)xhci_trb_virt_to_dma(ring->enq_seg,
+                                                           ring->enqueue));
+       xhci_dbg(xhci, "Ring enq updated %u times\n",
+                       ring->enq_updates);
+}
+
+/**
+ * Debugging for an xHCI ring, which is a queue broken into multiple segments.
+ *
+ * Print out each segment in the ring.  Check that the DMA address in
+ * each link segment actually matches the segment's stored DMA address.
+ * Check that the link end bit is only set at the end of the ring.
+ * Check that the dequeue and enqueue pointers point to real data in this ring
+ * (not some other ring).
+ */
+void xhci_debug_ring(struct xhci_hcd *xhci, struct xhci_ring *ring)
+{
+       /* FIXME: Throw an error if any segment doesn't have a Link TRB */
+       struct xhci_segment *seg;
+       struct xhci_segment *first_seg = ring->first_seg;
+       xhci_debug_segment(xhci, first_seg);
+
+       if (!ring->enq_updates && !ring->deq_updates) {
+               xhci_dbg(xhci, "  Ring has not been updated\n");
+               return;
+       }
+       for (seg = first_seg->next; seg != first_seg; seg = seg->next)
+               xhci_debug_segment(xhci, seg);
+}
+
+void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
+{
+       u32 addr = (u32) erst->erst_dma_addr;
+       int i;
+       struct xhci_erst_entry *entry;
+
+       for (i = 0; i < erst->num_entries; ++i) {
+               entry = &erst->entries[i];
+               xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n",
+                               (unsigned int) addr,
+                               (unsigned int) entry->seg_addr[0],
+                               (unsigned int) entry->seg_addr[1],
+                               (unsigned int) entry->seg_size,
+                               (unsigned int) entry->rsvd);
+               addr += sizeof(*entry);
+       }
+}
+
+void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci)
+{
+       u32 val;
+
+       val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[0]);
+       xhci_dbg(xhci, "// xHC command ring deq ptr low bits + flags = 0x%x\n", val);
+       val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[1]);
+       xhci_dbg(xhci, "// xHC command ring deq ptr high bits = 0x%x\n", val);
+}
+
+void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_device_control *ctx, dma_addr_t dma, unsigned int last_ep)
+{
+       int i, j;
+       int last_ep_ctx = 31;
+       /* Fields are 32 bits wide, DMA addresses are in bytes */
+       int field_size = 32 / 8;
+
+       xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - drop flags\n",
+                       &ctx->drop_flags, (unsigned long long)dma,
+                       ctx->drop_flags);
+       dma += field_size;
+       xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - add flags\n",
+                       &ctx->add_flags, (unsigned long long)dma,
+                       ctx->add_flags);
+       dma += field_size;
+       for (i = 0; i > 6; ++i) {
+               xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
+                               &ctx->rsvd[i], (unsigned long long)dma,
+                               ctx->rsvd[i], i);
+               dma += field_size;
+       }
+
+       xhci_dbg(xhci, "Slot Context:\n");
+       xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info\n",
+                       &ctx->slot.dev_info,
+                       (unsigned long long)dma, ctx->slot.dev_info);
+       dma += field_size;
+       xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info2\n",
+                       &ctx->slot.dev_info2,
+                       (unsigned long long)dma, ctx->slot.dev_info2);
+       dma += field_size;
+       xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - tt_info\n",
+                       &ctx->slot.tt_info,
+                       (unsigned long long)dma, ctx->slot.tt_info);
+       dma += field_size;
+       xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_state\n",
+                       &ctx->slot.dev_state,
+                       (unsigned long long)dma, ctx->slot.dev_state);
+       dma += field_size;
+       for (i = 0; i > 4; ++i) {
+               xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
+                               &ctx->slot.reserved[i], (unsigned long long)dma,
+                               ctx->slot.reserved[i], i);
+               dma += field_size;
+       }
+
+       if (last_ep < 31)
+               last_ep_ctx = last_ep + 1;
+       for (i = 0; i < last_ep_ctx; ++i) {
+               xhci_dbg(xhci, "Endpoint %02d Context:\n", i);
+               xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info\n",
+                               &ctx->ep[i].ep_info,
+                               (unsigned long long)dma, ctx->ep[i].ep_info);
+               dma += field_size;
+               xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info2\n",
+                               &ctx->ep[i].ep_info2,
+                               (unsigned long long)dma, ctx->ep[i].ep_info2);
+               dma += field_size;
+               xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - deq[0]\n",
+                               &ctx->ep[i].deq[0],
+                               (unsigned long long)dma, ctx->ep[i].deq[0]);
+               dma += field_size;
+               xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - deq[1]\n",
+                               &ctx->ep[i].deq[1],
+                               (unsigned long long)dma, ctx->ep[i].deq[1]);
+               dma += field_size;
+               xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - tx_info\n",
+                               &ctx->ep[i].tx_info,
+                               (unsigned long long)dma, ctx->ep[i].tx_info);
+               dma += field_size;
+               for (j = 0; j < 3; ++j) {
+                       xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
+                                       &ctx->ep[i].reserved[j],
+                                       (unsigned long long)dma,
+                                       ctx->ep[i].reserved[j], j);
+                       dma += field_size;
+               }
+       }
+}
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
new file mode 100644 (file)
index 0000000..ecc131c
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/* Up to 16 microframes to halt an HC - one microframe is 125 microsectonds */
+#define XHCI_MAX_HALT_USEC     (16*125)
+/* HC not running - set to 1 when run/stop bit is cleared. */
+#define XHCI_STS_HALT          (1<<0)
+
+/* HCCPARAMS offset from PCI base address */
+#define XHCI_HCC_PARAMS_OFFSET 0x10
+/* HCCPARAMS contains the first extended capability pointer */
+#define XHCI_HCC_EXT_CAPS(p)   (((p)>>16)&0xffff)
+
+/* Command and Status registers offset from the Operational Registers address */
+#define XHCI_CMD_OFFSET                0x00
+#define XHCI_STS_OFFSET                0x04
+
+#define XHCI_MAX_EXT_CAPS              50
+
+/* Capability Register */
+/* bits 7:0 - how long is the Capabilities register */
+#define XHCI_HC_LENGTH(p)      (((p)>>00)&0x00ff)
+
+/* Extended capability register fields */
+#define XHCI_EXT_CAPS_ID(p)    (((p)>>0)&0xff)
+#define XHCI_EXT_CAPS_NEXT(p)  (((p)>>8)&0xff)
+#define        XHCI_EXT_CAPS_VAL(p)    ((p)>>16)
+/* Extended capability IDs - ID 0 reserved */
+#define XHCI_EXT_CAPS_LEGACY   1
+#define XHCI_EXT_CAPS_PROTOCOL 2
+#define XHCI_EXT_CAPS_PM       3
+#define XHCI_EXT_CAPS_VIRT     4
+#define XHCI_EXT_CAPS_ROUTE    5
+/* IDs 6-9 reserved */
+#define XHCI_EXT_CAPS_DEBUG    10
+/* USB Legacy Support Capability - section 7.1.1 */
+#define XHCI_HC_BIOS_OWNED     (1 << 16)
+#define XHCI_HC_OS_OWNED       (1 << 24)
+
+/* USB Legacy Support Capability - section 7.1.1 */
+/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */
+#define XHCI_LEGACY_SUPPORT_OFFSET     (0x00)
+
+/* USB Legacy Support Control and Status Register  - section 7.1.2 */
+/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */
+#define XHCI_LEGACY_CONTROL_OFFSET     (0x04)
+/* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */
+#define        XHCI_LEGACY_DISABLE_SMI         ((0x3 << 1) + (0xff << 5) + (0x7 << 17))
+
+/* command register values to disable interrupts and halt the HC */
+/* start/stop HC execution - do not write unless HC is halted*/
+#define XHCI_CMD_RUN           (1 << 0)
+/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */
+#define XHCI_CMD_EIE           (1 << 2)
+/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */
+#define XHCI_CMD_HSEIE         (1 << 3)
+/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */
+#define XHCI_CMD_EWE           (1 << 10)
+
+#define XHCI_IRQS              (XHCI_CMD_EIE | XHCI_CMD_HSEIE | XHCI_CMD_EWE)
+
+/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
+#define XHCI_STS_CNR           (1 << 11)
+
+#include <linux/io.h>
+
+/**
+ * Return the next extended capability pointer register.
+ *
+ * @base       PCI register base address.
+ *
+ * @ext_offset Offset of the 32-bit register that contains the extended
+ * capabilites pointer.  If searching for the first extended capability, pass
+ * in XHCI_HCC_PARAMS_OFFSET.  If searching for the next extended capability,
+ * pass in the offset of the current extended capability register.
+ *
+ * Returns 0 if there is no next extended capability register or returns the register offset
+ * from the PCI registers base address.
+ */
+static inline int xhci_find_next_cap_offset(void __iomem *base, int ext_offset)
+{
+       u32 next;
+
+       next = readl(base + ext_offset);
+
+       if (ext_offset == XHCI_HCC_PARAMS_OFFSET)
+               /* Find the first extended capability */
+               next = XHCI_HCC_EXT_CAPS(next);
+       else
+               /* Find the next extended capability */
+               next = XHCI_EXT_CAPS_NEXT(next);
+       if (!next)
+               return 0;
+       /*
+        * Address calculation from offset of extended capabilities
+        * (or HCCPARAMS) register - see section 5.3.6 and section 7.
+        */
+       return ext_offset + (next << 2);
+}
+
+/**
+ * Find the offset of the extended capabilities with capability ID id.
+ *
+ * @base PCI MMIO registers base address.
+ * @ext_offset Offset from base of the first extended capability to look at,
+ *             or the address of HCCPARAMS.
+ * @id Extended capability ID to search for.
+ *
+ * This uses an arbitrary limit of XHCI_MAX_EXT_CAPS extended capabilities
+ * to make sure that the list doesn't contain a loop.
+ */
+static inline int xhci_find_ext_cap_by_id(void __iomem *base, int ext_offset, int id)
+{
+       u32 val;
+       int limit = XHCI_MAX_EXT_CAPS;
+
+       while (ext_offset && limit > 0) {
+               val = readl(base + ext_offset);
+               if (XHCI_EXT_CAPS_ID(val) == id)
+                       break;
+               ext_offset = xhci_find_next_cap_offset(base, ext_offset);
+               limit--;
+       }
+       if (limit > 0)
+               return ext_offset;
+       return 0;
+}
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c
new file mode 100644 (file)
index 0000000..dba3e07
--- /dev/null
@@ -0,0 +1,1274 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/irq.h>
+#include <linux/module.h>
+
+#include "xhci.h"
+
+#define DRIVER_AUTHOR "Sarah Sharp"
+#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
+
+/* TODO: copied from ehci-hcd.c - can this be refactored? */
+/*
+ * handshake - spin reading hc until handshake completes or fails
+ * @ptr: address of hc register to be read
+ * @mask: bits to look at in result of read
+ * @done: value of those bits when handshake succeeds
+ * @usec: timeout in microseconds
+ *
+ * Returns negative errno, or zero on success
+ *
+ * Success happens when the "mask" bits have the specified value (hardware
+ * handshake done).  There are two failure modes:  "usec" have passed (major
+ * hardware flakeout), or the register reads as all-ones (hardware removed).
+ */
+static int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
+                     u32 mask, u32 done, int usec)
+{
+       u32     result;
+
+       do {
+               result = xhci_readl(xhci, ptr);
+               if (result == ~(u32)0)          /* card removed */
+                       return -ENODEV;
+               result &= mask;
+               if (result == done)
+                       return 0;
+               udelay(1);
+               usec--;
+       } while (usec > 0);
+       return -ETIMEDOUT;
+}
+
+/*
+ * Force HC into halt state.
+ *
+ * Disable any IRQs and clear the run/stop bit.
+ * HC will complete any current and actively pipelined transactions, and
+ * should halt within 16 microframes of the run/stop bit being cleared.
+ * Read HC Halted bit in the status register to see when the HC is finished.
+ * XXX: shouldn't we set HC_STATE_HALT here somewhere?
+ */
+int xhci_halt(struct xhci_hcd *xhci)
+{
+       u32 halted;
+       u32 cmd;
+       u32 mask;
+
+       xhci_dbg(xhci, "// Halt the HC\n");
+       /* Disable all interrupts from the host controller */
+       mask = ~(XHCI_IRQS);
+       halted = xhci_readl(xhci, &xhci->op_regs->status) & STS_HALT;
+       if (!halted)
+               mask &= ~CMD_RUN;
+
+       cmd = xhci_readl(xhci, &xhci->op_regs->command);
+       cmd &= mask;
+       xhci_writel(xhci, cmd, &xhci->op_regs->command);
+
+       return handshake(xhci, &xhci->op_regs->status,
+                       STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
+}
+
+/*
+ * Reset a halted HC, and set the internal HC state to HC_STATE_HALT.
+ *
+ * This resets pipelines, timers, counters, state machines, etc.
+ * Transactions will be terminated immediately, and operational registers
+ * will be set to their defaults.
+ */
+int xhci_reset(struct xhci_hcd *xhci)
+{
+       u32 command;
+       u32 state;
+
+       state = xhci_readl(xhci, &xhci->op_regs->status);
+       BUG_ON((state & STS_HALT) == 0);
+
+       xhci_dbg(xhci, "// Reset the HC\n");
+       command = xhci_readl(xhci, &xhci->op_regs->command);
+       command |= CMD_RESET;
+       xhci_writel(xhci, command, &xhci->op_regs->command);
+       /* XXX: Why does EHCI set this here?  Shouldn't other code do this? */
+       xhci_to_hcd(xhci)->state = HC_STATE_HALT;
+
+       return handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 250 * 1000);
+}
+
+/*
+ * Stop the HC from processing the endpoint queues.
+ */
+static void xhci_quiesce(struct xhci_hcd *xhci)
+{
+       /*
+        * Queues are per endpoint, so we need to disable an endpoint or slot.
+        *
+        * To disable a slot, we need to insert a disable slot command on the
+        * command ring and ring the doorbell.  This will also free any internal
+        * resources associated with the slot (which might not be what we want).
+        *
+        * A Release Endpoint command sounds better - doesn't free internal HC
+        * memory, but removes the endpoints from the schedule and releases the
+        * bandwidth, disables the doorbells, and clears the endpoint enable
+        * flag.  Usually used prior to a set interface command.
+        *
+        * TODO: Implement after command ring code is done.
+        */
+       BUG_ON(!HC_IS_RUNNING(xhci_to_hcd(xhci)->state));
+       xhci_dbg(xhci, "Finished quiescing -- code not written yet\n");
+}
+
+#if 0
+/* Set up MSI-X table for entry 0 (may claim other entries later) */
+static int xhci_setup_msix(struct xhci_hcd *xhci)
+{
+       int ret;
+       struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+
+       xhci->msix_count = 0;
+       /* XXX: did I do this right?  ixgbe does kcalloc for more than one */
+       xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL);
+       if (!xhci->msix_entries) {
+               xhci_err(xhci, "Failed to allocate MSI-X entries\n");
+               return -ENOMEM;
+       }
+       xhci->msix_entries[0].entry = 0;
+
+       ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
+       if (ret) {
+               xhci_err(xhci, "Failed to enable MSI-X\n");
+               goto free_entries;
+       }
+
+       /*
+        * Pass the xhci pointer value as the request_irq "cookie".
+        * If more irqs are added, this will need to be unique for each one.
+        */
+       ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0,
+                       "xHCI", xhci_to_hcd(xhci));
+       if (ret) {
+               xhci_err(xhci, "Failed to allocate MSI-X interrupt\n");
+               goto disable_msix;
+       }
+       xhci_dbg(xhci, "Finished setting up MSI-X\n");
+       return 0;
+
+disable_msix:
+       pci_disable_msix(pdev);
+free_entries:
+       kfree(xhci->msix_entries);
+       xhci->msix_entries = NULL;
+       return ret;
+}
+
+/* XXX: code duplication; can xhci_setup_msix call this? */
+/* Free any IRQs and disable MSI-X */
+static void xhci_cleanup_msix(struct xhci_hcd *xhci)
+{
+       struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+       if (!xhci->msix_entries)
+               return;
+
+       free_irq(xhci->msix_entries[0].vector, xhci);
+       pci_disable_msix(pdev);
+       kfree(xhci->msix_entries);
+       xhci->msix_entries = NULL;
+       xhci_dbg(xhci, "Finished cleaning up MSI-X\n");
+}
+#endif
+
+/*
+ * Initialize memory for HCD and xHC (one-time init).
+ *
+ * Program the PAGESIZE register, initialize the device context array, create
+ * device contexts (?), set up a command ring segment (or two?), create event
+ * ring (one for now).
+ */
+int xhci_init(struct usb_hcd *hcd)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       int retval = 0;
+
+       xhci_dbg(xhci, "xhci_init\n");
+       spin_lock_init(&xhci->lock);
+       retval = xhci_mem_init(xhci, GFP_KERNEL);
+       xhci_dbg(xhci, "Finished xhci_init\n");
+
+       return retval;
+}
+
+/*
+ * Called in interrupt context when there might be work
+ * queued on the event ring
+ *
+ * xhci->lock must be held by caller.
+ */
+static void xhci_work(struct xhci_hcd *xhci)
+{
+       u32 temp;
+
+       /*
+        * Clear the op reg interrupt status first,
+        * so we can receive interrupts from other MSI-X interrupters.
+        * Write 1 to clear the interrupt status.
+        */
+       temp = xhci_readl(xhci, &xhci->op_regs->status);
+       temp |= STS_EINT;
+       xhci_writel(xhci, temp, &xhci->op_regs->status);
+       /* FIXME when MSI-X is supported and there are multiple vectors */
+       /* Clear the MSI-X event interrupt status */
+
+       /* Acknowledge the interrupt */
+       temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+       temp |= 0x3;
+       xhci_writel(xhci, temp, &xhci->ir_set->irq_pending);
+       /* Flush posted writes */
+       xhci_readl(xhci, &xhci->ir_set->irq_pending);
+
+       /* FIXME this should be a delayed service routine that clears the EHB */
+       xhci_handle_event(xhci);
+
+       /* Clear the event handler busy flag; the event ring should be empty. */
+       temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[0]);
+       xhci_writel(xhci, temp & ~ERST_EHB, &xhci->ir_set->erst_dequeue[0]);
+       /* Flush posted writes -- FIXME is this necessary? */
+       xhci_readl(xhci, &xhci->ir_set->irq_pending);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * xHCI spec says we can get an interrupt, and if the HC has an error condition,
+ * we might get bad data out of the event ring.  Section 4.10.2.7 has a list of
+ * indicators of an event TRB error, but we check the status *first* to be safe.
+ */
+irqreturn_t xhci_irq(struct usb_hcd *hcd)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       u32 temp, temp2;
+
+       spin_lock(&xhci->lock);
+       /* Check if the xHC generated the interrupt, or the irq is shared */
+       temp = xhci_readl(xhci, &xhci->op_regs->status);
+       temp2 = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+       if (!(temp & STS_EINT) && !ER_IRQ_PENDING(temp2)) {
+               spin_unlock(&xhci->lock);
+               return IRQ_NONE;
+       }
+
+       if (temp & STS_FATAL) {
+               xhci_warn(xhci, "WARNING: Host System Error\n");
+               xhci_halt(xhci);
+               xhci_to_hcd(xhci)->state = HC_STATE_HALT;
+               spin_unlock(&xhci->lock);
+               return -ESHUTDOWN;
+       }
+
+       xhci_work(xhci);
+       spin_unlock(&xhci->lock);
+
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
+void xhci_event_ring_work(unsigned long arg)
+{
+       unsigned long flags;
+       int temp;
+       struct xhci_hcd *xhci = (struct xhci_hcd *) arg;
+       int i, j;
+
+       xhci_dbg(xhci, "Poll event ring: %lu\n", jiffies);
+
+       spin_lock_irqsave(&xhci->lock, flags);
+       temp = xhci_readl(xhci, &xhci->op_regs->status);
+       xhci_dbg(xhci, "op reg status = 0x%x\n", temp);
+       temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+       xhci_dbg(xhci, "ir_set 0 pending = 0x%x\n", temp);
+       xhci_dbg(xhci, "No-op commands handled = %d\n", xhci->noops_handled);
+       xhci_dbg(xhci, "HC error bitmask = 0x%x\n", xhci->error_bitmask);
+       xhci->error_bitmask = 0;
+       xhci_dbg(xhci, "Event ring:\n");
+       xhci_debug_segment(xhci, xhci->event_ring->deq_seg);
+       xhci_dbg_ring_ptrs(xhci, xhci->event_ring);
+       temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[0]);
+       temp &= ERST_PTR_MASK;
+       xhci_dbg(xhci, "ERST deq = 0x%x\n", temp);
+       xhci_dbg(xhci, "Command ring:\n");
+       xhci_debug_segment(xhci, xhci->cmd_ring->deq_seg);
+       xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring);
+       xhci_dbg_cmd_ptrs(xhci);
+       for (i = 0; i < MAX_HC_SLOTS; ++i) {
+               if (xhci->devs[i]) {
+                       for (j = 0; j < 31; ++j) {
+                               if (xhci->devs[i]->ep_rings[j]) {
+                                       xhci_dbg(xhci, "Dev %d endpoint ring %d:\n", i, j);
+                                       xhci_debug_segment(xhci, xhci->devs[i]->ep_rings[j]->deq_seg);
+                               }
+                       }
+               }
+       }
+
+       if (xhci->noops_submitted != NUM_TEST_NOOPS)
+               if (xhci_setup_one_noop(xhci))
+                       xhci_ring_cmd_db(xhci);
+       spin_unlock_irqrestore(&xhci->lock, flags);
+
+       if (!xhci->zombie)
+               mod_timer(&xhci->event_ring_timer, jiffies + POLL_TIMEOUT * HZ);
+       else
+               xhci_dbg(xhci, "Quit polling the event ring.\n");
+}
+#endif
+
+/*
+ * Start the HC after it was halted.
+ *
+ * This function is called by the USB core when the HC driver is added.
+ * Its opposite is xhci_stop().
+ *
+ * xhci_init() must be called once before this function can be called.
+ * Reset the HC, enable device slot contexts, program DCBAAP, and
+ * set command ring pointer and event ring pointer.
+ *
+ * Setup MSI-X vectors and enable interrupts.
+ */
+int xhci_run(struct usb_hcd *hcd)
+{
+       u32 temp;
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       void (*doorbell)(struct xhci_hcd *) = NULL;
+
+       hcd->uses_new_polling = 1;
+       hcd->poll_rh = 0;
+
+       xhci_dbg(xhci, "xhci_run\n");
+#if 0  /* FIXME: MSI not setup yet */
+       /* Do this at the very last minute */
+       ret = xhci_setup_msix(xhci);
+       if (!ret)
+               return ret;
+
+       return -ENOSYS;
+#endif
+#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
+       init_timer(&xhci->event_ring_timer);
+       xhci->event_ring_timer.data = (unsigned long) xhci;
+       xhci->event_ring_timer.function = xhci_event_ring_work;
+       /* Poll the event ring */
+       xhci->event_ring_timer.expires = jiffies + POLL_TIMEOUT * HZ;
+       xhci->zombie = 0;
+       xhci_dbg(xhci, "Setting event ring polling timer\n");
+       add_timer(&xhci->event_ring_timer);
+#endif
+
+       xhci_dbg(xhci, "// Set the interrupt modulation register\n");
+       temp = xhci_readl(xhci, &xhci->ir_set->irq_control);
+       temp &= ~ER_IRQ_INTERVAL_MASK;
+       temp |= (u32) 160;
+       xhci_writel(xhci, temp, &xhci->ir_set->irq_control);
+
+       /* Set the HCD state before we enable the irqs */
+       hcd->state = HC_STATE_RUNNING;
+       temp = xhci_readl(xhci, &xhci->op_regs->command);
+       temp |= (CMD_EIE);
+       xhci_dbg(xhci, "// Enable interrupts, cmd = 0x%x.\n",
+                       temp);
+       xhci_writel(xhci, temp, &xhci->op_regs->command);
+
+       temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+       xhci_dbg(xhci, "// Enabling event ring interrupter %p by writing 0x%x to irq_pending\n",
+                       xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp));
+       xhci_writel(xhci, ER_IRQ_ENABLE(temp),
+                       &xhci->ir_set->irq_pending);
+       xhci_print_ir_set(xhci, xhci->ir_set, 0);
+
+       if (NUM_TEST_NOOPS > 0)
+               doorbell = xhci_setup_one_noop(xhci);
+
+       xhci_dbg(xhci, "Command ring memory map follows:\n");
+       xhci_debug_ring(xhci, xhci->cmd_ring);
+       xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring);
+       xhci_dbg_cmd_ptrs(xhci);
+
+       xhci_dbg(xhci, "ERST memory map follows:\n");
+       xhci_dbg_erst(xhci, &xhci->erst);
+       xhci_dbg(xhci, "Event ring:\n");
+       xhci_debug_ring(xhci, xhci->event_ring);
+       xhci_dbg_ring_ptrs(xhci, xhci->event_ring);
+       temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[0]);
+       temp &= ERST_PTR_MASK;
+       xhci_dbg(xhci, "ERST deq = 0x%x\n", temp);
+       temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[1]);
+       xhci_dbg(xhci, "ERST deq upper = 0x%x\n", temp);
+
+       temp = xhci_readl(xhci, &xhci->op_regs->command);
+       temp |= (CMD_RUN);
+       xhci_dbg(xhci, "// Turn on HC, cmd = 0x%x.\n",
+                       temp);
+       xhci_writel(xhci, temp, &xhci->op_regs->command);
+       /* Flush PCI posted writes */
+       temp = xhci_readl(xhci, &xhci->op_regs->command);
+       xhci_dbg(xhci, "// @%p = 0x%x\n", &xhci->op_regs->command, temp);
+       if (doorbell)
+               (*doorbell)(xhci);
+
+       xhci_dbg(xhci, "Finished xhci_run\n");
+       return 0;
+}
+
+/*
+ * Stop xHCI driver.
+ *
+ * This function is called by the USB core when the HC driver is removed.
+ * Its opposite is xhci_run().
+ *
+ * Disable device contexts, disable IRQs, and quiesce the HC.
+ * Reset the HC, finish any completed transactions, and cleanup memory.
+ */
+void xhci_stop(struct usb_hcd *hcd)
+{
+       u32 temp;
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+       spin_lock_irq(&xhci->lock);
+       if (HC_IS_RUNNING(hcd->state))
+               xhci_quiesce(xhci);
+       xhci_halt(xhci);
+       xhci_reset(xhci);
+       spin_unlock_irq(&xhci->lock);
+
+#if 0  /* No MSI yet */
+       xhci_cleanup_msix(xhci);
+#endif
+#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
+       /* Tell the event ring poll function not to reschedule */
+       xhci->zombie = 1;
+       del_timer_sync(&xhci->event_ring_timer);
+#endif
+
+       xhci_dbg(xhci, "// Disabling event ring interrupts\n");
+       temp = xhci_readl(xhci, &xhci->op_regs->status);
+       xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
+       temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+       xhci_writel(xhci, ER_IRQ_DISABLE(temp),
+                       &xhci->ir_set->irq_pending);
+       xhci_print_ir_set(xhci, xhci->ir_set, 0);
+
+       xhci_dbg(xhci, "cleaning up memory\n");
+       xhci_mem_cleanup(xhci);
+       xhci_dbg(xhci, "xhci_stop completed - status = %x\n",
+                   xhci_readl(xhci, &xhci->op_regs->status));
+}
+
+/*
+ * Shutdown HC (not bus-specific)
+ *
+ * This is called when the machine is rebooting or halting.  We assume that the
+ * machine will be powered off, and the HC's internal state will be reset.
+ * Don't bother to free memory.
+ */
+void xhci_shutdown(struct usb_hcd *hcd)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+       spin_lock_irq(&xhci->lock);
+       xhci_halt(xhci);
+       spin_unlock_irq(&xhci->lock);
+
+#if 0
+       xhci_cleanup_msix(xhci);
+#endif
+
+       xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n",
+                   xhci_readl(xhci, &xhci->op_regs->status));
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and
+ * HCDs.  Find the index for an endpoint given its descriptor.  Use the return
+ * value to right shift 1 for the bitmask.
+ *
+ * Index  = (epnum * 2) + direction - 1,
+ * where direction = 0 for OUT, 1 for IN.
+ * For control endpoints, the IN index is used (OUT index is unused), so
+ * index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2)
+ */
+unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc)
+{
+       unsigned int index;
+       if (usb_endpoint_xfer_control(desc))
+               index = (unsigned int) (usb_endpoint_num(desc)*2);
+       else
+               index = (unsigned int) (usb_endpoint_num(desc)*2) +
+                       (usb_endpoint_dir_in(desc) ? 1 : 0) - 1;
+       return index;
+}
+
+/* Find the flag for this endpoint (for use in the control context).  Use the
+ * endpoint index to create a bitmask.  The slot context is bit 0, endpoint 0 is
+ * bit 1, etc.
+ */
+unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc)
+{
+       return 1 << (xhci_get_endpoint_index(desc) + 1);
+}
+
+/* Compute the last valid endpoint context index.  Basically, this is the
+ * endpoint index plus one.  For slot contexts with more than valid endpoint,
+ * we find the most significant bit set in the added contexts flags.
+ * e.g. ep 1 IN (with epnum 0x81) => added_ctxs = 0b1000
+ * fls(0b1000) = 4, but the endpoint context index is 3, so subtract one.
+ */
+static inline unsigned int xhci_last_valid_endpoint(u32 added_ctxs)
+{
+       return fls(added_ctxs) - 1;
+}
+
+/* Returns 1 if the arguments are OK;
+ * returns 0 this is a root hub; returns -EINVAL for NULL pointers.
+ */
+int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
+               struct usb_host_endpoint *ep, int check_ep, const char *func) {
+       if (!hcd || (check_ep && !ep) || !udev) {
+               printk(KERN_DEBUG "xHCI %s called with invalid args\n",
+                               func);
+               return -EINVAL;
+       }
+       if (!udev->parent) {
+               printk(KERN_DEBUG "xHCI %s called for root hub\n",
+                               func);
+               return 0;
+       }
+       if (!udev->slot_id) {
+               printk(KERN_DEBUG "xHCI %s called with unaddressed device\n",
+                               func);
+               return -EINVAL;
+       }
+       return 1;
+}
+
+/*
+ * non-error returns are a promise to giveback() the urb later
+ * we drop ownership so next owner (or urb unlink) can get it
+ */
+int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       unsigned long flags;
+       int ret = 0;
+       unsigned int slot_id, ep_index;
+
+       if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0)
+               return -EINVAL;
+
+       slot_id = urb->dev->slot_id;
+       ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+
+       spin_lock_irqsave(&xhci->lock, flags);
+       if (!xhci->devs || !xhci->devs[slot_id]) {
+               if (!in_interrupt())
+                       dev_warn(&urb->dev->dev, "WARN: urb submitted for dev with no Slot ID\n");
+               ret = -EINVAL;
+               goto exit;
+       }
+       if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+               if (!in_interrupt())
+                       xhci_dbg(xhci, "urb submitted during PCI suspend\n");
+               ret = -ESHUTDOWN;
+               goto exit;
+       }
+       if (usb_endpoint_xfer_control(&urb->ep->desc))
+               ret = xhci_queue_ctrl_tx(xhci, mem_flags, urb,
+                               slot_id, ep_index);
+       else if (usb_endpoint_xfer_bulk(&urb->ep->desc))
+               ret = xhci_queue_bulk_tx(xhci, mem_flags, urb,
+                               slot_id, ep_index);
+       else
+               ret = -EINVAL;
+exit:
+       spin_unlock_irqrestore(&xhci->lock, flags);
+       return ret;
+}
+
+/*
+ * Remove the URB's TD from the endpoint ring.  This may cause the HC to stop
+ * USB transfers, potentially stopping in the middle of a TRB buffer.  The HC
+ * should pick up where it left off in the TD, unless a Set Transfer Ring
+ * Dequeue Pointer is issued.
+ *
+ * The TRBs that make up the buffers for the canceled URB will be "removed" from
+ * the ring.  Since the ring is a contiguous structure, they can't be physically
+ * removed.  Instead, there are two options:
+ *
+ *  1) If the HC is in the middle of processing the URB to be canceled, we
+ *     simply move the ring's dequeue pointer past those TRBs using the Set
+ *     Transfer Ring Dequeue Pointer command.  This will be the common case,
+ *     when drivers timeout on the last submitted URB and attempt to cancel.
+ *
+ *  2) If the HC is in the middle of a different TD, we turn the TRBs into a
+ *     series of 1-TRB transfer no-op TDs.  (No-ops shouldn't be chained.)  The
+ *     HC will need to invalidate the any TRBs it has cached after the stop
+ *     endpoint command, as noted in the xHCI 0.95 errata.
+ *
+ *  3) The TD may have completed by the time the Stop Endpoint Command
+ *     completes, so software needs to handle that case too.
+ *
+ * This function should protect against the TD enqueueing code ringing the
+ * doorbell while this code is waiting for a Stop Endpoint command to complete.
+ * It also needs to account for multiple cancellations on happening at the same
+ * time for the same endpoint.
+ *
+ * Note that this function can be called in any context, or so says
+ * usb_hcd_unlink_urb()
+ */
+int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+       unsigned long flags;
+       int ret;
+       struct xhci_hcd *xhci;
+       struct xhci_td *td;
+       unsigned int ep_index;
+       struct xhci_ring *ep_ring;
+
+       xhci = hcd_to_xhci(hcd);
+       spin_lock_irqsave(&xhci->lock, flags);
+       /* Make sure the URB hasn't completed or been unlinked already */
+       ret = usb_hcd_check_unlink_urb(hcd, urb, status);
+       if (ret || !urb->hcpriv)
+               goto done;
+
+       xhci_dbg(xhci, "Cancel URB %p\n", urb);
+       ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+       ep_ring = xhci->devs[urb->dev->slot_id]->ep_rings[ep_index];
+       td = (struct xhci_td *) urb->hcpriv;
+
+       ep_ring->cancels_pending++;
+       list_add_tail(&td->cancelled_td_list, &ep_ring->cancelled_td_list);
+       /* Queue a stop endpoint command, but only if this is
+        * the first cancellation to be handled.
+        */
+       if (ep_ring->cancels_pending == 1) {
+               xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index);
+               xhci_ring_cmd_db(xhci);
+       }
+done:
+       spin_unlock_irqrestore(&xhci->lock, flags);
+       return ret;
+}
+
+/* Drop an endpoint from a new bandwidth configuration for this device.
+ * Only one call to this function is allowed per endpoint before
+ * check_bandwidth() or reset_bandwidth() must be called.
+ * A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will
+ * add the endpoint to the schedule with possibly new parameters denoted by a
+ * different endpoint descriptor in usb_host_endpoint.
+ * A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is
+ * not allowed.
+ *
+ * The USB core will not allow URBs to be queued to an endpoint that is being
+ * disabled, so there's no need for mutual exclusion to protect
+ * the xhci->devs[slot_id] structure.
+ */
+int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+               struct usb_host_endpoint *ep)
+{
+       struct xhci_hcd *xhci;
+       struct xhci_device_control *in_ctx;
+       unsigned int last_ctx;
+       unsigned int ep_index;
+       struct xhci_ep_ctx *ep_ctx;
+       u32 drop_flag;
+       u32 new_add_flags, new_drop_flags, new_slot_info;
+       int ret;
+
+       ret = xhci_check_args(hcd, udev, ep, 1, __func__);
+       if (ret <= 0)
+               return ret;
+       xhci = hcd_to_xhci(hcd);
+       xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
+
+       drop_flag = xhci_get_endpoint_flag(&ep->desc);
+       if (drop_flag == SLOT_FLAG || drop_flag == EP0_FLAG) {
+               xhci_dbg(xhci, "xHCI %s - can't drop slot or ep 0 %#x\n",
+                               __func__, drop_flag);
+               return 0;
+       }
+
+       if (!xhci->devs || !xhci->devs[udev->slot_id]) {
+               xhci_warn(xhci, "xHCI %s called with unaddressed device\n",
+                               __func__);
+               return -EINVAL;
+       }
+
+       in_ctx = xhci->devs[udev->slot_id]->in_ctx;
+       ep_index = xhci_get_endpoint_index(&ep->desc);
+       ep_ctx = &xhci->devs[udev->slot_id]->out_ctx->ep[ep_index];
+       /* If the HC already knows the endpoint is disabled,
+        * or the HCD has noted it is disabled, ignore this request
+        */
+       if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED ||
+                       in_ctx->drop_flags & xhci_get_endpoint_flag(&ep->desc)) {
+               xhci_warn(xhci, "xHCI %s called with disabled ep %p\n",
+                               __func__, ep);
+               return 0;
+       }
+
+       in_ctx->drop_flags |= drop_flag;
+       new_drop_flags = in_ctx->drop_flags;
+
+       in_ctx->add_flags = ~drop_flag;
+       new_add_flags = in_ctx->add_flags;
+
+       last_ctx = xhci_last_valid_endpoint(in_ctx->add_flags);
+       /* Update the last valid endpoint context, if we deleted the last one */
+       if ((in_ctx->slot.dev_info & LAST_CTX_MASK) > LAST_CTX(last_ctx)) {
+               in_ctx->slot.dev_info &= ~LAST_CTX_MASK;
+               in_ctx->slot.dev_info |= LAST_CTX(last_ctx);
+       }
+       new_slot_info = in_ctx->slot.dev_info;
+
+       xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
+
+       xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
+                       (unsigned int) ep->desc.bEndpointAddress,
+                       udev->slot_id,
+                       (unsigned int) new_drop_flags,
+                       (unsigned int) new_add_flags,
+                       (unsigned int) new_slot_info);
+       return 0;
+}
+
+/* Add an endpoint to a new possible bandwidth configuration for this device.
+ * Only one call to this function is allowed per endpoint before
+ * check_bandwidth() or reset_bandwidth() must be called.
+ * A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will
+ * add the endpoint to the schedule with possibly new parameters denoted by a
+ * different endpoint descriptor in usb_host_endpoint.
+ * A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is
+ * not allowed.
+ *
+ * The USB core will not allow URBs to be queued to an endpoint until the
+ * configuration or alt setting is installed in the device, so there's no need
+ * for mutual exclusion to protect the xhci->devs[slot_id] structure.
+ */
+int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+               struct usb_host_endpoint *ep)
+{
+       struct xhci_hcd *xhci;
+       struct xhci_device_control *in_ctx;
+       unsigned int ep_index;
+       struct xhci_ep_ctx *ep_ctx;
+       u32 added_ctxs;
+       unsigned int last_ctx;
+       u32 new_add_flags, new_drop_flags, new_slot_info;
+       int ret = 0;
+
+       ret = xhci_check_args(hcd, udev, ep, 1, __func__);
+       if (ret <= 0)
+               return ret;
+       xhci = hcd_to_xhci(hcd);
+
+       added_ctxs = xhci_get_endpoint_flag(&ep->desc);
+       last_ctx = xhci_last_valid_endpoint(added_ctxs);
+       if (added_ctxs == SLOT_FLAG || added_ctxs == EP0_FLAG) {
+               /* FIXME when we have to issue an evaluate endpoint command to
+                * deal with ep0 max packet size changing once we get the
+                * descriptors
+                */
+               xhci_dbg(xhci, "xHCI %s - can't add slot or ep 0 %#x\n",
+                               __func__, added_ctxs);
+               return 0;
+       }
+
+       if (!xhci->devs || !xhci->devs[udev->slot_id]) {
+               xhci_warn(xhci, "xHCI %s called with unaddressed device\n",
+                               __func__);
+               return -EINVAL;
+       }
+
+       in_ctx = xhci->devs[udev->slot_id]->in_ctx;
+       ep_index = xhci_get_endpoint_index(&ep->desc);
+       ep_ctx = &xhci->devs[udev->slot_id]->out_ctx->ep[ep_index];
+       /* If the HCD has already noted the endpoint is enabled,
+        * ignore this request.
+        */
+       if (in_ctx->add_flags & xhci_get_endpoint_flag(&ep->desc)) {
+               xhci_warn(xhci, "xHCI %s called with enabled ep %p\n",
+                               __func__, ep);
+               return 0;
+       }
+
+       /*
+        * Configuration and alternate setting changes must be done in
+        * process context, not interrupt context (or so documenation
+        * for usb_set_interface() and usb_set_configuration() claim).
+        */
+       if (xhci_endpoint_init(xhci, xhci->devs[udev->slot_id],
+                               udev, ep, GFP_KERNEL) < 0) {
+               dev_dbg(&udev->dev, "%s - could not initialize ep %#x\n",
+                               __func__, ep->desc.bEndpointAddress);
+               return -ENOMEM;
+       }
+
+       in_ctx->add_flags |= added_ctxs;
+       new_add_flags = in_ctx->add_flags;
+
+       /* If xhci_endpoint_disable() was called for this endpoint, but the
+        * xHC hasn't been notified yet through the check_bandwidth() call,
+        * this re-adds a new state for the endpoint from the new endpoint
+        * descriptors.  We must drop and re-add this endpoint, so we leave the
+        * drop flags alone.
+        */
+       new_drop_flags = in_ctx->drop_flags;
+
+       /* Update the last valid endpoint context, if we just added one past */
+       if ((in_ctx->slot.dev_info & LAST_CTX_MASK) < LAST_CTX(last_ctx)) {
+               in_ctx->slot.dev_info &= ~LAST_CTX_MASK;
+               in_ctx->slot.dev_info |= LAST_CTX(last_ctx);
+       }
+       new_slot_info = in_ctx->slot.dev_info;
+
+       xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
+                       (unsigned int) ep->desc.bEndpointAddress,
+                       udev->slot_id,
+                       (unsigned int) new_drop_flags,
+                       (unsigned int) new_add_flags,
+                       (unsigned int) new_slot_info);
+       return 0;
+}
+
+static void xhci_zero_in_ctx(struct xhci_virt_device *virt_dev)
+{
+       struct xhci_ep_ctx *ep_ctx;
+       int i;
+
+       /* When a device's add flag and drop flag are zero, any subsequent
+        * configure endpoint command will leave that endpoint's state
+        * untouched.  Make sure we don't leave any old state in the input
+        * endpoint contexts.
+        */
+       virt_dev->in_ctx->drop_flags = 0;
+       virt_dev->in_ctx->add_flags = 0;
+       virt_dev->in_ctx->slot.dev_info &= ~LAST_CTX_MASK;
+       /* Endpoint 0 is always valid */
+       virt_dev->in_ctx->slot.dev_info |= LAST_CTX(1);
+       for (i = 1; i < 31; ++i) {
+               ep_ctx = &virt_dev->in_ctx->ep[i];
+               ep_ctx->ep_info = 0;
+               ep_ctx->ep_info2 = 0;
+               ep_ctx->deq[0] = 0;
+               ep_ctx->deq[1] = 0;
+               ep_ctx->tx_info = 0;
+       }
+}
+
+/* Called after one or more calls to xhci_add_endpoint() or
+ * xhci_drop_endpoint().  If this call fails, the USB core is expected
+ * to call xhci_reset_bandwidth().
+ *
+ * Since we are in the middle of changing either configuration or
+ * installing a new alt setting, the USB core won't allow URBs to be
+ * enqueued for any endpoint on the old config or interface.  Nothing
+ * else should be touching the xhci->devs[slot_id] structure, so we
+ * don't need to take the xhci->lock for manipulating that.
+ */
+int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       int i;
+       int ret = 0;
+       int timeleft;
+       unsigned long flags;
+       struct xhci_hcd *xhci;
+       struct xhci_virt_device *virt_dev;
+
+       ret = xhci_check_args(hcd, udev, NULL, 0, __func__);
+       if (ret <= 0)
+               return ret;
+       xhci = hcd_to_xhci(hcd);
+
+       if (!udev->slot_id || !xhci->devs || !xhci->devs[udev->slot_id]) {
+               xhci_warn(xhci, "xHCI %s called with unaddressed device\n",
+                               __func__);
+               return -EINVAL;
+       }
+       xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
+       virt_dev = xhci->devs[udev->slot_id];
+
+       /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */
+       virt_dev->in_ctx->add_flags |= SLOT_FLAG;
+       virt_dev->in_ctx->add_flags &= ~EP0_FLAG;
+       virt_dev->in_ctx->drop_flags &= ~SLOT_FLAG;
+       virt_dev->in_ctx->drop_flags &= ~EP0_FLAG;
+       xhci_dbg(xhci, "New Input Control Context:\n");
+       xhci_dbg_ctx(xhci, virt_dev->in_ctx, virt_dev->in_ctx_dma,
+                       LAST_CTX_TO_EP_NUM(virt_dev->in_ctx->slot.dev_info));
+
+       spin_lock_irqsave(&xhci->lock, flags);
+       ret = xhci_queue_configure_endpoint(xhci, virt_dev->in_ctx_dma,
+                       udev->slot_id);
+       if (ret < 0) {
+               spin_unlock_irqrestore(&xhci->lock, flags);
+               xhci_dbg(xhci, "FIXME allocate a new ring segment\n");
+               return -ENOMEM;
+       }
+       xhci_ring_cmd_db(xhci);
+       spin_unlock_irqrestore(&xhci->lock, flags);
+
+       /* Wait for the configure endpoint command to complete */
+       timeleft = wait_for_completion_interruptible_timeout(
+                       &virt_dev->cmd_completion,
+                       USB_CTRL_SET_TIMEOUT);
+       if (timeleft <= 0) {
+               xhci_warn(xhci, "%s while waiting for configure endpoint command\n",
+                               timeleft == 0 ? "Timeout" : "Signal");
+               /* FIXME cancel the configure endpoint command */
+               return -ETIME;
+       }
+
+       switch (virt_dev->cmd_status) {
+       case COMP_ENOMEM:
+               dev_warn(&udev->dev, "Not enough host controller resources "
+                               "for new device state.\n");
+               ret = -ENOMEM;
+               /* FIXME: can we allocate more resources for the HC? */
+               break;
+       case COMP_BW_ERR:
+               dev_warn(&udev->dev, "Not enough bandwidth "
+                               "for new device state.\n");
+               ret = -ENOSPC;
+               /* FIXME: can we go back to the old state? */
+               break;
+       case COMP_TRB_ERR:
+               /* the HCD set up something wrong */
+               dev_warn(&udev->dev, "ERROR: Endpoint drop flag = 0, add flag = 1, "
+                               "and endpoint is not disabled.\n");
+               ret = -EINVAL;
+               break;
+       case COMP_SUCCESS:
+               dev_dbg(&udev->dev, "Successful Endpoint Configure command\n");
+               break;
+       default:
+               xhci_err(xhci, "ERROR: unexpected command completion "
+                               "code 0x%x.\n", virt_dev->cmd_status);
+               ret = -EINVAL;
+               break;
+       }
+       if (ret) {
+               /* Callee should call reset_bandwidth() */
+               return ret;
+       }
+
+       xhci_dbg(xhci, "Output context after successful config ep cmd:\n");
+       xhci_dbg_ctx(xhci, virt_dev->out_ctx, virt_dev->out_ctx_dma,
+                       LAST_CTX_TO_EP_NUM(virt_dev->in_ctx->slot.dev_info));
+
+       xhci_zero_in_ctx(virt_dev);
+       /* Free any old rings */
+       for (i = 1; i < 31; ++i) {
+               if (virt_dev->new_ep_rings[i]) {
+                       xhci_ring_free(xhci, virt_dev->ep_rings[i]);
+                       virt_dev->ep_rings[i] = virt_dev->new_ep_rings[i];
+                       virt_dev->new_ep_rings[i] = NULL;
+               }
+       }
+
+       return ret;
+}
+
+void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct xhci_hcd *xhci;
+       struct xhci_virt_device *virt_dev;
+       int i, ret;
+
+       ret = xhci_check_args(hcd, udev, NULL, 0, __func__);
+       if (ret <= 0)
+               return;
+       xhci = hcd_to_xhci(hcd);
+
+       if (!xhci->devs || !xhci->devs[udev->slot_id]) {
+               xhci_warn(xhci, "xHCI %s called with unaddressed device\n",
+                               __func__);
+               return;
+       }
+       xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
+       virt_dev = xhci->devs[udev->slot_id];
+       /* Free any rings allocated for added endpoints */
+       for (i = 0; i < 31; ++i) {
+               if (virt_dev->new_ep_rings[i]) {
+                       xhci_ring_free(xhci, virt_dev->new_ep_rings[i]);
+                       virt_dev->new_ep_rings[i] = NULL;
+               }
+       }
+       xhci_zero_in_ctx(virt_dev);
+}
+
+/*
+ * At this point, the struct usb_device is about to go away, the device has
+ * disconnected, and all traffic has been stopped and the endpoints have been
+ * disabled.  Free any HC data structures associated with that device.
+ */
+void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       unsigned long flags;
+
+       if (udev->slot_id == 0)
+               return;
+
+       spin_lock_irqsave(&xhci->lock, flags);
+       if (xhci_queue_slot_control(xhci, TRB_DISABLE_SLOT, udev->slot_id)) {
+               spin_unlock_irqrestore(&xhci->lock, flags);
+               xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
+               return;
+       }
+       xhci_ring_cmd_db(xhci);
+       spin_unlock_irqrestore(&xhci->lock, flags);
+       /*
+        * Event command completion handler will free any data structures
+        * associated with the slot.  XXX Can free sleep?
+        */
+}
+
+/*
+ * Returns 0 if the xHC ran out of device slots, the Enable Slot command
+ * timed out, or allocating memory failed.  Returns 1 on success.
+ */
+int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       unsigned long flags;
+       int timeleft;
+       int ret;
+
+       spin_lock_irqsave(&xhci->lock, flags);
+       ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
+       if (ret) {
+               spin_unlock_irqrestore(&xhci->lock, flags);
+               xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
+               return 0;
+       }
+       xhci_ring_cmd_db(xhci);
+       spin_unlock_irqrestore(&xhci->lock, flags);
+
+       /* XXX: how much time for xHC slot assignment? */
+       timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
+                       USB_CTRL_SET_TIMEOUT);
+       if (timeleft <= 0) {
+               xhci_warn(xhci, "%s while waiting for a slot\n",
+                               timeleft == 0 ? "Timeout" : "Signal");
+               /* FIXME cancel the enable slot request */
+               return 0;
+       }
+
+       if (!xhci->slot_id) {
+               xhci_err(xhci, "Error while assigning device slot ID\n");
+               return 0;
+       }
+       /* xhci_alloc_virt_device() does not touch rings; no need to lock */
+       if (!xhci_alloc_virt_device(xhci, xhci->slot_id, udev, GFP_KERNEL)) {
+               /* Disable slot, if we can do it without mem alloc */
+               xhci_warn(xhci, "Could not allocate xHCI USB device data structures\n");
+               spin_lock_irqsave(&xhci->lock, flags);
+               if (!xhci_queue_slot_control(xhci, TRB_DISABLE_SLOT, udev->slot_id))
+                       xhci_ring_cmd_db(xhci);
+               spin_unlock_irqrestore(&xhci->lock, flags);
+               return 0;
+       }
+       udev->slot_id = xhci->slot_id;
+       /* Is this a LS or FS device under a HS hub? */
+       /* Hub or peripherial? */
+       return 1;
+}
+
+/*
+ * Issue an Address Device command (which will issue a SetAddress request to
+ * the device).
+ * We should be protected by the usb_address0_mutex in khubd's hub_port_init, so
+ * we should only issue and wait on one address command at the same time.
+ *
+ * We add one to the device address issued by the hardware because the USB core
+ * uses address 1 for the root hubs (even though they're not really devices).
+ */
+int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       unsigned long flags;
+       int timeleft;
+       struct xhci_virt_device *virt_dev;
+       int ret = 0;
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       u32 temp;
+
+       if (!udev->slot_id) {
+               xhci_dbg(xhci, "Bad Slot ID %d\n", udev->slot_id);
+               return -EINVAL;
+       }
+
+       virt_dev = xhci->devs[udev->slot_id];
+
+       /* If this is a Set Address to an unconfigured device, setup ep 0 */
+       if (!udev->config)
+               xhci_setup_addressable_virt_dev(xhci, udev);
+       /* Otherwise, assume the core has the device configured how it wants */
+
+       spin_lock_irqsave(&xhci->lock, flags);
+       ret = xhci_queue_address_device(xhci, virt_dev->in_ctx_dma,
+                       udev->slot_id);
+       if (ret) {
+               spin_unlock_irqrestore(&xhci->lock, flags);
+               xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
+               return ret;
+       }
+       xhci_ring_cmd_db(xhci);
+       spin_unlock_irqrestore(&xhci->lock, flags);
+
+       /* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */
+       timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
+                       USB_CTRL_SET_TIMEOUT);
+       /* FIXME: From section 4.3.4: "Software shall be responsible for timing
+        * the SetAddress() "recovery interval" required by USB and aborting the
+        * command on a timeout.
+        */
+       if (timeleft <= 0) {
+               xhci_warn(xhci, "%s while waiting for a slot\n",
+                               timeleft == 0 ? "Timeout" : "Signal");
+               /* FIXME cancel the address device command */
+               return -ETIME;
+       }
+
+       switch (virt_dev->cmd_status) {
+       case COMP_CTX_STATE:
+       case COMP_EBADSLT:
+               xhci_err(xhci, "Setup ERROR: address device command for slot %d.\n",
+                               udev->slot_id);
+               ret = -EINVAL;
+               break;
+       case COMP_TX_ERR:
+               dev_warn(&udev->dev, "Device not responding to set address.\n");
+               ret = -EPROTO;
+               break;
+       case COMP_SUCCESS:
+               xhci_dbg(xhci, "Successful Address Device command\n");
+               break;
+       default:
+               xhci_err(xhci, "ERROR: unexpected command completion "
+                               "code 0x%x.\n", virt_dev->cmd_status);
+               ret = -EINVAL;
+               break;
+       }
+       if (ret) {
+               return ret;
+       }
+       temp = xhci_readl(xhci, &xhci->op_regs->dcbaa_ptr[0]);
+       xhci_dbg(xhci, "Op regs DCBAA ptr[0] = %#08x\n", temp);
+       temp = xhci_readl(xhci, &xhci->op_regs->dcbaa_ptr[1]);
+       xhci_dbg(xhci, "Op regs DCBAA ptr[1] = %#08x\n", temp);
+       xhci_dbg(xhci, "Slot ID %d dcbaa entry[0] @%p = %#08x\n",
+                       udev->slot_id,
+                       &xhci->dcbaa->dev_context_ptrs[2*udev->slot_id],
+                       xhci->dcbaa->dev_context_ptrs[2*udev->slot_id]);
+       xhci_dbg(xhci, "Slot ID %d dcbaa entry[1] @%p = %#08x\n",
+                       udev->slot_id,
+                       &xhci->dcbaa->dev_context_ptrs[2*udev->slot_id+1],
+                       xhci->dcbaa->dev_context_ptrs[2*udev->slot_id+1]);
+       xhci_dbg(xhci, "Output Context DMA address = %#08llx\n",
+                       (unsigned long long)virt_dev->out_ctx_dma);
+       xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id);
+       xhci_dbg_ctx(xhci, virt_dev->in_ctx, virt_dev->in_ctx_dma, 2);
+       xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id);
+       xhci_dbg_ctx(xhci, virt_dev->out_ctx, virt_dev->out_ctx_dma, 2);
+       /*
+        * USB core uses address 1 for the roothubs, so we add one to the
+        * address given back to us by the HC.
+        */
+       udev->devnum = (virt_dev->out_ctx->slot.dev_state & DEV_ADDR_MASK) + 1;
+       /* Zero the input context control for later use */
+       virt_dev->in_ctx->add_flags = 0;
+       virt_dev->in_ctx->drop_flags = 0;
+       /* Mirror flags in the output context for future ep enable/disable */
+       virt_dev->out_ctx->add_flags = SLOT_FLAG | EP0_FLAG;
+       virt_dev->out_ctx->drop_flags = 0;
+
+       xhci_dbg(xhci, "Device address = %d\n", udev->devnum);
+       /* XXX Meh, not sure if anyone else but choose_address uses this. */
+       set_bit(udev->devnum, udev->bus->devmap.devicemap);
+
+       return 0;
+}
+
+int xhci_get_frame(struct usb_hcd *hcd)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       /* EHCI mods by the periodic size.  Why? */
+       return xhci_readl(xhci, &xhci->run_regs->microframe_index) >> 3;
+}
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+
+static int __init xhci_hcd_init(void)
+{
+#ifdef CONFIG_PCI
+       int retval = 0;
+
+       retval = xhci_register_pci();
+
+       if (retval < 0) {
+               printk(KERN_DEBUG "Problem registering PCI driver.");
+               return retval;
+       }
+#endif
+       /*
+        * Check the compiler generated sizes of structures that must be laid
+        * out in specific ways for hardware access.
+        */
+       BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_slot_ctx) != 8*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_ep_ctx) != 8*32/8);
+       /* xhci_device_control has eight fields, and also
+        * embeds one xhci_slot_ctx and 31 xhci_ep_ctx
+        */
+       BUILD_BUG_ON(sizeof(struct xhci_device_control) != (8+8+8*31)*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_stream_ctx) != 4*32/8);
+       BUILD_BUG_ON(sizeof(union xhci_trb) != 4*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 7*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
+       /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
+       BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8);
+       return 0;
+}
+module_init(xhci_hcd_init);
+
+static void __exit xhci_hcd_cleanup(void)
+{
+#ifdef CONFIG_PCI
+       xhci_unregister_pci();
+#endif
+}
+module_exit(xhci_hcd_cleanup);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
new file mode 100644 (file)
index 0000000..eac5b53
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/unaligned.h>
+
+#include "xhci.h"
+
+static void xhci_hub_descriptor(struct xhci_hcd *xhci,
+               struct usb_hub_descriptor *desc)
+{
+       int ports;
+       u16 temp;
+
+       ports = HCS_MAX_PORTS(xhci->hcs_params1);
+
+       /* USB 3.0 hubs have a different descriptor, but we fake this for now */
+       desc->bDescriptorType = 0x29;
+       desc->bPwrOn2PwrGood = 10;      /* xhci section 5.4.9 says 20ms max */
+       desc->bHubContrCurrent = 0;
+
+       desc->bNbrPorts = ports;
+       temp = 1 + (ports / 8);
+       desc->bDescLength = 7 + 2 * temp;
+
+       /* Why does core/hcd.h define bitmap?  It's just confusing. */
+       memset(&desc->DeviceRemovable[0], 0, temp);
+       memset(&desc->DeviceRemovable[temp], 0xff, temp);
+
+       /* Ugh, these should be #defines, FIXME */
+       /* Using table 11-13 in USB 2.0 spec. */
+       temp = 0;
+       /* Bits 1:0 - support port power switching, or power always on */
+       if (HCC_PPC(xhci->hcc_params))
+               temp |= 0x0001;
+       else
+               temp |= 0x0002;
+       /* Bit  2 - root hubs are not part of a compound device */
+       /* Bits 4:3 - individual port over current protection */
+       temp |= 0x0008;
+       /* Bits 6:5 - no TTs in root ports */
+       /* Bit  7 - no port indicators */
+       desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp);
+}
+
+static unsigned int xhci_port_speed(unsigned int port_status)
+{
+       if (DEV_LOWSPEED(port_status))
+               return 1 << USB_PORT_FEAT_LOWSPEED;
+       if (DEV_HIGHSPEED(port_status))
+               return 1 << USB_PORT_FEAT_HIGHSPEED;
+       if (DEV_SUPERSPEED(port_status))
+               return 1 << USB_PORT_FEAT_SUPERSPEED;
+       /*
+        * FIXME: Yes, we should check for full speed, but the core uses that as
+        * a default in portspeed() in usb/core/hub.c (which is the only place
+        * USB_PORT_FEAT_*SPEED is used).
+        */
+       return 0;
+}
+
+/*
+ * These bits are Read Only (RO) and should be saved and written to the
+ * registers: 0, 3, 10:13, 30
+ * connect status, over-current status, port speed, and device removable.
+ * connect status and port speed are also sticky - meaning they're in
+ * the AUX well and they aren't changed by a hot, warm, or cold reset.
+ */
+#define        XHCI_PORT_RO    ((1<<0) | (1<<3) | (0xf<<10) | (1<<30))
+/*
+ * These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit:
+ * bits 5:8, 9, 14:15, 25:27
+ * link state, port power, port indicator state, "wake on" enable state
+ */
+#define XHCI_PORT_RWS  ((0xf<<5) | (1<<9) | (0x3<<14) | (0x7<<25))
+/*
+ * These bits are RW; writing a 1 sets the bit, writing a 0 has no effect:
+ * bit 4 (port reset)
+ */
+#define        XHCI_PORT_RW1S  ((1<<4))
+/*
+ * These bits are RW; writing a 1 clears the bit, writing a 0 has no effect:
+ * bits 1, 17, 18, 19, 20, 21, 22, 23
+ * port enable/disable, and
+ * change bits: connect, PED, warm port reset changed (reserved zero for USB 2.0 ports),
+ * over-current, reset, link state, and L1 change
+ */
+#define XHCI_PORT_RW1CS        ((1<<1) | (0x7f<<17))
+/*
+ * Bit 16 is RW, and writing a '1' to it causes the link state control to be
+ * latched in
+ */
+#define        XHCI_PORT_RW    ((1<<16))
+/*
+ * These bits are Reserved Zero (RsvdZ) and zero should be written to them:
+ * bits 2, 24, 28:31
+ */
+#define        XHCI_PORT_RZ    ((1<<2) | (1<<24) | (0xf<<28))
+
+/*
+ * Given a port state, this function returns a value that would result in the
+ * port being in the same state, if the value was written to the port status
+ * control register.
+ * Save Read Only (RO) bits and save read/write bits where
+ * writing a 0 clears the bit and writing a 1 sets the bit (RWS).
+ * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect.
+ */
+static u32 xhci_port_state_to_neutral(u32 state)
+{
+       /* Save read-only status and port state */
+       return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
+}
+
+int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+               u16 wIndex, char *buf, u16 wLength)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       int ports;
+       unsigned long flags;
+       u32 temp, status;
+       int retval = 0;
+       u32 __iomem *addr;
+       char *port_change_bit;
+
+       ports = HCS_MAX_PORTS(xhci->hcs_params1);
+
+       spin_lock_irqsave(&xhci->lock, flags);
+       switch (typeReq) {
+       case GetHubStatus:
+               /* No power source, over-current reported per port */
+               memset(buf, 0, 4);
+               break;
+       case GetHubDescriptor:
+               xhci_hub_descriptor(xhci, (struct usb_hub_descriptor *) buf);
+               break;
+       case GetPortStatus:
+               if (!wIndex || wIndex > ports)
+                       goto error;
+               wIndex--;
+               status = 0;
+               addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
+               temp = xhci_readl(xhci, addr);
+               xhci_dbg(xhci, "get port status, actual port %d status  = 0x%x\n", wIndex, temp);
+
+               /* wPortChange bits */
+               if (temp & PORT_CSC)
+                       status |= 1 << USB_PORT_FEAT_C_CONNECTION;
+               if (temp & PORT_PEC)
+                       status |= 1 << USB_PORT_FEAT_C_ENABLE;
+               if ((temp & PORT_OCC))
+                       status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
+               /*
+                * FIXME ignoring suspend, reset, and USB 2.1/3.0 specific
+                * changes
+                */
+               if (temp & PORT_CONNECT) {
+                       status |= 1 << USB_PORT_FEAT_CONNECTION;
+                       status |= xhci_port_speed(temp);
+               }
+               if (temp & PORT_PE)
+                       status |= 1 << USB_PORT_FEAT_ENABLE;
+               if (temp & PORT_OC)
+                       status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
+               if (temp & PORT_RESET)
+                       status |= 1 << USB_PORT_FEAT_RESET;
+               if (temp & PORT_POWER)
+                       status |= 1 << USB_PORT_FEAT_POWER;
+               xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
+               put_unaligned(cpu_to_le32(status), (__le32 *) buf);
+               break;
+       case SetPortFeature:
+               wIndex &= 0xff;
+               if (!wIndex || wIndex > ports)
+                       goto error;
+               wIndex--;
+               addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
+               temp = xhci_readl(xhci, addr);
+               temp = xhci_port_state_to_neutral(temp);
+               switch (wValue) {
+               case USB_PORT_FEAT_POWER:
+                       /*
+                        * Turn on ports, even if there isn't per-port switching.
+                        * HC will report connect events even before this is set.
+                        * However, khubd will ignore the roothub events until
+                        * the roothub is registered.
+                        */
+                       xhci_writel(xhci, temp | PORT_POWER, addr);
+
+                       temp = xhci_readl(xhci, addr);
+                       xhci_dbg(xhci, "set port power, actual port %d status  = 0x%x\n", wIndex, temp);
+                       break;
+               case USB_PORT_FEAT_RESET:
+                       temp = (temp | PORT_RESET);
+                       xhci_writel(xhci, temp, addr);
+
+                       temp = xhci_readl(xhci, addr);
+                       xhci_dbg(xhci, "set port reset, actual port %d status  = 0x%x\n", wIndex, temp);
+                       break;
+               default:
+                       goto error;
+               }
+               temp = xhci_readl(xhci, addr); /* unblock any posted writes */
+               break;
+       case ClearPortFeature:
+               if (!wIndex || wIndex > ports)
+                       goto error;
+               wIndex--;
+               addr = &xhci->op_regs->port_status_base +
+                       NUM_PORT_REGS*(wIndex & 0xff);
+               temp = xhci_readl(xhci, addr);
+               temp = xhci_port_state_to_neutral(temp);
+               switch (wValue) {
+               case USB_PORT_FEAT_C_RESET:
+                       status = PORT_RC;
+                       port_change_bit = "reset";
+                       break;
+               case USB_PORT_FEAT_C_CONNECTION:
+                       status = PORT_CSC;
+                       port_change_bit = "connect";
+                       break;
+               case USB_PORT_FEAT_C_OVER_CURRENT:
+                       status = PORT_OCC;
+                       port_change_bit = "over-current";
+                       break;
+               default:
+                       goto error;
+               }
+               /* Change bits are all write 1 to clear */
+               xhci_writel(xhci, temp | status, addr);
+               temp = xhci_readl(xhci, addr);
+               xhci_dbg(xhci, "clear port %s change, actual port %d status  = 0x%x\n",
+                               port_change_bit, wIndex, temp);
+               temp = xhci_readl(xhci, addr); /* unblock any posted writes */
+               break;
+       default:
+error:
+               /* "stall" on error */
+               retval = -EPIPE;
+       }
+       spin_unlock_irqrestore(&xhci->lock, flags);
+       return retval;
+}
+
+/*
+ * Returns 0 if the status hasn't changed, or the number of bytes in buf.
+ * Ports are 0-indexed from the HCD point of view,
+ * and 1-indexed from the USB core pointer of view.
+ * xHCI instances can have up to 127 ports, so FIXME if you see more than 15.
+ *
+ * Note that the status change bits will be cleared as soon as a port status
+ * change event is generated, so we use the saved status from that event.
+ */
+int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+       unsigned long flags;
+       u32 temp, status;
+       int i, retval;
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       int ports;
+       u32 __iomem *addr;
+
+       ports = HCS_MAX_PORTS(xhci->hcs_params1);
+
+       /* Initial status is no changes */
+       buf[0] = 0;
+       status = 0;
+       if (ports > 7) {
+               buf[1] = 0;
+               retval = 2;
+       } else {
+               retval = 1;
+       }
+
+       spin_lock_irqsave(&xhci->lock, flags);
+       /* For each port, did anything change?  If so, set that bit in buf. */
+       for (i = 0; i < ports; i++) {
+               addr = &xhci->op_regs->port_status_base +
+                       NUM_PORT_REGS*i;
+               temp = xhci_readl(xhci, addr);
+               if (temp & (PORT_CSC | PORT_PEC | PORT_OCC)) {
+                       if (i < 7)
+                               buf[0] |= 1 << (i + 1);
+                       else
+                               buf[1] |= 1 << (i - 7);
+                       status = 1;
+               }
+       }
+       spin_unlock_irqrestore(&xhci->lock, flags);
+       return status ? retval : 0;
+}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
new file mode 100644 (file)
index 0000000..c8a72de
--- /dev/null
@@ -0,0 +1,769 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/usb.h>
+#include <linux/pci.h>
+#include <linux/dmapool.h>
+
+#include "xhci.h"
+
+/*
+ * Allocates a generic ring segment from the ring pool, sets the dma address,
+ * initializes the segment to zero, and sets the private next pointer to NULL.
+ *
+ * Section 4.11.1.1:
+ * "All components of all Command and Transfer TRBs shall be initialized to '0'"
+ */
+static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flags)
+{
+       struct xhci_segment *seg;
+       dma_addr_t      dma;
+
+       seg = kzalloc(sizeof *seg, flags);
+       if (!seg)
+               return 0;
+       xhci_dbg(xhci, "Allocating priv segment structure at %p\n", seg);
+
+       seg->trbs = dma_pool_alloc(xhci->segment_pool, flags, &dma);
+       if (!seg->trbs) {
+               kfree(seg);
+               return 0;
+       }
+       xhci_dbg(xhci, "// Allocating segment at %p (virtual) 0x%llx (DMA)\n",
+                       seg->trbs, (unsigned long long)dma);
+
+       memset(seg->trbs, 0, SEGMENT_SIZE);
+       seg->dma = dma;
+       seg->next = NULL;
+
+       return seg;
+}
+
+static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
+{
+       if (!seg)
+               return;
+       if (seg->trbs) {
+               xhci_dbg(xhci, "Freeing DMA segment at %p (virtual) 0x%llx (DMA)\n",
+                               seg->trbs, (unsigned long long)seg->dma);
+               dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
+               seg->trbs = NULL;
+       }
+       xhci_dbg(xhci, "Freeing priv segment structure at %p\n", seg);
+       kfree(seg);
+}
+
+/*
+ * Make the prev segment point to the next segment.
+ *
+ * Change the last TRB in the prev segment to be a Link TRB which points to the
+ * DMA address of the next segment.  The caller needs to set any Link TRB
+ * related flags, such as End TRB, Toggle Cycle, and no snoop.
+ */
+static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
+               struct xhci_segment *next, bool link_trbs)
+{
+       u32 val;
+
+       if (!prev || !next)
+               return;
+       prev->next = next;
+       if (link_trbs) {
+               prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr[0] = next->dma;
+
+               /* Set the last TRB in the segment to have a TRB type ID of Link TRB */
+               val = prev->trbs[TRBS_PER_SEGMENT-1].link.control;
+               val &= ~TRB_TYPE_BITMASK;
+               val |= TRB_TYPE(TRB_LINK);
+               prev->trbs[TRBS_PER_SEGMENT-1].link.control = val;
+       }
+       xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n",
+                       (unsigned long long)prev->dma,
+                       (unsigned long long)next->dma);
+}
+
+/* XXX: Do we need the hcd structure in all these functions? */
+void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
+{
+       struct xhci_segment *seg;
+       struct xhci_segment *first_seg;
+
+       if (!ring || !ring->first_seg)
+               return;
+       first_seg = ring->first_seg;
+       seg = first_seg->next;
+       xhci_dbg(xhci, "Freeing ring at %p\n", ring);
+       while (seg != first_seg) {
+               struct xhci_segment *next = seg->next;
+               xhci_segment_free(xhci, seg);
+               seg = next;
+       }
+       xhci_segment_free(xhci, first_seg);
+       ring->first_seg = NULL;
+       kfree(ring);
+}
+
+/**
+ * Create a new ring with zero or more segments.
+ *
+ * Link each segment together into a ring.
+ * Set the end flag and the cycle toggle bit on the last segment.
+ * See section 4.9.1 and figures 15 and 16.
+ */
+static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
+               unsigned int num_segs, bool link_trbs, gfp_t flags)
+{
+       struct xhci_ring        *ring;
+       struct xhci_segment     *prev;
+
+       ring = kzalloc(sizeof *(ring), flags);
+       xhci_dbg(xhci, "Allocating ring at %p\n", ring);
+       if (!ring)
+               return 0;
+
+       INIT_LIST_HEAD(&ring->td_list);
+       INIT_LIST_HEAD(&ring->cancelled_td_list);
+       if (num_segs == 0)
+               return ring;
+
+       ring->first_seg = xhci_segment_alloc(xhci, flags);
+       if (!ring->first_seg)
+               goto fail;
+       num_segs--;
+
+       prev = ring->first_seg;
+       while (num_segs > 0) {
+               struct xhci_segment     *next;
+
+               next = xhci_segment_alloc(xhci, flags);
+               if (!next)
+                       goto fail;
+               xhci_link_segments(xhci, prev, next, link_trbs);
+
+               prev = next;
+               num_segs--;
+       }
+       xhci_link_segments(xhci, prev, ring->first_seg, link_trbs);
+
+       if (link_trbs) {
+               /* See section 4.9.2.1 and 6.4.4.1 */
+               prev->trbs[TRBS_PER_SEGMENT-1].link.control |= (LINK_TOGGLE);
+               xhci_dbg(xhci, "Wrote link toggle flag to"
+                               " segment %p (virtual), 0x%llx (DMA)\n",
+                               prev, (unsigned long long)prev->dma);
+       }
+       /* The ring is empty, so the enqueue pointer == dequeue pointer */
+       ring->enqueue = ring->first_seg->trbs;
+       ring->enq_seg = ring->first_seg;
+       ring->dequeue = ring->enqueue;
+       ring->deq_seg = ring->first_seg;
+       /* The ring is initialized to 0. The producer must write 1 to the cycle
+        * bit to handover ownership of the TRB, so PCS = 1.  The consumer must
+        * compare CCS to the cycle bit to check ownership, so CCS = 1.
+        */
+       ring->cycle_state = 1;
+
+       return ring;
+
+fail:
+       xhci_ring_free(xhci, ring);
+       return 0;
+}
+
+/* All the xhci_tds in the ring's TD list should be freed at this point */
+void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
+{
+       struct xhci_virt_device *dev;
+       int i;
+
+       /* Slot ID 0 is reserved */
+       if (slot_id == 0 || !xhci->devs[slot_id])
+               return;
+
+       dev = xhci->devs[slot_id];
+       xhci->dcbaa->dev_context_ptrs[2*slot_id] = 0;
+       xhci->dcbaa->dev_context_ptrs[2*slot_id + 1] = 0;
+       if (!dev)
+               return;
+
+       for (i = 0; i < 31; ++i)
+               if (dev->ep_rings[i])
+                       xhci_ring_free(xhci, dev->ep_rings[i]);
+
+       if (dev->in_ctx)
+               dma_pool_free(xhci->device_pool,
+                               dev->in_ctx, dev->in_ctx_dma);
+       if (dev->out_ctx)
+               dma_pool_free(xhci->device_pool,
+                               dev->out_ctx, dev->out_ctx_dma);
+       kfree(xhci->devs[slot_id]);
+       xhci->devs[slot_id] = 0;
+}
+
+int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
+               struct usb_device *udev, gfp_t flags)
+{
+       dma_addr_t      dma;
+       struct xhci_virt_device *dev;
+
+       /* Slot ID 0 is reserved */
+       if (slot_id == 0 || xhci->devs[slot_id]) {
+               xhci_warn(xhci, "Bad Slot ID %d\n", slot_id);
+               return 0;
+       }
+
+       xhci->devs[slot_id] = kzalloc(sizeof(*xhci->devs[slot_id]), flags);
+       if (!xhci->devs[slot_id])
+               return 0;
+       dev = xhci->devs[slot_id];
+
+       /* Allocate the (output) device context that will be used in the HC */
+       dev->out_ctx = dma_pool_alloc(xhci->device_pool, flags, &dma);
+       if (!dev->out_ctx)
+               goto fail;
+       dev->out_ctx_dma = dma;
+       xhci_dbg(xhci, "Slot %d output ctx = 0x%llx (dma)\n", slot_id,
+                       (unsigned long long)dma);
+       memset(dev->out_ctx, 0, sizeof(*dev->out_ctx));
+
+       /* Allocate the (input) device context for address device command */
+       dev->in_ctx = dma_pool_alloc(xhci->device_pool, flags, &dma);
+       if (!dev->in_ctx)
+               goto fail;
+       dev->in_ctx_dma = dma;
+       xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id,
+                       (unsigned long long)dma);
+       memset(dev->in_ctx, 0, sizeof(*dev->in_ctx));
+
+       /* Allocate endpoint 0 ring */
+       dev->ep_rings[0] = xhci_ring_alloc(xhci, 1, true, flags);
+       if (!dev->ep_rings[0])
+               goto fail;
+
+       init_completion(&dev->cmd_completion);
+
+       /*
+        * Point to output device context in dcbaa; skip the output control
+        * context, which is eight 32 bit fields (or 32 bytes long)
+        */
+       xhci->dcbaa->dev_context_ptrs[2*slot_id] =
+               (u32) dev->out_ctx_dma + (32);
+       xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n",
+                       slot_id,
+                       &xhci->dcbaa->dev_context_ptrs[2*slot_id],
+                       (unsigned long long)dev->out_ctx_dma);
+       xhci->dcbaa->dev_context_ptrs[2*slot_id + 1] = 0;
+
+       return 1;
+fail:
+       xhci_free_virt_device(xhci, slot_id);
+       return 0;
+}
+
+/* Setup an xHCI virtual device for a Set Address command */
+int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev)
+{
+       struct xhci_virt_device *dev;
+       struct xhci_ep_ctx      *ep0_ctx;
+       struct usb_device       *top_dev;
+
+       dev = xhci->devs[udev->slot_id];
+       /* Slot ID 0 is reserved */
+       if (udev->slot_id == 0 || !dev) {
+               xhci_warn(xhci, "Slot ID %d is not assigned to this device\n",
+                               udev->slot_id);
+               return -EINVAL;
+       }
+       ep0_ctx = &dev->in_ctx->ep[0];
+
+       /* 2) New slot context and endpoint 0 context are valid*/
+       dev->in_ctx->add_flags = SLOT_FLAG | EP0_FLAG;
+
+       /* 3) Only the control endpoint is valid - one endpoint context */
+       dev->in_ctx->slot.dev_info |= LAST_CTX(1);
+
+       switch (udev->speed) {
+       case USB_SPEED_SUPER:
+               dev->in_ctx->slot.dev_info |= (u32) udev->route;
+               dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_SS;
+               break;
+       case USB_SPEED_HIGH:
+               dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_HS;
+               break;
+       case USB_SPEED_FULL:
+               dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_FS;
+               break;
+       case USB_SPEED_LOW:
+               dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_LS;
+               break;
+       case USB_SPEED_VARIABLE:
+               xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n");
+               return -EINVAL;
+               break;
+       default:
+               /* Speed was set earlier, this shouldn't happen. */
+               BUG();
+       }
+       /* Find the root hub port this device is under */
+       for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
+                       top_dev = top_dev->parent)
+               /* Found device below root hub */;
+       dev->in_ctx->slot.dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum);
+       xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum);
+
+       /* Is this a LS/FS device under a HS hub? */
+       /*
+        * FIXME: I don't think this is right, where does the TT info for the
+        * roothub or parent hub come from?
+        */
+       if ((udev->speed == USB_SPEED_LOW || udev->speed == USB_SPEED_FULL) &&
+                       udev->tt) {
+               dev->in_ctx->slot.tt_info = udev->tt->hub->slot_id;
+               dev->in_ctx->slot.tt_info |= udev->ttport << 8;
+       }
+       xhci_dbg(xhci, "udev->tt = %p\n", udev->tt);
+       xhci_dbg(xhci, "udev->ttport = 0x%x\n", udev->ttport);
+
+       /* Step 4 - ring already allocated */
+       /* Step 5 */
+       ep0_ctx->ep_info2 = EP_TYPE(CTRL_EP);
+       /*
+        * See section 4.3 bullet 6:
+        * The default Max Packet size for ep0 is "8 bytes for a USB2
+        * LS/FS/HS device or 512 bytes for a USB3 SS device"
+        * XXX: Not sure about wireless USB devices.
+        */
+       if (udev->speed == USB_SPEED_SUPER)
+               ep0_ctx->ep_info2 |= MAX_PACKET(512);
+       else
+               ep0_ctx->ep_info2 |= MAX_PACKET(8);
+       /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */
+       ep0_ctx->ep_info2 |= MAX_BURST(0);
+       ep0_ctx->ep_info2 |= ERROR_COUNT(3);
+
+       ep0_ctx->deq[0] =
+               dev->ep_rings[0]->first_seg->dma;
+       ep0_ctx->deq[0] |= dev->ep_rings[0]->cycle_state;
+       ep0_ctx->deq[1] = 0;
+
+       /* Steps 7 and 8 were done in xhci_alloc_virt_device() */
+
+       return 0;
+}
+
+/* Return the polling or NAK interval.
+ *
+ * The polling interval is expressed in "microframes".  If xHCI's Interval field
+ * is set to N, it will service the endpoint every 2^(Interval)*125us.
+ *
+ * The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval
+ * is set to 0.
+ */
+static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
+               struct usb_host_endpoint *ep)
+{
+       unsigned int interval = 0;
+
+       switch (udev->speed) {
+       case USB_SPEED_HIGH:
+               /* Max NAK rate */
+               if (usb_endpoint_xfer_control(&ep->desc) ||
+                               usb_endpoint_xfer_bulk(&ep->desc))
+                       interval = ep->desc.bInterval;
+               /* Fall through - SS and HS isoc/int have same decoding */
+       case USB_SPEED_SUPER:
+               if (usb_endpoint_xfer_int(&ep->desc) ||
+                               usb_endpoint_xfer_isoc(&ep->desc)) {
+                       if (ep->desc.bInterval == 0)
+                               interval = 0;
+                       else
+                               interval = ep->desc.bInterval - 1;
+                       if (interval > 15)
+                               interval = 15;
+                       if (interval != ep->desc.bInterval + 1)
+                               dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n",
+                                               ep->desc.bEndpointAddress, 1 << interval);
+               }
+               break;
+       /* Convert bInterval (in 1-255 frames) to microframes and round down to
+        * nearest power of 2.
+        */
+       case USB_SPEED_FULL:
+       case USB_SPEED_LOW:
+               if (usb_endpoint_xfer_int(&ep->desc) ||
+                               usb_endpoint_xfer_isoc(&ep->desc)) {
+                       interval = fls(8*ep->desc.bInterval) - 1;
+                       if (interval > 10)
+                               interval = 10;
+                       if (interval < 3)
+                               interval = 3;
+                       if ((1 << interval) != 8*ep->desc.bInterval)
+                               dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n",
+                                               ep->desc.bEndpointAddress, 1 << interval);
+               }
+               break;
+       default:
+               BUG();
+       }
+       return EP_INTERVAL(interval);
+}
+
+static inline u32 xhci_get_endpoint_type(struct usb_device *udev,
+               struct usb_host_endpoint *ep)
+{
+       int in;
+       u32 type;
+
+       in = usb_endpoint_dir_in(&ep->desc);
+       if (usb_endpoint_xfer_control(&ep->desc)) {
+               type = EP_TYPE(CTRL_EP);
+       } else if (usb_endpoint_xfer_bulk(&ep->desc)) {
+               if (in)
+                       type = EP_TYPE(BULK_IN_EP);
+               else
+                       type = EP_TYPE(BULK_OUT_EP);
+       } else if (usb_endpoint_xfer_isoc(&ep->desc)) {
+               if (in)
+                       type = EP_TYPE(ISOC_IN_EP);
+               else
+                       type = EP_TYPE(ISOC_OUT_EP);
+       } else if (usb_endpoint_xfer_int(&ep->desc)) {
+               if (in)
+                       type = EP_TYPE(INT_IN_EP);
+               else
+                       type = EP_TYPE(INT_OUT_EP);
+       } else {
+               BUG();
+       }
+       return type;
+}
+
+int xhci_endpoint_init(struct xhci_hcd *xhci,
+               struct xhci_virt_device *virt_dev,
+               struct usb_device *udev,
+               struct usb_host_endpoint *ep,
+               gfp_t mem_flags)
+{
+       unsigned int ep_index;
+       struct xhci_ep_ctx *ep_ctx;
+       struct xhci_ring *ep_ring;
+       unsigned int max_packet;
+       unsigned int max_burst;
+
+       ep_index = xhci_get_endpoint_index(&ep->desc);
+       ep_ctx = &virt_dev->in_ctx->ep[ep_index];
+
+       /* Set up the endpoint ring */
+       virt_dev->new_ep_rings[ep_index] = xhci_ring_alloc(xhci, 1, true, mem_flags);
+       if (!virt_dev->new_ep_rings[ep_index])
+               return -ENOMEM;
+       ep_ring = virt_dev->new_ep_rings[ep_index];
+       ep_ctx->deq[0] = ep_ring->first_seg->dma | ep_ring->cycle_state;
+       ep_ctx->deq[1] = 0;
+
+       ep_ctx->ep_info = xhci_get_endpoint_interval(udev, ep);
+
+       /* FIXME dig Mult and streams info out of ep companion desc */
+
+       /* Allow 3 retries for everything but isoc */
+       if (!usb_endpoint_xfer_isoc(&ep->desc))
+               ep_ctx->ep_info2 = ERROR_COUNT(3);
+       else
+               ep_ctx->ep_info2 = ERROR_COUNT(0);
+
+       ep_ctx->ep_info2 |= xhci_get_endpoint_type(udev, ep);
+
+       /* Set the max packet size and max burst */
+       switch (udev->speed) {
+       case USB_SPEED_SUPER:
+               max_packet = ep->desc.wMaxPacketSize;
+               ep_ctx->ep_info2 |= MAX_PACKET(max_packet);
+               /* dig out max burst from ep companion desc */
+               max_packet = ep->ss_ep_comp->desc.bMaxBurst;
+               ep_ctx->ep_info2 |= MAX_BURST(max_packet);
+               break;
+       case USB_SPEED_HIGH:
+               /* bits 11:12 specify the number of additional transaction
+                * opportunities per microframe (USB 2.0, section 9.6.6)
+                */
+               if (usb_endpoint_xfer_isoc(&ep->desc) ||
+                               usb_endpoint_xfer_int(&ep->desc)) {
+                       max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11;
+                       ep_ctx->ep_info2 |= MAX_BURST(max_burst);
+               }
+               /* Fall through */
+       case USB_SPEED_FULL:
+       case USB_SPEED_LOW:
+               max_packet = ep->desc.wMaxPacketSize & 0x3ff;
+               ep_ctx->ep_info2 |= MAX_PACKET(max_packet);
+               break;
+       default:
+               BUG();
+       }
+       /* FIXME Debug endpoint context */
+       return 0;
+}
+
+void xhci_endpoint_zero(struct xhci_hcd *xhci,
+               struct xhci_virt_device *virt_dev,
+               struct usb_host_endpoint *ep)
+{
+       unsigned int ep_index;
+       struct xhci_ep_ctx *ep_ctx;
+
+       ep_index = xhci_get_endpoint_index(&ep->desc);
+       ep_ctx = &virt_dev->in_ctx->ep[ep_index];
+
+       ep_ctx->ep_info = 0;
+       ep_ctx->ep_info2 = 0;
+       ep_ctx->deq[0] = 0;
+       ep_ctx->deq[1] = 0;
+       ep_ctx->tx_info = 0;
+       /* Don't free the endpoint ring until the set interface or configuration
+        * request succeeds.
+        */
+}
+
+void xhci_mem_cleanup(struct xhci_hcd *xhci)
+{
+       struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+       int size;
+       int i;
+
+       /* Free the Event Ring Segment Table and the actual Event Ring */
+       xhci_writel(xhci, 0, &xhci->ir_set->erst_size);
+       xhci_writel(xhci, 0, &xhci->ir_set->erst_base[0]);
+       xhci_writel(xhci, 0, &xhci->ir_set->erst_base[1]);
+       xhci_writel(xhci, 0, &xhci->ir_set->erst_dequeue[0]);
+       xhci_writel(xhci, 0, &xhci->ir_set->erst_dequeue[1]);
+       size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
+       if (xhci->erst.entries)
+               pci_free_consistent(pdev, size,
+                               xhci->erst.entries, xhci->erst.erst_dma_addr);
+       xhci->erst.entries = NULL;
+       xhci_dbg(xhci, "Freed ERST\n");
+       if (xhci->event_ring)
+               xhci_ring_free(xhci, xhci->event_ring);
+       xhci->event_ring = NULL;
+       xhci_dbg(xhci, "Freed event ring\n");
+
+       xhci_writel(xhci, 0, &xhci->op_regs->cmd_ring[0]);
+       xhci_writel(xhci, 0, &xhci->op_regs->cmd_ring[1]);
+       if (xhci->cmd_ring)
+               xhci_ring_free(xhci, xhci->cmd_ring);
+       xhci->cmd_ring = NULL;
+       xhci_dbg(xhci, "Freed command ring\n");
+
+       for (i = 1; i < MAX_HC_SLOTS; ++i)
+               xhci_free_virt_device(xhci, i);
+
+       if (xhci->segment_pool)
+               dma_pool_destroy(xhci->segment_pool);
+       xhci->segment_pool = NULL;
+       xhci_dbg(xhci, "Freed segment pool\n");
+
+       if (xhci->device_pool)
+               dma_pool_destroy(xhci->device_pool);
+       xhci->device_pool = NULL;
+       xhci_dbg(xhci, "Freed device context pool\n");
+
+       xhci_writel(xhci, 0, &xhci->op_regs->dcbaa_ptr[0]);
+       xhci_writel(xhci, 0, &xhci->op_regs->dcbaa_ptr[1]);
+       if (xhci->dcbaa)
+               pci_free_consistent(pdev, sizeof(*xhci->dcbaa),
+                               xhci->dcbaa, xhci->dcbaa->dma);
+       xhci->dcbaa = NULL;
+
+       xhci->page_size = 0;
+       xhci->page_shift = 0;
+}
+
+int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
+{
+       dma_addr_t      dma;
+       struct device   *dev = xhci_to_hcd(xhci)->self.controller;
+       unsigned int    val, val2;
+       struct xhci_segment     *seg;
+       u32 page_size;
+       int i;
+
+       page_size = xhci_readl(xhci, &xhci->op_regs->page_size);
+       xhci_dbg(xhci, "Supported page size register = 0x%x\n", page_size);
+       for (i = 0; i < 16; i++) {
+               if ((0x1 & page_size) != 0)
+                       break;
+               page_size = page_size >> 1;
+       }
+       if (i < 16)
+               xhci_dbg(xhci, "Supported page size of %iK\n", (1 << (i+12)) / 1024);
+       else
+               xhci_warn(xhci, "WARN: no supported page size\n");
+       /* Use 4K pages, since that's common and the minimum the HC supports */
+       xhci->page_shift = 12;
+       xhci->page_size = 1 << xhci->page_shift;
+       xhci_dbg(xhci, "HCD page size set to %iK\n", xhci->page_size / 1024);
+
+       /*
+        * Program the Number of Device Slots Enabled field in the CONFIG
+        * register with the max value of slots the HC can handle.
+        */
+       val = HCS_MAX_SLOTS(xhci_readl(xhci, &xhci->cap_regs->hcs_params1));
+       xhci_dbg(xhci, "// xHC can handle at most %d device slots.\n",
+                       (unsigned int) val);
+       val2 = xhci_readl(xhci, &xhci->op_regs->config_reg);
+       val |= (val2 & ~HCS_SLOTS_MASK);
+       xhci_dbg(xhci, "// Setting Max device slots reg = 0x%x.\n",
+                       (unsigned int) val);
+       xhci_writel(xhci, val, &xhci->op_regs->config_reg);
+
+       /*
+        * Section 5.4.8 - doorbell array must be
+        * "physically contiguous and 64-byte (cache line) aligned".
+        */
+       xhci->dcbaa = pci_alloc_consistent(to_pci_dev(dev),
+                       sizeof(*xhci->dcbaa), &dma);
+       if (!xhci->dcbaa)
+               goto fail;
+       memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
+       xhci->dcbaa->dma = dma;
+       xhci_dbg(xhci, "// Device context base array address = 0x%llx (DMA), %p (virt)\n",
+                       (unsigned long long)xhci->dcbaa->dma, xhci->dcbaa);
+       xhci_writel(xhci, dma, &xhci->op_regs->dcbaa_ptr[0]);
+       xhci_writel(xhci, (u32) 0, &xhci->op_regs->dcbaa_ptr[1]);
+
+       /*
+        * Initialize the ring segment pool.  The ring must be a contiguous
+        * structure comprised of TRBs.  The TRBs must be 16 byte aligned,
+        * however, the command ring segment needs 64-byte aligned segments,
+        * so we pick the greater alignment need.
+        */
+       xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
+                       SEGMENT_SIZE, 64, xhci->page_size);
+       /* See Table 46 and Note on Figure 55 */
+       /* FIXME support 64-byte contexts */
+       xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
+                       sizeof(struct xhci_device_control),
+                       64, xhci->page_size);
+       if (!xhci->segment_pool || !xhci->device_pool)
+               goto fail;
+
+       /* Set up the command ring to have one segments for now. */
+       xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, flags);
+       if (!xhci->cmd_ring)
+               goto fail;
+       xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring);
+       xhci_dbg(xhci, "First segment DMA is 0x%llx\n",
+                       (unsigned long long)xhci->cmd_ring->first_seg->dma);
+
+       /* Set the address in the Command Ring Control register */
+       val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[0]);
+       val = (val & ~CMD_RING_ADDR_MASK) |
+               (xhci->cmd_ring->first_seg->dma & CMD_RING_ADDR_MASK) |
+               xhci->cmd_ring->cycle_state;
+       xhci_dbg(xhci, "// Setting command ring address low bits to 0x%x\n", val);
+       xhci_writel(xhci, val, &xhci->op_regs->cmd_ring[0]);
+       xhci_dbg(xhci, "// Setting command ring address high bits to 0x0\n");
+       xhci_writel(xhci, (u32) 0, &xhci->op_regs->cmd_ring[1]);
+       xhci_dbg_cmd_ptrs(xhci);
+
+       val = xhci_readl(xhci, &xhci->cap_regs->db_off);
+       val &= DBOFF_MASK;
+       xhci_dbg(xhci, "// Doorbell array is located at offset 0x%x"
+                       " from cap regs base addr\n", val);
+       xhci->dba = (void *) xhci->cap_regs + val;
+       xhci_dbg_regs(xhci);
+       xhci_print_run_regs(xhci);
+       /* Set ir_set to interrupt register set 0 */
+       xhci->ir_set = (void *) xhci->run_regs->ir_set;
+
+       /*
+        * Event ring setup: Allocate a normal ring, but also setup
+        * the event ring segment table (ERST).  Section 4.9.3.
+        */
+       xhci_dbg(xhci, "// Allocating event ring\n");
+       xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags);
+       if (!xhci->event_ring)
+               goto fail;
+
+       xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev),
+                       sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma);
+       if (!xhci->erst.entries)
+               goto fail;
+       xhci_dbg(xhci, "// Allocated event ring segment table at 0x%llx\n",
+                       (unsigned long long)dma);
+
+       memset(xhci->erst.entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS);
+       xhci->erst.num_entries = ERST_NUM_SEGS;
+       xhci->erst.erst_dma_addr = dma;
+       xhci_dbg(xhci, "Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx\n",
+                       xhci->erst.num_entries,
+                       xhci->erst.entries,
+                       (unsigned long long)xhci->erst.erst_dma_addr);
+
+       /* set ring base address and size for each segment table entry */
+       for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) {
+               struct xhci_erst_entry *entry = &xhci->erst.entries[val];
+               entry->seg_addr[0] = seg->dma;
+               entry->seg_addr[1] = 0;
+               entry->seg_size = TRBS_PER_SEGMENT;
+               entry->rsvd = 0;
+               seg = seg->next;
+       }
+
+       /* set ERST count with the number of entries in the segment table */
+       val = xhci_readl(xhci, &xhci->ir_set->erst_size);
+       val &= ERST_SIZE_MASK;
+       val |= ERST_NUM_SEGS;
+       xhci_dbg(xhci, "// Write ERST size = %i to ir_set 0 (some bits preserved)\n",
+                       val);
+       xhci_writel(xhci, val, &xhci->ir_set->erst_size);
+
+       xhci_dbg(xhci, "// Set ERST entries to point to event ring.\n");
+       /* set the segment table base address */
+       xhci_dbg(xhci, "// Set ERST base address for ir_set 0 = 0x%llx\n",
+                       (unsigned long long)xhci->erst.erst_dma_addr);
+       val = xhci_readl(xhci, &xhci->ir_set->erst_base[0]);
+       val &= ERST_PTR_MASK;
+       val |= (xhci->erst.erst_dma_addr & ~ERST_PTR_MASK);
+       xhci_writel(xhci, val, &xhci->ir_set->erst_base[0]);
+       xhci_writel(xhci, 0, &xhci->ir_set->erst_base[1]);
+
+       /* Set the event ring dequeue address */
+       xhci_set_hc_event_deq(xhci);
+       xhci_dbg(xhci, "Wrote ERST address to ir_set 0.\n");
+       xhci_print_ir_set(xhci, xhci->ir_set, 0);
+
+       /*
+        * XXX: Might need to set the Interrupter Moderation Register to
+        * something other than the default (~1ms minimum between interrupts).
+        * See section 5.5.1.2.
+        */
+       init_completion(&xhci->addr_dev);
+       for (i = 0; i < MAX_HC_SLOTS; ++i)
+               xhci->devs[i] = 0;
+
+       return 0;
+fail:
+       xhci_warn(xhci, "Couldn't initialize memory\n");
+       xhci_mem_cleanup(xhci);
+       return -ENOMEM;
+}
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
new file mode 100644 (file)
index 0000000..1462709
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * xHCI host controller driver PCI Bus Glue.
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/pci.h>
+
+#include "xhci.h"
+
+static const char hcd_name[] = "xhci_hcd";
+
+/* called after powerup, by probe or system-pm "wakeup" */
+static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
+{
+       /*
+        * TODO: Implement finding debug ports later.
+        * TODO: see if there are any quirks that need to be added to handle
+        * new extended capabilities.
+        */
+
+       /* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
+       if (!pci_set_mwi(pdev))
+               xhci_dbg(xhci, "MWI active\n");
+
+       xhci_dbg(xhci, "Finished xhci_pci_reinit\n");
+       return 0;
+}
+
+/* called during probe() after chip reset completes */
+static int xhci_pci_setup(struct usb_hcd *hcd)
+{
+       struct xhci_hcd         *xhci = hcd_to_xhci(hcd);
+       struct pci_dev          *pdev = to_pci_dev(hcd->self.controller);
+       int                     retval;
+
+       xhci->cap_regs = hcd->regs;
+       xhci->op_regs = hcd->regs +
+               HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase));
+       xhci->run_regs = hcd->regs +
+               (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
+       /* Cache read-only capability registers */
+       xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1);
+       xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2);
+       xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
+       xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+       xhci_print_registers(xhci);
+
+       /* Make sure the HC is halted. */
+       retval = xhci_halt(xhci);
+       if (retval)
+               return retval;
+
+       xhci_dbg(xhci, "Resetting HCD\n");
+       /* Reset the internal HC memory state and registers. */
+       retval = xhci_reset(xhci);
+       if (retval)
+               return retval;
+       xhci_dbg(xhci, "Reset complete\n");
+
+       xhci_dbg(xhci, "Calling HCD init\n");
+       /* Initialize HCD and host controller data structures. */
+       retval = xhci_init(hcd);
+       if (retval)
+               return retval;
+       xhci_dbg(xhci, "Called HCD init\n");
+
+       pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
+       xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);
+
+       /* Find any debug ports */
+       return xhci_pci_reinit(xhci, pdev);
+}
+
+static const struct hc_driver xhci_pci_hc_driver = {
+       .description =          hcd_name,
+       .product_desc =         "xHCI Host Controller",
+       .hcd_priv_size =        sizeof(struct xhci_hcd),
+
+       /*
+        * generic hardware linkage
+        */
+       .irq =                  xhci_irq,
+       .flags =                HCD_MEMORY | HCD_USB3,
+
+       /*
+        * basic lifecycle operations
+        */
+       .reset =                xhci_pci_setup,
+       .start =                xhci_run,
+       /* suspend and resume implemented later */
+       .stop =                 xhci_stop,
+       .shutdown =             xhci_shutdown,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       .urb_enqueue =          xhci_urb_enqueue,
+       .urb_dequeue =          xhci_urb_dequeue,
+       .alloc_dev =            xhci_alloc_dev,
+       .free_dev =             xhci_free_dev,
+       .add_endpoint =         xhci_add_endpoint,
+       .drop_endpoint =        xhci_drop_endpoint,
+       .check_bandwidth =      xhci_check_bandwidth,
+       .reset_bandwidth =      xhci_reset_bandwidth,
+       .address_device =       xhci_address_device,
+
+       /*
+        * scheduling support
+        */
+       .get_frame_number =     xhci_get_frame,
+
+       /* Root hub support */
+       .hub_control =          xhci_hub_control,
+       .hub_status_data =      xhci_hub_status_data,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* PCI driver selection metadata; PCI hotplugging uses this */
+static const struct pci_device_id pci_ids[] = { {
+       /* handle any USB 3.0 xHCI controller */
+       PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
+       .driver_data =  (unsigned long) &xhci_pci_hc_driver,
+       },
+       { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver xhci_pci_driver = {
+       .name =         (char *) hcd_name,
+       .id_table =     pci_ids,
+
+       .probe =        usb_hcd_pci_probe,
+       .remove =       usb_hcd_pci_remove,
+       /* suspend and resume implemented later */
+
+       .shutdown =     usb_hcd_pci_shutdown,
+};
+
+int xhci_register_pci()
+{
+       return pci_register_driver(&xhci_pci_driver);
+}
+
+void xhci_unregister_pci()
+{
+       pci_unregister_driver(&xhci_pci_driver);
+}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
new file mode 100644 (file)
index 0000000..02d8198
--- /dev/null
@@ -0,0 +1,1648 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Ring initialization rules:
+ * 1. Each segment is initialized to zero, except for link TRBs.
+ * 2. Ring cycle state = 0.  This represents Producer Cycle State (PCS) or
+ *    Consumer Cycle State (CCS), depending on ring function.
+ * 3. Enqueue pointer = dequeue pointer = address of first TRB in the segment.
+ *
+ * Ring behavior rules:
+ * 1. A ring is empty if enqueue == dequeue.  This means there will always be at
+ *    least one free TRB in the ring.  This is useful if you want to turn that
+ *    into a link TRB and expand the ring.
+ * 2. When incrementing an enqueue or dequeue pointer, if the next TRB is a
+ *    link TRB, then load the pointer with the address in the link TRB.  If the
+ *    link TRB had its toggle bit set, you may need to update the ring cycle
+ *    state (see cycle bit rules).  You may have to do this multiple times
+ *    until you reach a non-link TRB.
+ * 3. A ring is full if enqueue++ (for the definition of increment above)
+ *    equals the dequeue pointer.
+ *
+ * Cycle bit rules:
+ * 1. When a consumer increments a dequeue pointer and encounters a toggle bit
+ *    in a link TRB, it must toggle the ring cycle state.
+ * 2. When a producer increments an enqueue pointer and encounters a toggle bit
+ *    in a link TRB, it must toggle the ring cycle state.
+ *
+ * Producer rules:
+ * 1. Check if ring is full before you enqueue.
+ * 2. Write the ring cycle state to the cycle bit in the TRB you're enqueuing.
+ *    Update enqueue pointer between each write (which may update the ring
+ *    cycle state).
+ * 3. Notify consumer.  If SW is producer, it rings the doorbell for command
+ *    and endpoint rings.  If HC is the producer for the event ring,
+ *    and it generates an interrupt according to interrupt modulation rules.
+ *
+ * Consumer rules:
+ * 1. Check if TRB belongs to you.  If the cycle bit == your ring cycle state,
+ *    the TRB is owned by the consumer.
+ * 2. Update dequeue pointer (which may update the ring cycle state) and
+ *    continue processing TRBs until you reach a TRB which is not owned by you.
+ * 3. Notify the producer.  SW is the consumer for the event ring, and it
+ *   updates event ring dequeue pointer.  HC is the consumer for the command and
+ *   endpoint rings; it generates events on the event ring for these.
+ */
+
+#include <linux/scatterlist.h>
+#include "xhci.h"
+
+/*
+ * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
+ * address of the TRB.
+ */
+dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
+               union xhci_trb *trb)
+{
+       unsigned long segment_offset;
+
+       if (!seg || !trb || trb < seg->trbs)
+               return 0;
+       /* offset in TRBs */
+       segment_offset = trb - seg->trbs;
+       if (segment_offset > TRBS_PER_SEGMENT)
+               return 0;
+       return seg->dma + (segment_offset * sizeof(*trb));
+}
+
+/* Does this link TRB point to the first segment in a ring,
+ * or was the previous TRB the last TRB on the last segment in the ERST?
+ */
+static inline bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring,
+               struct xhci_segment *seg, union xhci_trb *trb)
+{
+       if (ring == xhci->event_ring)
+               return (trb == &seg->trbs[TRBS_PER_SEGMENT]) &&
+                       (seg->next == xhci->event_ring->first_seg);
+       else
+               return trb->link.control & LINK_TOGGLE;
+}
+
+/* Is this TRB a link TRB or was the last TRB the last TRB in this event ring
+ * segment?  I.e. would the updated event TRB pointer step off the end of the
+ * event seg?
+ */
+static inline int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
+               struct xhci_segment *seg, union xhci_trb *trb)
+{
+       if (ring == xhci->event_ring)
+               return trb == &seg->trbs[TRBS_PER_SEGMENT];
+       else
+               return (trb->link.control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK);
+}
+
+/* Updates trb to point to the next TRB in the ring, and updates seg if the next
+ * TRB is in a new segment.  This does not skip over link TRBs, and it does not
+ * effect the ring dequeue or enqueue pointers.
+ */
+static void next_trb(struct xhci_hcd *xhci,
+               struct xhci_ring *ring,
+               struct xhci_segment **seg,
+               union xhci_trb **trb)
+{
+       if (last_trb(xhci, ring, *seg, *trb)) {
+               *seg = (*seg)->next;
+               *trb = ((*seg)->trbs);
+       } else {
+               *trb = (*trb)++;
+       }
+}
+
+/*
+ * See Cycle bit rules. SW is the consumer for the event ring only.
+ * Don't make a ring full of link TRBs.  That would be dumb and this would loop.
+ */
+static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer)
+{
+       union xhci_trb *next = ++(ring->dequeue);
+
+       ring->deq_updates++;
+       /* Update the dequeue pointer further if that was a link TRB or we're at
+        * the end of an event ring segment (which doesn't have link TRBS)
+        */
+       while (last_trb(xhci, ring, ring->deq_seg, next)) {
+               if (consumer && last_trb_on_last_seg(xhci, ring, ring->deq_seg, next)) {
+                       ring->cycle_state = (ring->cycle_state ? 0 : 1);
+                       if (!in_interrupt())
+                               xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n",
+                                               ring,
+                                               (unsigned int) ring->cycle_state);
+               }
+               ring->deq_seg = ring->deq_seg->next;
+               ring->dequeue = ring->deq_seg->trbs;
+               next = ring->dequeue;
+       }
+}
+
+/*
+ * See Cycle bit rules. SW is the consumer for the event ring only.
+ * Don't make a ring full of link TRBs.  That would be dumb and this would loop.
+ *
+ * If we've just enqueued a TRB that is in the middle of a TD (meaning the
+ * chain bit is set), then set the chain bit in all the following link TRBs.
+ * If we've enqueued the last TRB in a TD, make sure the following link TRBs
+ * have their chain bit cleared (so that each Link TRB is a separate TD).
+ *
+ * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
+ * set, but other sections talk about dealing with the chain bit set.
+ * Assume section 6.4.4.1 is wrong, and the chain bit can be set in a Link TRB.
+ */
+static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer)
+{
+       u32 chain;
+       union xhci_trb *next;
+
+       chain = ring->enqueue->generic.field[3] & TRB_CHAIN;
+       next = ++(ring->enqueue);
+
+       ring->enq_updates++;
+       /* Update the dequeue pointer further if that was a link TRB or we're at
+        * the end of an event ring segment (which doesn't have link TRBS)
+        */
+       while (last_trb(xhci, ring, ring->enq_seg, next)) {
+               if (!consumer) {
+                       if (ring != xhci->event_ring) {
+                               next->link.control &= ~TRB_CHAIN;
+                               next->link.control |= chain;
+                               /* Give this link TRB to the hardware */
+                               wmb();
+                               if (next->link.control & TRB_CYCLE)
+                                       next->link.control &= (u32) ~TRB_CYCLE;
+                               else
+                                       next->link.control |= (u32) TRB_CYCLE;
+                       }
+                       /* Toggle the cycle bit after the last ring segment. */
+                       if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
+                               ring->cycle_state = (ring->cycle_state ? 0 : 1);
+                               if (!in_interrupt())
+                                       xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n",
+                                                       ring,
+                                                       (unsigned int) ring->cycle_state);
+                       }
+               }
+               ring->enq_seg = ring->enq_seg->next;
+               ring->enqueue = ring->enq_seg->trbs;
+               next = ring->enqueue;
+       }
+}
+
+/*
+ * Check to see if there's room to enqueue num_trbs on the ring.  See rules
+ * above.
+ * FIXME: this would be simpler and faster if we just kept track of the number
+ * of free TRBs in a ring.
+ */
+static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
+               unsigned int num_trbs)
+{
+       int i;
+       union xhci_trb *enq = ring->enqueue;
+       struct xhci_segment *enq_seg = ring->enq_seg;
+
+       /* Check if ring is empty */
+       if (enq == ring->dequeue)
+               return 1;
+       /* Make sure there's an extra empty TRB available */
+       for (i = 0; i <= num_trbs; ++i) {
+               if (enq == ring->dequeue)
+                       return 0;
+               enq++;
+               while (last_trb(xhci, ring, enq_seg, enq)) {
+                       enq_seg = enq_seg->next;
+                       enq = enq_seg->trbs;
+               }
+       }
+       return 1;
+}
+
+void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
+{
+       u32 temp;
+       dma_addr_t deq;
+
+       deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
+                       xhci->event_ring->dequeue);
+       if (deq == 0 && !in_interrupt())
+               xhci_warn(xhci, "WARN something wrong with SW event ring "
+                               "dequeue ptr.\n");
+       /* Update HC event ring dequeue pointer */
+       temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[0]);
+       temp &= ERST_PTR_MASK;
+       if (!in_interrupt())
+               xhci_dbg(xhci, "// Write event ring dequeue pointer\n");
+       xhci_writel(xhci, 0, &xhci->ir_set->erst_dequeue[1]);
+       xhci_writel(xhci, (deq & ~ERST_PTR_MASK) | temp,
+                       &xhci->ir_set->erst_dequeue[0]);
+}
+
+/* Ring the host controller doorbell after placing a command on the ring */
+void xhci_ring_cmd_db(struct xhci_hcd *xhci)
+{
+       u32 temp;
+
+       xhci_dbg(xhci, "// Ding dong!\n");
+       temp = xhci_readl(xhci, &xhci->dba->doorbell[0]) & DB_MASK;
+       xhci_writel(xhci, temp | DB_TARGET_HOST, &xhci->dba->doorbell[0]);
+       /* Flush PCI posted writes */
+       xhci_readl(xhci, &xhci->dba->doorbell[0]);
+}
+
+static void ring_ep_doorbell(struct xhci_hcd *xhci,
+               unsigned int slot_id,
+               unsigned int ep_index)
+{
+       struct xhci_ring *ep_ring;
+       u32 field;
+       __u32 __iomem *db_addr = &xhci->dba->doorbell[slot_id];
+
+       ep_ring = xhci->devs[slot_id]->ep_rings[ep_index];
+       /* Don't ring the doorbell for this endpoint if there are pending
+        * cancellations because the we don't want to interrupt processing.
+        */
+       if (!ep_ring->cancels_pending && !(ep_ring->state & SET_DEQ_PENDING)) {
+               field = xhci_readl(xhci, db_addr) & DB_MASK;
+               xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr);
+               /* Flush PCI posted writes - FIXME Matthew Wilcox says this
+                * isn't time-critical and we shouldn't make the CPU wait for
+                * the flush.
+                */
+               xhci_readl(xhci, db_addr);
+       }
+}
+
+/*
+ * Find the segment that trb is in.  Start searching in start_seg.
+ * If we must move past a segment that has a link TRB with a toggle cycle state
+ * bit set, then we will toggle the value pointed at by cycle_state.
+ */
+static struct xhci_segment *find_trb_seg(
+               struct xhci_segment *start_seg,
+               union xhci_trb  *trb, int *cycle_state)
+{
+       struct xhci_segment *cur_seg = start_seg;
+       struct xhci_generic_trb *generic_trb;
+
+       while (cur_seg->trbs > trb ||
+                       &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) {
+               generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic;
+               if (TRB_TYPE(generic_trb->field[3]) == TRB_LINK &&
+                               (generic_trb->field[3] & LINK_TOGGLE))
+                       *cycle_state = ~(*cycle_state) & 0x1;
+               cur_seg = cur_seg->next;
+               if (cur_seg == start_seg)
+                       /* Looped over the entire list.  Oops! */
+                       return 0;
+       }
+       return cur_seg;
+}
+
+struct dequeue_state {
+       struct xhci_segment *new_deq_seg;
+       union xhci_trb *new_deq_ptr;
+       int new_cycle_state;
+};
+
+/*
+ * Move the xHC's endpoint ring dequeue pointer past cur_td.
+ * Record the new state of the xHC's endpoint ring dequeue segment,
+ * dequeue pointer, and new consumer cycle state in state.
+ * Update our internal representation of the ring's dequeue pointer.
+ *
+ * We do this in three jumps:
+ *  - First we update our new ring state to be the same as when the xHC stopped.
+ *  - Then we traverse the ring to find the segment that contains
+ *    the last TRB in the TD.  We toggle the xHC's new cycle state when we pass
+ *    any link TRBs with the toggle cycle bit set.
+ *  - Finally we move the dequeue state one TRB further, toggling the cycle bit
+ *    if we've moved it past a link TRB with the toggle cycle bit set.
+ */
+static void find_new_dequeue_state(struct xhci_hcd *xhci,
+               unsigned int slot_id, unsigned int ep_index,
+               struct xhci_td *cur_td, struct dequeue_state *state)
+{
+       struct xhci_virt_device *dev = xhci->devs[slot_id];
+       struct xhci_ring *ep_ring = dev->ep_rings[ep_index];
+       struct xhci_generic_trb *trb;
+
+       state->new_cycle_state = 0;
+       state->new_deq_seg = find_trb_seg(cur_td->start_seg,
+                       ep_ring->stopped_trb,
+                       &state->new_cycle_state);
+       if (!state->new_deq_seg)
+               BUG();
+       /* Dig out the cycle state saved by the xHC during the stop ep cmd */
+       state->new_cycle_state = 0x1 & dev->out_ctx->ep[ep_index].deq[0];
+
+       state->new_deq_ptr = cur_td->last_trb;
+       state->new_deq_seg = find_trb_seg(state->new_deq_seg,
+                       state->new_deq_ptr,
+                       &state->new_cycle_state);
+       if (!state->new_deq_seg)
+               BUG();
+
+       trb = &state->new_deq_ptr->generic;
+       if (TRB_TYPE(trb->field[3]) == TRB_LINK &&
+                               (trb->field[3] & LINK_TOGGLE))
+               state->new_cycle_state = ~(state->new_cycle_state) & 0x1;
+       next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr);
+
+       /* Don't update the ring cycle state for the producer (us). */
+       ep_ring->dequeue = state->new_deq_ptr;
+       ep_ring->deq_seg = state->new_deq_seg;
+}
+
+static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
+               struct xhci_td *cur_td)
+{
+       struct xhci_segment *cur_seg;
+       union xhci_trb *cur_trb;
+
+       for (cur_seg = cur_td->start_seg, cur_trb = cur_td->first_trb;
+                       true;
+                       next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
+               if ((cur_trb->generic.field[3] & TRB_TYPE_BITMASK) ==
+                               TRB_TYPE(TRB_LINK)) {
+                       /* Unchain any chained Link TRBs, but
+                        * leave the pointers intact.
+                        */
+                       cur_trb->generic.field[3] &= ~TRB_CHAIN;
+                       xhci_dbg(xhci, "Cancel (unchain) link TRB\n");
+                       xhci_dbg(xhci, "Address = %p (0x%llx dma); "
+                                       "in seg %p (0x%llx dma)\n",
+                                       cur_trb,
+                                       (unsigned long long)xhci_trb_virt_to_dma(cur_seg, cur_trb),
+                                       cur_seg,
+                                       (unsigned long long)cur_seg->dma);
+               } else {
+                       cur_trb->generic.field[0] = 0;
+                       cur_trb->generic.field[1] = 0;
+                       cur_trb->generic.field[2] = 0;
+                       /* Preserve only the cycle bit of this TRB */
+                       cur_trb->generic.field[3] &= TRB_CYCLE;
+                       cur_trb->generic.field[3] |= TRB_TYPE(TRB_TR_NOOP);
+                       xhci_dbg(xhci, "Cancel TRB %p (0x%llx dma) "
+                                       "in seg %p (0x%llx dma)\n",
+                                       cur_trb,
+                                       (unsigned long long)xhci_trb_virt_to_dma(cur_seg, cur_trb),
+                                       cur_seg,
+                                       (unsigned long long)cur_seg->dma);
+               }
+               if (cur_trb == cur_td->last_trb)
+                       break;
+       }
+}
+
+static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
+               unsigned int ep_index, struct xhci_segment *deq_seg,
+               union xhci_trb *deq_ptr, u32 cycle_state);
+
+/*
+ * When we get a command completion for a Stop Endpoint Command, we need to
+ * unlink any cancelled TDs from the ring.  There are two ways to do that:
+ *
+ *  1. If the HW was in the middle of processing the TD that needs to be
+ *     cancelled, then we must move the ring's dequeue pointer past the last TRB
+ *     in the TD with a Set Dequeue Pointer Command.
+ *  2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain
+ *     bit cleared) so that the HW will skip over them.
+ */
+static void handle_stopped_endpoint(struct xhci_hcd *xhci,
+               union xhci_trb *trb)
+{
+       unsigned int slot_id;
+       unsigned int ep_index;
+       struct xhci_ring *ep_ring;
+       struct list_head *entry;
+       struct xhci_td *cur_td = 0;
+       struct xhci_td *last_unlinked_td;
+
+       struct dequeue_state deq_state;
+#ifdef CONFIG_USB_HCD_STAT
+       ktime_t stop_time = ktime_get();
+#endif
+
+       memset(&deq_state, 0, sizeof(deq_state));
+       slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
+       ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
+       ep_ring = xhci->devs[slot_id]->ep_rings[ep_index];
+
+       if (list_empty(&ep_ring->cancelled_td_list))
+               return;
+
+       /* Fix up the ep ring first, so HW stops executing cancelled TDs.
+        * We have the xHCI lock, so nothing can modify this list until we drop
+        * it.  We're also in the event handler, so we can't get re-interrupted
+        * if another Stop Endpoint command completes
+        */
+       list_for_each(entry, &ep_ring->cancelled_td_list) {
+               cur_td = list_entry(entry, struct xhci_td, cancelled_td_list);
+               xhci_dbg(xhci, "Cancelling TD starting at %p, 0x%llx (dma).\n",
+                               cur_td->first_trb,
+                               (unsigned long long)xhci_trb_virt_to_dma(cur_td->start_seg, cur_td->first_trb));
+               /*
+                * If we stopped on the TD we need to cancel, then we have to
+                * move the xHC endpoint ring dequeue pointer past this TD.
+                */
+               if (cur_td == ep_ring->stopped_td)
+                       find_new_dequeue_state(xhci, slot_id, ep_index, cur_td,
+                                       &deq_state);
+               else
+                       td_to_noop(xhci, ep_ring, cur_td);
+               /*
+                * The event handler won't see a completion for this TD anymore,
+                * so remove it from the endpoint ring's TD list.  Keep it in
+                * the cancelled TD list for URB completion later.
+                */
+               list_del(&cur_td->td_list);
+               ep_ring->cancels_pending--;
+       }
+       last_unlinked_td = cur_td;
+
+       /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */
+       if (deq_state.new_deq_ptr && deq_state.new_deq_seg) {
+               xhci_dbg(xhci, "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), "
+                               "new deq ptr = %p (0x%llx dma), new cycle = %u\n",
+                               deq_state.new_deq_seg,
+                               (unsigned long long)deq_state.new_deq_seg->dma,
+                               deq_state.new_deq_ptr,
+                               (unsigned long long)xhci_trb_virt_to_dma(deq_state.new_deq_seg, deq_state.new_deq_ptr),
+                               deq_state.new_cycle_state);
+               queue_set_tr_deq(xhci, slot_id, ep_index,
+                               deq_state.new_deq_seg,
+                               deq_state.new_deq_ptr,
+                               (u32) deq_state.new_cycle_state);
+               /* Stop the TD queueing code from ringing the doorbell until
+                * this command completes.  The HC won't set the dequeue pointer
+                * if the ring is running, and ringing the doorbell starts the
+                * ring running.
+                */
+               ep_ring->state |= SET_DEQ_PENDING;
+               xhci_ring_cmd_db(xhci);
+       } else {
+               /* Otherwise just ring the doorbell to restart the ring */
+               ring_ep_doorbell(xhci, slot_id, ep_index);
+       }
+
+       /*
+        * Drop the lock and complete the URBs in the cancelled TD list.
+        * New TDs to be cancelled might be added to the end of the list before
+        * we can complete all the URBs for the TDs we already unlinked.
+        * So stop when we've completed the URB for the last TD we unlinked.
+        */
+       do {
+               cur_td = list_entry(ep_ring->cancelled_td_list.next,
+                               struct xhci_td, cancelled_td_list);
+               list_del(&cur_td->cancelled_td_list);
+
+               /* Clean up the cancelled URB */
+#ifdef CONFIG_USB_HCD_STAT
+               hcd_stat_update(xhci->tp_stat, cur_td->urb->actual_length,
+                               ktime_sub(stop_time, cur_td->start_time));
+#endif
+               cur_td->urb->hcpriv = NULL;
+               usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), cur_td->urb);
+
+               xhci_dbg(xhci, "Giveback cancelled URB %p\n", cur_td->urb);
+               spin_unlock(&xhci->lock);
+               /* Doesn't matter what we pass for status, since the core will
+                * just overwrite it (because the URB has been unlinked).
+                */
+               usb_hcd_giveback_urb(xhci_to_hcd(xhci), cur_td->urb, 0);
+               kfree(cur_td);
+
+               spin_lock(&xhci->lock);
+       } while (cur_td != last_unlinked_td);
+
+       /* Return to the event handler with xhci->lock re-acquired */
+}
+
+/*
+ * When we get a completion for a Set Transfer Ring Dequeue Pointer command,
+ * we need to clear the set deq pending flag in the endpoint ring state, so that
+ * the TD queueing code can ring the doorbell again.  We also need to ring the
+ * endpoint doorbell to restart the ring, but only if there aren't more
+ * cancellations pending.
+ */
+static void handle_set_deq_completion(struct xhci_hcd *xhci,
+               struct xhci_event_cmd *event,
+               union xhci_trb *trb)
+{
+       unsigned int slot_id;
+       unsigned int ep_index;
+       struct xhci_ring *ep_ring;
+       struct xhci_virt_device *dev;
+
+       slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
+       ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
+       dev = xhci->devs[slot_id];
+       ep_ring = dev->ep_rings[ep_index];
+
+       if (GET_COMP_CODE(event->status) != COMP_SUCCESS) {
+               unsigned int ep_state;
+               unsigned int slot_state;
+
+               switch (GET_COMP_CODE(event->status)) {
+               case COMP_TRB_ERR:
+                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because "
+                                       "of stream ID configuration\n");
+                       break;
+               case COMP_CTX_STATE:
+                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due "
+                                       "to incorrect slot or ep state.\n");
+                       ep_state = dev->out_ctx->ep[ep_index].ep_info;
+                       ep_state &= EP_STATE_MASK;
+                       slot_state = dev->out_ctx->slot.dev_state;
+                       slot_state = GET_SLOT_STATE(slot_state);
+                       xhci_dbg(xhci, "Slot state = %u, EP state = %u\n",
+                                       slot_state, ep_state);
+                       break;
+               case COMP_EBADSLT:
+                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because "
+                                       "slot %u was not enabled.\n", slot_id);
+                       break;
+               default:
+                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown "
+                                       "completion code of %u.\n",
+                                       GET_COMP_CODE(event->status));
+                       break;
+               }
+               /* OK what do we do now?  The endpoint state is hosed, and we
+                * should never get to this point if the synchronization between
+                * queueing, and endpoint state are correct.  This might happen
+                * if the device gets disconnected after we've finished
+                * cancelling URBs, which might not be an error...
+                */
+       } else {
+               xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq[0] = 0x%x, "
+                               "deq[1] = 0x%x.\n",
+                               dev->out_ctx->ep[ep_index].deq[0],
+                               dev->out_ctx->ep[ep_index].deq[1]);
+       }
+
+       ep_ring->state &= ~SET_DEQ_PENDING;
+       ring_ep_doorbell(xhci, slot_id, ep_index);
+}
+
+
+static void handle_cmd_completion(struct xhci_hcd *xhci,
+               struct xhci_event_cmd *event)
+{
+       int slot_id = TRB_TO_SLOT_ID(event->flags);
+       u64 cmd_dma;
+       dma_addr_t cmd_dequeue_dma;
+
+       cmd_dma = (((u64) event->cmd_trb[1]) << 32) + event->cmd_trb[0];
+       cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
+                       xhci->cmd_ring->dequeue);
+       /* Is the command ring deq ptr out of sync with the deq seg ptr? */
+       if (cmd_dequeue_dma == 0) {
+               xhci->error_bitmask |= 1 << 4;
+               return;
+       }
+       /* Does the DMA address match our internal dequeue pointer address? */
+       if (cmd_dma != (u64) cmd_dequeue_dma) {
+               xhci->error_bitmask |= 1 << 5;
+               return;
+       }
+       switch (xhci->cmd_ring->dequeue->generic.field[3] & TRB_TYPE_BITMASK) {
+       case TRB_TYPE(TRB_ENABLE_SLOT):
+               if (GET_COMP_CODE(event->status) == COMP_SUCCESS)
+                       xhci->slot_id = slot_id;
+               else
+                       xhci->slot_id = 0;
+               complete(&xhci->addr_dev);
+               break;
+       case TRB_TYPE(TRB_DISABLE_SLOT):
+               if (xhci->devs[slot_id])
+                       xhci_free_virt_device(xhci, slot_id);
+               break;
+       case TRB_TYPE(TRB_CONFIG_EP):
+               xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status);
+               complete(&xhci->devs[slot_id]->cmd_completion);
+               break;
+       case TRB_TYPE(TRB_ADDR_DEV):
+               xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status);
+               complete(&xhci->addr_dev);
+               break;
+       case TRB_TYPE(TRB_STOP_RING):
+               handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue);
+               break;
+       case TRB_TYPE(TRB_SET_DEQ):
+               handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue);
+               break;
+       case TRB_TYPE(TRB_CMD_NOOP):
+               ++xhci->noops_handled;
+               break;
+       default:
+               /* Skip over unknown commands on the event ring */
+               xhci->error_bitmask |= 1 << 6;
+               break;
+       }
+       inc_deq(xhci, xhci->cmd_ring, false);
+}
+
+static void handle_port_status(struct xhci_hcd *xhci,
+               union xhci_trb *event)
+{
+       u32 port_id;
+
+       /* Port status change events always have a successful completion code */
+       if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
+               xhci_warn(xhci, "WARN: xHC returned failed port status event\n");
+               xhci->error_bitmask |= 1 << 8;
+       }
+       /* FIXME: core doesn't care about all port link state changes yet */
+       port_id = GET_PORT_ID(event->generic.field[0]);
+       xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id);
+
+       /* Update event ring dequeue pointer before dropping the lock */
+       inc_deq(xhci, xhci->event_ring, true);
+       xhci_set_hc_event_deq(xhci);
+
+       spin_unlock(&xhci->lock);
+       /* Pass this up to the core */
+       usb_hcd_poll_rh_status(xhci_to_hcd(xhci));
+       spin_lock(&xhci->lock);
+}
+
+/*
+ * This TD is defined by the TRBs starting at start_trb in start_seg and ending
+ * at end_trb, which may be in another segment.  If the suspect DMA address is a
+ * TRB in this TD, this function returns that TRB's segment.  Otherwise it
+ * returns 0.
+ */
+static struct xhci_segment *trb_in_td(
+               struct xhci_segment *start_seg,
+               union xhci_trb  *start_trb,
+               union xhci_trb  *end_trb,
+               dma_addr_t      suspect_dma)
+{
+       dma_addr_t start_dma;
+       dma_addr_t end_seg_dma;
+       dma_addr_t end_trb_dma;
+       struct xhci_segment *cur_seg;
+
+       start_dma = xhci_trb_virt_to_dma(start_seg, start_trb);
+       cur_seg = start_seg;
+
+       do {
+               /* We may get an event for a Link TRB in the middle of a TD */
+               end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
+                               &start_seg->trbs[TRBS_PER_SEGMENT - 1]);
+               /* If the end TRB isn't in this segment, this is set to 0 */
+               end_trb_dma = xhci_trb_virt_to_dma(cur_seg, end_trb);
+
+               if (end_trb_dma > 0) {
+                       /* The end TRB is in this segment, so suspect should be here */
+                       if (start_dma <= end_trb_dma) {
+                               if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma)
+                                       return cur_seg;
+                       } else {
+                               /* Case for one segment with
+                                * a TD wrapped around to the top
+                                */
+                               if ((suspect_dma >= start_dma &&
+                                                       suspect_dma <= end_seg_dma) ||
+                                               (suspect_dma >= cur_seg->dma &&
+                                                suspect_dma <= end_trb_dma))
+                                       return cur_seg;
+                       }
+                       return 0;
+               } else {
+                       /* Might still be somewhere in this segment */
+                       if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
+                               return cur_seg;
+               }
+               cur_seg = cur_seg->next;
+               start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
+       } while (1);
+
+}
+
+/*
+ * If this function returns an error condition, it means it got a Transfer
+ * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
+ * At this point, the host controller is probably hosed and should be reset.
+ */
+static int handle_tx_event(struct xhci_hcd *xhci,
+               struct xhci_transfer_event *event)
+{
+       struct xhci_virt_device *xdev;
+       struct xhci_ring *ep_ring;
+       int ep_index;
+       struct xhci_td *td = 0;
+       dma_addr_t event_dma;
+       struct xhci_segment *event_seg;
+       union xhci_trb *event_trb;
+       struct urb *urb = 0;
+       int status = -EINPROGRESS;
+
+       xdev = xhci->devs[TRB_TO_SLOT_ID(event->flags)];
+       if (!xdev) {
+               xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n");
+               return -ENODEV;
+       }
+
+       /* Endpoint ID is 1 based, our index is zero based */
+       ep_index = TRB_TO_EP_ID(event->flags) - 1;
+       ep_ring = xdev->ep_rings[ep_index];
+       if (!ep_ring || (xdev->out_ctx->ep[ep_index].ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) {
+               xhci_err(xhci, "ERROR Transfer event pointed to disabled endpoint\n");
+               return -ENODEV;
+       }
+
+       event_dma = event->buffer[0];
+       if (event->buffer[1] != 0)
+               xhci_warn(xhci, "WARN ignoring upper 32-bits of 64-bit TRB dma address\n");
+
+       /* This TRB should be in the TD at the head of this ring's TD list */
+       if (list_empty(&ep_ring->td_list)) {
+               xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
+                               TRB_TO_SLOT_ID(event->flags), ep_index);
+               xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
+                               (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
+               xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
+               urb = NULL;
+               goto cleanup;
+       }
+       td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+
+       /* Is this a TRB in the currently executing TD? */
+       event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
+                       td->last_trb, event_dma);
+       if (!event_seg) {
+               /* HC is busted, give up! */
+               xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n");
+               return -ESHUTDOWN;
+       }
+       event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)];
+       xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
+                       (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
+       xhci_dbg(xhci, "Offset 0x00 (buffer[0]) = 0x%x\n",
+                       (unsigned int) event->buffer[0]);
+       xhci_dbg(xhci, "Offset 0x04 (buffer[0]) = 0x%x\n",
+                       (unsigned int) event->buffer[1]);
+       xhci_dbg(xhci, "Offset 0x08 (transfer length) = 0x%x\n",
+                       (unsigned int) event->transfer_len);
+       xhci_dbg(xhci, "Offset 0x0C (flags) = 0x%x\n",
+                       (unsigned int) event->flags);
+
+       /* Look for common error cases */
+       switch (GET_COMP_CODE(event->transfer_len)) {
+       /* Skip codes that require special handling depending on
+        * transfer type
+        */
+       case COMP_SUCCESS:
+       case COMP_SHORT_TX:
+               break;
+       case COMP_STOP:
+               xhci_dbg(xhci, "Stopped on Transfer TRB\n");
+               break;
+       case COMP_STOP_INVAL:
+               xhci_dbg(xhci, "Stopped on No-op or Link TRB\n");
+               break;
+       case COMP_STALL:
+               xhci_warn(xhci, "WARN: Stalled endpoint\n");
+               status = -EPIPE;
+               break;
+       case COMP_TRB_ERR:
+               xhci_warn(xhci, "WARN: TRB error on endpoint\n");
+               status = -EILSEQ;
+               break;
+       case COMP_TX_ERR:
+               xhci_warn(xhci, "WARN: transfer error on endpoint\n");
+               status = -EPROTO;
+               break;
+       case COMP_DB_ERR:
+               xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n");
+               status = -ENOSR;
+               break;
+       default:
+               xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n");
+               urb = NULL;
+               goto cleanup;
+       }
+       /* Now update the urb's actual_length and give back to the core */
+       /* Was this a control transfer? */
+       if (usb_endpoint_xfer_control(&td->urb->ep->desc)) {
+               xhci_debug_trb(xhci, xhci->event_ring->dequeue);
+               switch (GET_COMP_CODE(event->transfer_len)) {
+               case COMP_SUCCESS:
+                       if (event_trb == ep_ring->dequeue) {
+                               xhci_warn(xhci, "WARN: Success on ctrl setup TRB without IOC set??\n");
+                               status = -ESHUTDOWN;
+                       } else if (event_trb != td->last_trb) {
+                               xhci_warn(xhci, "WARN: Success on ctrl data TRB without IOC set??\n");
+                               status = -ESHUTDOWN;
+                       } else {
+                               xhci_dbg(xhci, "Successful control transfer!\n");
+                               status = 0;
+                       }
+                       break;
+               case COMP_SHORT_TX:
+                       xhci_warn(xhci, "WARN: short transfer on control ep\n");
+                       status = -EREMOTEIO;
+                       break;
+               default:
+                       /* Others already handled above */
+                       break;
+               }
+               /*
+                * Did we transfer any data, despite the errors that might have
+                * happened?  I.e. did we get past the setup stage?
+                */
+               if (event_trb != ep_ring->dequeue) {
+                       /* The event was for the status stage */
+                       if (event_trb == td->last_trb) {
+                               td->urb->actual_length =
+                                       td->urb->transfer_buffer_length;
+                       } else {
+                       /* Maybe the event was for the data stage? */
+                               if (GET_COMP_CODE(event->transfer_len) != COMP_STOP_INVAL)
+                                       /* We didn't stop on a link TRB in the middle */
+                                       td->urb->actual_length =
+                                               td->urb->transfer_buffer_length -
+                                               TRB_LEN(event->transfer_len);
+                       }
+               }
+       } else {
+               switch (GET_COMP_CODE(event->transfer_len)) {
+               case COMP_SUCCESS:
+                       /* Double check that the HW transferred everything. */
+                       if (event_trb != td->last_trb) {
+                               xhci_warn(xhci, "WARN Successful completion "
+                                               "on short TX\n");
+                               if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+                                       status = -EREMOTEIO;
+                               else
+                                       status = 0;
+                       } else {
+                               xhci_dbg(xhci, "Successful bulk transfer!\n");
+                               status = 0;
+                       }
+                       break;
+               case COMP_SHORT_TX:
+                       if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+                               status = -EREMOTEIO;
+                       else
+                               status = 0;
+                       break;
+               default:
+                       /* Others already handled above */
+                       break;
+               }
+               dev_dbg(&td->urb->dev->dev,
+                               "ep %#x - asked for %d bytes, "
+                               "%d bytes untransferred\n",
+                               td->urb->ep->desc.bEndpointAddress,
+                               td->urb->transfer_buffer_length,
+                               TRB_LEN(event->transfer_len));
+               /* Fast path - was this the last TRB in the TD for this URB? */
+               if (event_trb == td->last_trb) {
+                       if (TRB_LEN(event->transfer_len) != 0) {
+                               td->urb->actual_length =
+                                       td->urb->transfer_buffer_length -
+                                       TRB_LEN(event->transfer_len);
+                               if (td->urb->actual_length < 0) {
+                                       xhci_warn(xhci, "HC gave bad length "
+                                                       "of %d bytes left\n",
+                                                       TRB_LEN(event->transfer_len));
+                                       td->urb->actual_length = 0;
+                               }
+                               if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+                                       status = -EREMOTEIO;
+                               else
+                                       status = 0;
+                       } else {
+                               td->urb->actual_length = td->urb->transfer_buffer_length;
+                               /* Ignore a short packet completion if the
+                                * untransferred length was zero.
+                                */
+                               status = 0;
+                       }
+               } else {
+                       /* Slow path - walk the list, starting from the dequeue
+                        * pointer, to get the actual length transferred.
+                        */
+                       union xhci_trb *cur_trb;
+                       struct xhci_segment *cur_seg;
+
+                       td->urb->actual_length = 0;
+                       for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
+                                       cur_trb != event_trb;
+                                       next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
+                               if (TRB_TYPE(cur_trb->generic.field[3]) != TRB_TR_NOOP &&
+                                               TRB_TYPE(cur_trb->generic.field[3]) != TRB_LINK)
+                                       td->urb->actual_length +=
+                                               TRB_LEN(cur_trb->generic.field[2]);
+                       }
+                       /* If the ring didn't stop on a Link or No-op TRB, add
+                        * in the actual bytes transferred from the Normal TRB
+                        */
+                       if (GET_COMP_CODE(event->transfer_len) != COMP_STOP_INVAL)
+                               td->urb->actual_length +=
+                                       TRB_LEN(cur_trb->generic.field[2]) -
+                                       TRB_LEN(event->transfer_len);
+               }
+       }
+       /* The Endpoint Stop Command completion will take care of
+        * any stopped TDs.  A stopped TD may be restarted, so don't update the
+        * ring dequeue pointer or take this TD off any lists yet.
+        */
+       if (GET_COMP_CODE(event->transfer_len) == COMP_STOP_INVAL ||
+                       GET_COMP_CODE(event->transfer_len) == COMP_STOP) {
+               ep_ring->stopped_td = td;
+               ep_ring->stopped_trb = event_trb;
+       } else {
+               /* Update ring dequeue pointer */
+               while (ep_ring->dequeue != td->last_trb)
+                       inc_deq(xhci, ep_ring, false);
+               inc_deq(xhci, ep_ring, false);
+
+               /* Clean up the endpoint's TD list */
+               urb = td->urb;
+               list_del(&td->td_list);
+               /* Was this TD slated to be cancelled but completed anyway? */
+               if (!list_empty(&td->cancelled_td_list)) {
+                       list_del(&td->cancelled_td_list);
+                       ep_ring->cancels_pending--;
+               }
+               kfree(td);
+               urb->hcpriv = NULL;
+       }
+cleanup:
+       inc_deq(xhci, xhci->event_ring, true);
+       xhci_set_hc_event_deq(xhci);
+
+       /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */
+       if (urb) {
+               usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
+               spin_unlock(&xhci->lock);
+               usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
+               spin_lock(&xhci->lock);
+       }
+       return 0;
+}
+
+/*
+ * This function handles all OS-owned events on the event ring.  It may drop
+ * xhci->lock between event processing (e.g. to pass up port status changes).
+ */
+void xhci_handle_event(struct xhci_hcd *xhci)
+{
+       union xhci_trb *event;
+       int update_ptrs = 1;
+       int ret;
+
+       if (!xhci->event_ring || !xhci->event_ring->dequeue) {
+               xhci->error_bitmask |= 1 << 1;
+               return;
+       }
+
+       event = xhci->event_ring->dequeue;
+       /* Does the HC or OS own the TRB? */
+       if ((event->event_cmd.flags & TRB_CYCLE) !=
+                       xhci->event_ring->cycle_state) {
+               xhci->error_bitmask |= 1 << 2;
+               return;
+       }
+
+       /* FIXME: Handle more event types. */
+       switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) {
+       case TRB_TYPE(TRB_COMPLETION):
+               handle_cmd_completion(xhci, &event->event_cmd);
+               break;
+       case TRB_TYPE(TRB_PORT_STATUS):
+               handle_port_status(xhci, event);
+               update_ptrs = 0;
+               break;
+       case TRB_TYPE(TRB_TRANSFER):
+               ret = handle_tx_event(xhci, &event->trans_event);
+               if (ret < 0)
+                       xhci->error_bitmask |= 1 << 9;
+               else
+                       update_ptrs = 0;
+               break;
+       default:
+               xhci->error_bitmask |= 1 << 3;
+       }
+
+       if (update_ptrs) {
+               /* Update SW and HC event ring dequeue pointer */
+               inc_deq(xhci, xhci->event_ring, true);
+               xhci_set_hc_event_deq(xhci);
+       }
+       /* Are there more items on the event ring? */
+       xhci_handle_event(xhci);
+}
+
+/****          Endpoint Ring Operations        ****/
+
+/*
+ * Generic function for queueing a TRB on a ring.
+ * The caller must have checked to make sure there's room on the ring.
+ */
+static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
+               bool consumer,
+               u32 field1, u32 field2, u32 field3, u32 field4)
+{
+       struct xhci_generic_trb *trb;
+
+       trb = &ring->enqueue->generic;
+       trb->field[0] = field1;
+       trb->field[1] = field2;
+       trb->field[2] = field3;
+       trb->field[3] = field4;
+       inc_enq(xhci, ring, consumer);
+}
+
+/*
+ * Does various checks on the endpoint ring, and makes it ready to queue num_trbs.
+ * FIXME allocate segments if the ring is full.
+ */
+static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
+               u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
+{
+       /* Make sure the endpoint has been added to xHC schedule */
+       xhci_dbg(xhci, "Endpoint state = 0x%x\n", ep_state);
+       switch (ep_state) {
+       case EP_STATE_DISABLED:
+               /*
+                * USB core changed config/interfaces without notifying us,
+                * or hardware is reporting the wrong state.
+                */
+               xhci_warn(xhci, "WARN urb submitted to disabled ep\n");
+               return -ENOENT;
+       case EP_STATE_HALTED:
+       case EP_STATE_ERROR:
+               xhci_warn(xhci, "WARN waiting for halt or error on ep "
+                               "to be cleared\n");
+               /* FIXME event handling code for error needs to clear it */
+               /* XXX not sure if this should be -ENOENT or not */
+               return -EINVAL;
+       case EP_STATE_STOPPED:
+       case EP_STATE_RUNNING:
+               break;
+       default:
+               xhci_err(xhci, "ERROR unknown endpoint state for ep\n");
+               /*
+                * FIXME issue Configure Endpoint command to try to get the HC
+                * back into a known state.
+                */
+               return -EINVAL;
+       }
+       if (!room_on_ring(xhci, ep_ring, num_trbs)) {
+               /* FIXME allocate more room */
+               xhci_err(xhci, "ERROR no room on ep ring\n");
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static int prepare_transfer(struct xhci_hcd *xhci,
+               struct xhci_virt_device *xdev,
+               unsigned int ep_index,
+               unsigned int num_trbs,
+               struct urb *urb,
+               struct xhci_td **td,
+               gfp_t mem_flags)
+{
+       int ret;
+
+       ret = prepare_ring(xhci, xdev->ep_rings[ep_index],
+                       xdev->out_ctx->ep[ep_index].ep_info & EP_STATE_MASK,
+                       num_trbs, mem_flags);
+       if (ret)
+               return ret;
+       *td = kzalloc(sizeof(struct xhci_td), mem_flags);
+       if (!*td)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&(*td)->td_list);
+       INIT_LIST_HEAD(&(*td)->cancelled_td_list);
+
+       ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb);
+       if (unlikely(ret)) {
+               kfree(*td);
+               return ret;
+       }
+
+       (*td)->urb = urb;
+       urb->hcpriv = (void *) (*td);
+       /* Add this TD to the tail of the endpoint ring's TD list */
+       list_add_tail(&(*td)->td_list, &xdev->ep_rings[ep_index]->td_list);
+       (*td)->start_seg = xdev->ep_rings[ep_index]->enq_seg;
+       (*td)->first_trb = xdev->ep_rings[ep_index]->enqueue;
+
+       return 0;
+}
+
+static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
+{
+       int num_sgs, num_trbs, running_total, temp, i;
+       struct scatterlist *sg;
+
+       sg = NULL;
+       num_sgs = urb->num_sgs;
+       temp = urb->transfer_buffer_length;
+
+       xhci_dbg(xhci, "count sg list trbs: \n");
+       num_trbs = 0;
+       for_each_sg(urb->sg->sg, sg, num_sgs, i) {
+               unsigned int previous_total_trbs = num_trbs;
+               unsigned int len = sg_dma_len(sg);
+
+               /* Scatter gather list entries may cross 64KB boundaries */
+               running_total = TRB_MAX_BUFF_SIZE -
+                       (sg_dma_address(sg) & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+               if (running_total != 0)
+                       num_trbs++;
+
+               /* How many more 64KB chunks to transfer, how many more TRBs? */
+               while (running_total < sg_dma_len(sg)) {
+                       num_trbs++;
+                       running_total += TRB_MAX_BUFF_SIZE;
+               }
+               xhci_dbg(xhci, " sg #%d: dma = %#llx, len = %#x (%d), num_trbs = %d\n",
+                               i, (unsigned long long)sg_dma_address(sg),
+                               len, len, num_trbs - previous_total_trbs);
+
+               len = min_t(int, len, temp);
+               temp -= len;
+               if (temp == 0)
+                       break;
+       }
+       xhci_dbg(xhci, "\n");
+       if (!in_interrupt())
+               dev_dbg(&urb->dev->dev, "ep %#x - urb len = %d, sglist used, num_trbs = %d\n",
+                               urb->ep->desc.bEndpointAddress,
+                               urb->transfer_buffer_length,
+                               num_trbs);
+       return num_trbs;
+}
+
+static void check_trb_math(struct urb *urb, int num_trbs, int running_total)
+{
+       if (num_trbs != 0)
+               dev_dbg(&urb->dev->dev, "%s - ep %#x - Miscalculated number of "
+                               "TRBs, %d left\n", __func__,
+                               urb->ep->desc.bEndpointAddress, num_trbs);
+       if (running_total != urb->transfer_buffer_length)
+               dev_dbg(&urb->dev->dev, "%s - ep %#x - Miscalculated tx length, "
+                               "queued %#x (%d), asked for %#x (%d)\n",
+                               __func__,
+                               urb->ep->desc.bEndpointAddress,
+                               running_total, running_total,
+                               urb->transfer_buffer_length,
+                               urb->transfer_buffer_length);
+}
+
+static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
+               unsigned int ep_index, int start_cycle,
+               struct xhci_generic_trb *start_trb, struct xhci_td *td)
+{
+       /*
+        * Pass all the TRBs to the hardware at once and make sure this write
+        * isn't reordered.
+        */
+       wmb();
+       start_trb->field[3] |= start_cycle;
+       ring_ep_doorbell(xhci, slot_id, ep_index);
+}
+
+static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
+               struct urb *urb, int slot_id, unsigned int ep_index)
+{
+       struct xhci_ring *ep_ring;
+       unsigned int num_trbs;
+       struct xhci_td *td;
+       struct scatterlist *sg;
+       int num_sgs;
+       int trb_buff_len, this_sg_len, running_total;
+       bool first_trb;
+       u64 addr;
+
+       struct xhci_generic_trb *start_trb;
+       int start_cycle;
+
+       ep_ring = xhci->devs[slot_id]->ep_rings[ep_index];
+       num_trbs = count_sg_trbs_needed(xhci, urb);
+       num_sgs = urb->num_sgs;
+
+       trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
+                       ep_index, num_trbs, urb, &td, mem_flags);
+       if (trb_buff_len < 0)
+               return trb_buff_len;
+       /*
+        * Don't give the first TRB to the hardware (by toggling the cycle bit)
+        * until we've finished creating all the other TRBs.  The ring's cycle
+        * state may change as we enqueue the other TRBs, so save it too.
+        */
+       start_trb = &ep_ring->enqueue->generic;
+       start_cycle = ep_ring->cycle_state;
+
+       running_total = 0;
+       /*
+        * How much data is in the first TRB?
+        *
+        * There are three forces at work for TRB buffer pointers and lengths:
+        * 1. We don't want to walk off the end of this sg-list entry buffer.
+        * 2. The transfer length that the driver requested may be smaller than
+        *    the amount of memory allocated for this scatter-gather list.
+        * 3. TRBs buffers can't cross 64KB boundaries.
+        */
+       sg = urb->sg->sg;
+       addr = (u64) sg_dma_address(sg);
+       this_sg_len = sg_dma_len(sg);
+       trb_buff_len = TRB_MAX_BUFF_SIZE -
+               (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+       trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
+       if (trb_buff_len > urb->transfer_buffer_length)
+               trb_buff_len = urb->transfer_buffer_length;
+       xhci_dbg(xhci, "First length to xfer from 1st sglist entry = %u\n",
+                       trb_buff_len);
+
+       first_trb = true;
+       /* Queue the first TRB, even if it's zero-length */
+       do {
+               u32 field = 0;
+
+               /* Don't change the cycle bit of the first TRB until later */
+               if (first_trb)
+                       first_trb = false;
+               else
+                       field |= ep_ring->cycle_state;
+
+               /* Chain all the TRBs together; clear the chain bit in the last
+                * TRB to indicate it's the last TRB in the chain.
+                */
+               if (num_trbs > 1) {
+                       field |= TRB_CHAIN;
+               } else {
+                       /* FIXME - add check for ZERO_PACKET flag before this */
+                       td->last_trb = ep_ring->enqueue;
+                       field |= TRB_IOC;
+               }
+               xhci_dbg(xhci, " sg entry: dma = %#x, len = %#x (%d), "
+                               "64KB boundary at %#x, end dma = %#x\n",
+                               (unsigned int) addr, trb_buff_len, trb_buff_len,
+                               (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
+                               (unsigned int) addr + trb_buff_len);
+               if (TRB_MAX_BUFF_SIZE -
+                               (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)) < trb_buff_len) {
+                       xhci_warn(xhci, "WARN: sg dma xfer crosses 64KB boundaries!\n");
+                       xhci_dbg(xhci, "Next boundary at %#x, end dma = %#x\n",
+                                       (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
+                                       (unsigned int) addr + trb_buff_len);
+               }
+               queue_trb(xhci, ep_ring, false,
+                               (u32) addr,
+                               (u32) ((u64) addr >> 32),
+                               TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0),
+                               /* We always want to know if the TRB was short,
+                                * or we won't get an event when it completes.
+                                * (Unless we use event data TRBs, which are a
+                                * waste of space and HC resources.)
+                                */
+                               field | TRB_ISP | TRB_TYPE(TRB_NORMAL));
+               --num_trbs;
+               running_total += trb_buff_len;
+
+               /* Calculate length for next transfer --
+                * Are we done queueing all the TRBs for this sg entry?
+                */
+               this_sg_len -= trb_buff_len;
+               if (this_sg_len == 0) {
+                       --num_sgs;
+                       if (num_sgs == 0)
+                               break;
+                       sg = sg_next(sg);
+                       addr = (u64) sg_dma_address(sg);
+                       this_sg_len = sg_dma_len(sg);
+               } else {
+                       addr += trb_buff_len;
+               }
+
+               trb_buff_len = TRB_MAX_BUFF_SIZE -
+                       (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+               trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
+               if (running_total + trb_buff_len > urb->transfer_buffer_length)
+                       trb_buff_len =
+                               urb->transfer_buffer_length - running_total;
+       } while (running_total < urb->transfer_buffer_length);
+
+       check_trb_math(urb, num_trbs, running_total);
+       giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td);
+       return 0;
+}
+
+/* This is very similar to what ehci-q.c qtd_fill() does */
+int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
+               struct urb *urb, int slot_id, unsigned int ep_index)
+{
+       struct xhci_ring *ep_ring;
+       struct xhci_td *td;
+       int num_trbs;
+       struct xhci_generic_trb *start_trb;
+       bool first_trb;
+       int start_cycle;
+       u32 field;
+
+       int running_total, trb_buff_len, ret;
+       u64 addr;
+
+       if (urb->sg)
+               return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index);
+
+       ep_ring = xhci->devs[slot_id]->ep_rings[ep_index];
+
+       num_trbs = 0;
+       /* How much data is (potentially) left before the 64KB boundary? */
+       running_total = TRB_MAX_BUFF_SIZE -
+               (urb->transfer_dma & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+
+       /* If there's some data on this 64KB chunk, or we have to send a
+        * zero-length transfer, we need at least one TRB
+        */
+       if (running_total != 0 || urb->transfer_buffer_length == 0)
+               num_trbs++;
+       /* How many more 64KB chunks to transfer, how many more TRBs? */
+       while (running_total < urb->transfer_buffer_length) {
+               num_trbs++;
+               running_total += TRB_MAX_BUFF_SIZE;
+       }
+       /* FIXME: this doesn't deal with URB_ZERO_PACKET - need one more */
+
+       if (!in_interrupt())
+               dev_dbg(&urb->dev->dev, "ep %#x - urb len = %#x (%d), addr = %#llx, num_trbs = %d\n",
+                               urb->ep->desc.bEndpointAddress,
+                               urb->transfer_buffer_length,
+                               urb->transfer_buffer_length,
+                               (unsigned long long)urb->transfer_dma,
+                               num_trbs);
+
+       ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
+                       num_trbs, urb, &td, mem_flags);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Don't give the first TRB to the hardware (by toggling the cycle bit)
+        * until we've finished creating all the other TRBs.  The ring's cycle
+        * state may change as we enqueue the other TRBs, so save it too.
+        */
+       start_trb = &ep_ring->enqueue->generic;
+       start_cycle = ep_ring->cycle_state;
+
+       running_total = 0;
+       /* How much data is in the first TRB? */
+       addr = (u64) urb->transfer_dma;
+       trb_buff_len = TRB_MAX_BUFF_SIZE -
+               (urb->transfer_dma & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+       if (urb->transfer_buffer_length < trb_buff_len)
+               trb_buff_len = urb->transfer_buffer_length;
+
+       first_trb = true;
+
+       /* Queue the first TRB, even if it's zero-length */
+       do {
+               field = 0;
+
+               /* Don't change the cycle bit of the first TRB until later */
+               if (first_trb)
+                       first_trb = false;
+               else
+                       field |= ep_ring->cycle_state;
+
+               /* Chain all the TRBs together; clear the chain bit in the last
+                * TRB to indicate it's the last TRB in the chain.
+                */
+               if (num_trbs > 1) {
+                       field |= TRB_CHAIN;
+               } else {
+                       /* FIXME - add check for ZERO_PACKET flag before this */
+                       td->last_trb = ep_ring->enqueue;
+                       field |= TRB_IOC;
+               }
+               queue_trb(xhci, ep_ring, false,
+                               (u32) addr,
+                               (u32) ((u64) addr >> 32),
+                               TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0),
+                               /* We always want to know if the TRB was short,
+                                * or we won't get an event when it completes.
+                                * (Unless we use event data TRBs, which are a
+                                * waste of space and HC resources.)
+                                */
+                               field | TRB_ISP | TRB_TYPE(TRB_NORMAL));
+               --num_trbs;
+               running_total += trb_buff_len;
+
+               /* Calculate length for next transfer */
+               addr += trb_buff_len;
+               trb_buff_len = urb->transfer_buffer_length - running_total;
+               if (trb_buff_len > TRB_MAX_BUFF_SIZE)
+                       trb_buff_len = TRB_MAX_BUFF_SIZE;
+       } while (running_total < urb->transfer_buffer_length);
+
+       check_trb_math(urb, num_trbs, running_total);
+       giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td);
+       return 0;
+}
+
+/* Caller must have locked xhci->lock */
+int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
+               struct urb *urb, int slot_id, unsigned int ep_index)
+{
+       struct xhci_ring *ep_ring;
+       int num_trbs;
+       int ret;
+       struct usb_ctrlrequest *setup;
+       struct xhci_generic_trb *start_trb;
+       int start_cycle;
+       u32 field;
+       struct xhci_td *td;
+
+       ep_ring = xhci->devs[slot_id]->ep_rings[ep_index];
+
+       /*
+        * Need to copy setup packet into setup TRB, so we can't use the setup
+        * DMA address.
+        */
+       if (!urb->setup_packet)
+               return -EINVAL;
+
+       if (!in_interrupt())
+               xhci_dbg(xhci, "Queueing ctrl tx for slot id %d, ep %d\n",
+                               slot_id, ep_index);
+       /* 1 TRB for setup, 1 for status */
+       num_trbs = 2;
+       /*
+        * Don't need to check if we need additional event data and normal TRBs,
+        * since data in control transfers will never get bigger than 16MB
+        * XXX: can we get a buffer that crosses 64KB boundaries?
+        */
+       if (urb->transfer_buffer_length > 0)
+               num_trbs++;
+       ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, num_trbs,
+                       urb, &td, mem_flags);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Don't give the first TRB to the hardware (by toggling the cycle bit)
+        * until we've finished creating all the other TRBs.  The ring's cycle
+        * state may change as we enqueue the other TRBs, so save it too.
+        */
+       start_trb = &ep_ring->enqueue->generic;
+       start_cycle = ep_ring->cycle_state;
+
+       /* Queue setup TRB - see section 6.4.1.2.1 */
+       /* FIXME better way to translate setup_packet into two u32 fields? */
+       setup = (struct usb_ctrlrequest *) urb->setup_packet;
+       queue_trb(xhci, ep_ring, false,
+                       /* FIXME endianness is probably going to bite my ass here. */
+                       setup->bRequestType | setup->bRequest << 8 | setup->wValue << 16,
+                       setup->wIndex | setup->wLength << 16,
+                       TRB_LEN(8) | TRB_INTR_TARGET(0),
+                       /* Immediate data in pointer */
+                       TRB_IDT | TRB_TYPE(TRB_SETUP));
+
+       /* If there's data, queue data TRBs */
+       field = 0;
+       if (urb->transfer_buffer_length > 0) {
+               if (setup->bRequestType & USB_DIR_IN)
+                       field |= TRB_DIR_IN;
+               queue_trb(xhci, ep_ring, false,
+                               lower_32_bits(urb->transfer_dma),
+                               upper_32_bits(urb->transfer_dma),
+                               TRB_LEN(urb->transfer_buffer_length) | TRB_INTR_TARGET(0),
+                               /* Event on short tx */
+                               field | TRB_ISP | TRB_TYPE(TRB_DATA) | ep_ring->cycle_state);
+       }
+
+       /* Save the DMA address of the last TRB in the TD */
+       td->last_trb = ep_ring->enqueue;
+
+       /* Queue status TRB - see Table 7 and sections 4.11.2.2 and 6.4.1.2.3 */
+       /* If the device sent data, the status stage is an OUT transfer */
+       if (urb->transfer_buffer_length > 0 && setup->bRequestType & USB_DIR_IN)
+               field = 0;
+       else
+               field = TRB_DIR_IN;
+       queue_trb(xhci, ep_ring, false,
+                       0,
+                       0,
+                       TRB_INTR_TARGET(0),
+                       /* Event on completion */
+                       field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state);
+
+       giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td);
+       return 0;
+}
+
+/****          Command Ring Operations         ****/
+
+/* Generic function for queueing a command TRB on the command ring */
+static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, u32 field3, u32 field4)
+{
+       if (!room_on_ring(xhci, xhci->cmd_ring, 1)) {
+               if (!in_interrupt())
+                       xhci_err(xhci, "ERR: No room for command on command ring\n");
+               return -ENOMEM;
+       }
+       queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
+                       field4 | xhci->cmd_ring->cycle_state);
+       return 0;
+}
+
+/* Queue a no-op command on the command ring */
+static int queue_cmd_noop(struct xhci_hcd *xhci)
+{
+       return queue_command(xhci, 0, 0, 0, TRB_TYPE(TRB_CMD_NOOP));
+}
+
+/*
+ * Place a no-op command on the command ring to test the command and
+ * event ring.
+ */
+void *xhci_setup_one_noop(struct xhci_hcd *xhci)
+{
+       if (queue_cmd_noop(xhci) < 0)
+               return NULL;
+       xhci->noops_submitted++;
+       return xhci_ring_cmd_db;
+}
+
+/* Queue a slot enable or disable request on the command ring */
+int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id)
+{
+       return queue_command(xhci, 0, 0, 0,
+                       TRB_TYPE(trb_type) | SLOT_ID_FOR_TRB(slot_id));
+}
+
+/* Queue an address device command TRB */
+int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
+               u32 slot_id)
+{
+       return queue_command(xhci, in_ctx_ptr, 0, 0,
+                       TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(slot_id));
+}
+
+/* Queue a configure endpoint command TRB */
+int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
+               u32 slot_id)
+{
+       return queue_command(xhci, in_ctx_ptr, 0, 0,
+                       TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(slot_id));
+}
+
+int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
+               unsigned int ep_index)
+{
+       u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
+       u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+       u32 type = TRB_TYPE(TRB_STOP_RING);
+
+       return queue_command(xhci, 0, 0, 0,
+                       trb_slot_id | trb_ep_index | type);
+}
+
+/* Set Transfer Ring Dequeue Pointer command.
+ * This should not be used for endpoints that have streams enabled.
+ */
+static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
+               unsigned int ep_index, struct xhci_segment *deq_seg,
+               union xhci_trb *deq_ptr, u32 cycle_state)
+{
+       dma_addr_t addr;
+       u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
+       u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+       u32 type = TRB_TYPE(TRB_SET_DEQ);
+
+       addr = xhci_trb_virt_to_dma(deq_seg, deq_ptr);
+       if (addr == 0)
+               xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n");
+               xhci_warn(xhci, "WARN deq seg = %p, deq pt = %p\n",
+                               deq_seg, deq_ptr);
+       return queue_command(xhci, (u32) addr | cycle_state, 0, 0,
+                       trb_slot_id | trb_ep_index | type);
+}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
new file mode 100644 (file)
index 0000000..8936eeb
--- /dev/null
@@ -0,0 +1,1157 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_XHCI_HCD_H
+#define __LINUX_XHCI_HCD_H
+
+#include <linux/usb.h>
+#include <linux/timer.h>
+
+#include "../core/hcd.h"
+/* Code sharing between pci-quirks and xhci hcd */
+#include       "xhci-ext-caps.h"
+
+/* xHCI PCI Configuration Registers */
+#define XHCI_SBRN_OFFSET       (0x60)
+
+/* Max number of USB devices for any host controller - limit in section 6.1 */
+#define MAX_HC_SLOTS           256
+/* Section 5.3.3 - MaxPorts */
+#define MAX_HC_PORTS           127
+
+/*
+ * xHCI register interface.
+ * This corresponds to the eXtensible Host Controller Interface (xHCI)
+ * Revision 0.95 specification
+ *
+ * Registers should always be accessed with double word or quad word accesses.
+ *
+ * Some xHCI implementations may support 64-bit address pointers.  Registers
+ * with 64-bit address pointers should be written to with dword accesses by
+ * writing the low dword first (ptr[0]), then the high dword (ptr[1]) second.
+ * xHCI implementations that do not support 64-bit address pointers will ignore
+ * the high dword, and write order is irrelevant.
+ */
+
+/**
+ * struct xhci_cap_regs - xHCI Host Controller Capability Registers.
+ * @hc_capbase:                length of the capabilities register and HC version number
+ * @hcs_params1:       HCSPARAMS1 - Structural Parameters 1
+ * @hcs_params2:       HCSPARAMS2 - Structural Parameters 2
+ * @hcs_params3:       HCSPARAMS3 - Structural Parameters 3
+ * @hcc_params:                HCCPARAMS - Capability Parameters
+ * @db_off:            DBOFF - Doorbell array offset
+ * @run_regs_off:      RTSOFF - Runtime register space offset
+ */
+struct xhci_cap_regs {
+       u32     hc_capbase;
+       u32     hcs_params1;
+       u32     hcs_params2;
+       u32     hcs_params3;
+       u32     hcc_params;
+       u32     db_off;
+       u32     run_regs_off;
+       /* Reserved up to (CAPLENGTH - 0x1C) */
+};
+
+/* hc_capbase bitmasks */
+/* bits 7:0 - how long is the Capabilities register */
+#define HC_LENGTH(p)           XHCI_HC_LENGTH(p)
+/* bits 31:16  */
+#define HC_VERSION(p)          (((p) >> 16) & 0xffff)
+
+/* HCSPARAMS1 - hcs_params1 - bitmasks */
+/* bits 0:7, Max Device Slots */
+#define HCS_MAX_SLOTS(p)       (((p) >> 0) & 0xff)
+#define HCS_SLOTS_MASK         0xff
+/* bits 8:18, Max Interrupters */
+#define HCS_MAX_INTRS(p)       (((p) >> 8) & 0x7ff)
+/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */
+#define HCS_MAX_PORTS(p)       (((p) >> 24) & 0x7f)
+
+/* HCSPARAMS2 - hcs_params2 - bitmasks */
+/* bits 0:3, frames or uframes that SW needs to queue transactions
+ * ahead of the HW to meet periodic deadlines */
+#define HCS_IST(p)             (((p) >> 0) & 0xf)
+/* bits 4:7, max number of Event Ring segments */
+#define HCS_ERST_MAX(p)                (((p) >> 4) & 0xf)
+/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */
+/* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */
+
+/* HCSPARAMS3 - hcs_params3 - bitmasks */
+/* bits 0:7, Max U1 to U0 latency for the roothub ports */
+#define HCS_U1_LATENCY(p)      (((p) >> 0) & 0xff)
+/* bits 16:31, Max U2 to U0 latency for the roothub ports */
+#define HCS_U2_LATENCY(p)      (((p) >> 16) & 0xffff)
+
+/* HCCPARAMS - hcc_params - bitmasks */
+/* true: HC can use 64-bit address pointers */
+#define HCC_64BIT_ADDR(p)      ((p) & (1 << 0))
+/* true: HC can do bandwidth negotiation */
+#define HCC_BANDWIDTH_NEG(p)   ((p) & (1 << 1))
+/* true: HC uses 64-byte Device Context structures
+ * FIXME 64-byte context structures aren't supported yet.
+ */
+#define HCC_64BYTE_CONTEXT(p)  ((p) & (1 << 2))
+/* true: HC has port power switches */
+#define HCC_PPC(p)             ((p) & (1 << 3))
+/* true: HC has port indicators */
+#define HCS_INDICATOR(p)       ((p) & (1 << 4))
+/* true: HC has Light HC Reset Capability */
+#define HCC_LIGHT_RESET(p)     ((p) & (1 << 5))
+/* true: HC supports latency tolerance messaging */
+#define HCC_LTC(p)             ((p) & (1 << 6))
+/* true: no secondary Stream ID Support */
+#define HCC_NSS(p)             ((p) & (1 << 7))
+/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */
+#define HCC_MAX_PSA            (1 << ((((p) >> 12) & 0xf) + 1))
+/* Extended Capabilities pointer from PCI base - section 5.3.6 */
+#define HCC_EXT_CAPS(p)                XHCI_HCC_EXT_CAPS(p)
+
+/* db_off bitmask - bits 0:1 reserved */
+#define        DBOFF_MASK      (~0x3)
+
+/* run_regs_off bitmask - bits 0:4 reserved */
+#define        RTSOFF_MASK     (~0x1f)
+
+
+/* Number of registers per port */
+#define        NUM_PORT_REGS   4
+
+/**
+ * struct xhci_op_regs - xHCI Host Controller Operational Registers.
+ * @command:           USBCMD - xHC command register
+ * @status:            USBSTS - xHC status register
+ * @page_size:         This indicates the page size that the host controller
+ *                     supports.  If bit n is set, the HC supports a page size
+ *                     of 2^(n+12), up to a 128MB page size.
+ *                     4K is the minimum page size.
+ * @cmd_ring:          CRP - 64-bit Command Ring Pointer
+ * @dcbaa_ptr:         DCBAAP - 64-bit Device Context Base Address Array Pointer
+ * @config_reg:                CONFIG - Configure Register
+ * @port_status_base:  PORTSCn - base address for Port Status and Control
+ *                     Each port has a Port Status and Control register,
+ *                     followed by a Port Power Management Status and Control
+ *                     register, a Port Link Info register, and a reserved
+ *                     register.
+ * @port_power_base:   PORTPMSCn - base address for
+ *                     Port Power Management Status and Control
+ * @port_link_base:    PORTLIn - base address for Port Link Info (current
+ *                     Link PM state and control) for USB 2.1 and USB 3.0
+ *                     devices.
+ */
+struct xhci_op_regs {
+       u32     command;
+       u32     status;
+       u32     page_size;
+       u32     reserved1;
+       u32     reserved2;
+       u32     dev_notification;
+       u32     cmd_ring[2];
+       /* rsvd: offset 0x20-2F */
+       u32     reserved3[4];
+       u32     dcbaa_ptr[2];
+       u32     config_reg;
+       /* rsvd: offset 0x3C-3FF */
+       u32     reserved4[241];
+       /* port 1 registers, which serve as a base address for other ports */
+       u32     port_status_base;
+       u32     port_power_base;
+       u32     port_link_base;
+       u32     reserved5;
+       /* registers for ports 2-255 */
+       u32     reserved6[NUM_PORT_REGS*254];
+};
+
+/* USBCMD - USB command - command bitmasks */
+/* start/stop HC execution - do not write unless HC is halted*/
+#define CMD_RUN                XHCI_CMD_RUN
+/* Reset HC - resets internal HC state machine and all registers (except
+ * PCI config regs).  HC does NOT drive a USB reset on the downstream ports.
+ * The xHCI driver must reinitialize the xHC after setting this bit.
+ */
+#define CMD_RESET      (1 << 1)
+/* Event Interrupt Enable - a '1' allows interrupts from the host controller */
+#define CMD_EIE                XHCI_CMD_EIE
+/* Host System Error Interrupt Enable - get out-of-band signal for HC errors */
+#define CMD_HSEIE      XHCI_CMD_HSEIE
+/* bits 4:6 are reserved (and should be preserved on writes). */
+/* light reset (port status stays unchanged) - reset completed when this is 0 */
+#define CMD_LRESET     (1 << 7)
+/* FIXME: ignoring host controller save/restore state for now. */
+#define CMD_CSS                (1 << 8)
+#define CMD_CRS                (1 << 9)
+/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */
+#define CMD_EWE                XHCI_CMD_EWE
+/* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root
+ * hubs are in U3 (selective suspend), disconnect, disabled, or powered-off.
+ * '0' means the xHC can power it off if all ports are in the disconnect,
+ * disabled, or powered-off state.
+ */
+#define CMD_PM_INDEX   (1 << 11)
+/* bits 12:31 are reserved (and should be preserved on writes). */
+
+/* USBSTS - USB status - status bitmasks */
+/* HC not running - set to 1 when run/stop bit is cleared. */
+#define STS_HALT       XHCI_STS_HALT
+/* serious error, e.g. PCI parity error.  The HC will clear the run/stop bit. */
+#define STS_FATAL      (1 << 2)
+/* event interrupt - clear this prior to clearing any IP flags in IR set*/
+#define STS_EINT       (1 << 3)
+/* port change detect */
+#define STS_PORT       (1 << 4)
+/* bits 5:7 reserved and zeroed */
+/* save state status - '1' means xHC is saving state */
+#define STS_SAVE       (1 << 8)
+/* restore state status - '1' means xHC is restoring state */
+#define STS_RESTORE    (1 << 9)
+/* true: save or restore error */
+#define STS_SRE                (1 << 10)
+/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
+#define STS_CNR                XHCI_STS_CNR
+/* true: internal Host Controller Error - SW needs to reset and reinitialize */
+#define STS_HCE                (1 << 12)
+/* bits 13:31 reserved and should be preserved */
+
+/*
+ * DNCTRL - Device Notification Control Register - dev_notification bitmasks
+ * Generate a device notification event when the HC sees a transaction with a
+ * notification type that matches a bit set in this bit field.
+ */
+#define        DEV_NOTE_MASK           (0xffff)
+#define ENABLE_DEV_NOTE(x)     (1 << x)
+/* Most of the device notification types should only be used for debug.
+ * SW does need to pay attention to function wake notifications.
+ */
+#define        DEV_NOTE_FWAKE          ENABLE_DEV_NOTE(1)
+
+/* CRCR - Command Ring Control Register - cmd_ring bitmasks */
+/* bit 0 is the command ring cycle state */
+/* stop ring operation after completion of the currently executing command */
+#define CMD_RING_PAUSE         (1 << 1)
+/* stop ring immediately - abort the currently executing command */
+#define CMD_RING_ABORT         (1 << 2)
+/* true: command ring is running */
+#define CMD_RING_RUNNING       (1 << 3)
+/* bits 4:5 reserved and should be preserved */
+/* Command Ring pointer - bit mask for the lower 32 bits. */
+#define CMD_RING_ADDR_MASK     (0xffffffc0)
+
+/* CONFIG - Configure Register - config_reg bitmasks */
+/* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */
+#define MAX_DEVS(p)    ((p) & 0xff)
+/* bits 8:31 - reserved and should be preserved */
+
+/* PORTSC - Port Status and Control Register - port_status_base bitmasks */
+/* true: device connected */
+#define PORT_CONNECT   (1 << 0)
+/* true: port enabled */
+#define PORT_PE                (1 << 1)
+/* bit 2 reserved and zeroed */
+/* true: port has an over-current condition */
+#define PORT_OC                (1 << 3)
+/* true: port reset signaling asserted */
+#define PORT_RESET     (1 << 4)
+/* Port Link State - bits 5:8
+ * A read gives the current link PM state of the port,
+ * a write with Link State Write Strobe set sets the link state.
+ */
+/* true: port has power (see HCC_PPC) */
+#define PORT_POWER     (1 << 9)
+/* bits 10:13 indicate device speed:
+ * 0 - undefined speed - port hasn't be initialized by a reset yet
+ * 1 - full speed
+ * 2 - low speed
+ * 3 - high speed
+ * 4 - super speed
+ * 5-15 reserved
+ */
+#define DEV_SPEED_MASK         (0xf << 10)
+#define        XDEV_FS                 (0x1 << 10)
+#define        XDEV_LS                 (0x2 << 10)
+#define        XDEV_HS                 (0x3 << 10)
+#define        XDEV_SS                 (0x4 << 10)
+#define DEV_UNDEFSPEED(p)      (((p) & DEV_SPEED_MASK) == (0x0<<10))
+#define DEV_FULLSPEED(p)       (((p) & DEV_SPEED_MASK) == XDEV_FS)
+#define DEV_LOWSPEED(p)                (((p) & DEV_SPEED_MASK) == XDEV_LS)
+#define DEV_HIGHSPEED(p)       (((p) & DEV_SPEED_MASK) == XDEV_HS)
+#define DEV_SUPERSPEED(p)      (((p) & DEV_SPEED_MASK) == XDEV_SS)
+/* Bits 20:23 in the Slot Context are the speed for the device */
+#define        SLOT_SPEED_FS           (XDEV_FS << 10)
+#define        SLOT_SPEED_LS           (XDEV_LS << 10)
+#define        SLOT_SPEED_HS           (XDEV_HS << 10)
+#define        SLOT_SPEED_SS           (XDEV_SS << 10)
+/* Port Indicator Control */
+#define PORT_LED_OFF   (0 << 14)
+#define PORT_LED_AMBER (1 << 14)
+#define PORT_LED_GREEN (2 << 14)
+#define PORT_LED_MASK  (3 << 14)
+/* Port Link State Write Strobe - set this when changing link state */
+#define PORT_LINK_STROBE       (1 << 16)
+/* true: connect status change */
+#define PORT_CSC       (1 << 17)
+/* true: port enable change */
+#define PORT_PEC       (1 << 18)
+/* true: warm reset for a USB 3.0 device is done.  A "hot" reset puts the port
+ * into an enabled state, and the device into the default state.  A "warm" reset
+ * also resets the link, forcing the device through the link training sequence.
+ * SW can also look at the Port Reset register to see when warm reset is done.
+ */
+#define PORT_WRC       (1 << 19)
+/* true: over-current change */
+#define PORT_OCC       (1 << 20)
+/* true: reset change - 1 to 0 transition of PORT_RESET */
+#define PORT_RC                (1 << 21)
+/* port link status change - set on some port link state transitions:
+ *  Transition                         Reason
+ *  ------------------------------------------------------------------------------
+ *  - U3 to Resume                     Wakeup signaling from a device
+ *  - Resume to Recovery to U0         USB 3.0 device resume
+ *  - Resume to U0                     USB 2.0 device resume
+ *  - U3 to Recovery to U0             Software resume of USB 3.0 device complete
+ *  - U3 to U0                         Software resume of USB 2.0 device complete
+ *  - U2 to U0                         L1 resume of USB 2.1 device complete
+ *  - U0 to U0 (???)                   L1 entry rejection by USB 2.1 device
+ *  - U0 to disabled                   L1 entry error with USB 2.1 device
+ *  - Any state to inactive            Error on USB 3.0 port
+ */
+#define PORT_PLC       (1 << 22)
+/* port configure error change - port failed to configure its link partner */
+#define PORT_CEC       (1 << 23)
+/* bit 24 reserved */
+/* wake on connect (enable) */
+#define PORT_WKCONN_E  (1 << 25)
+/* wake on disconnect (enable) */
+#define PORT_WKDISC_E  (1 << 26)
+/* wake on over-current (enable) */
+#define PORT_WKOC_E    (1 << 27)
+/* bits 28:29 reserved */
+/* true: device is removable - for USB 3.0 roothub emulation */
+#define PORT_DEV_REMOVE        (1 << 30)
+/* Initiate a warm port reset - complete when PORT_WRC is '1' */
+#define PORT_WR                (1 << 31)
+
+/* Port Power Management Status and Control - port_power_base bitmasks */
+/* Inactivity timer value for transitions into U1, in microseconds.
+ * Timeout can be up to 127us.  0xFF means an infinite timeout.
+ */
+#define PORT_U1_TIMEOUT(p)     ((p) & 0xff)
+/* Inactivity timer value for transitions into U2 */
+#define PORT_U2_TIMEOUT(p)     (((p) & 0xff) << 8)
+/* Bits 24:31 for port testing */
+
+
+/**
+ * struct xhci_intr_reg - Interrupt Register Set
+ * @irq_pending:       IMAN - Interrupt Management Register.  Used to enable
+ *                     interrupts and check for pending interrupts.
+ * @irq_control:       IMOD - Interrupt Moderation Register.
+ *                     Used to throttle interrupts.
+ * @erst_size:         Number of segments in the Event Ring Segment Table (ERST).
+ * @erst_base:         ERST base address.
+ * @erst_dequeue:      Event ring dequeue pointer.
+ *
+ * Each interrupter (defined by a MSI-X vector) has an event ring and an Event
+ * Ring Segment Table (ERST) associated with it.  The event ring is comprised of
+ * multiple segments of the same size.  The HC places events on the ring and
+ * "updates the Cycle bit in the TRBs to indicate to software the current
+ * position of the Enqueue Pointer." The HCD (Linux) processes those events and
+ * updates the dequeue pointer.
+ */
+struct xhci_intr_reg {
+       u32     irq_pending;
+       u32     irq_control;
+       u32     erst_size;
+       u32     rsvd;
+       u32     erst_base[2];
+       u32     erst_dequeue[2];
+};
+
+/* irq_pending bitmasks */
+#define        ER_IRQ_PENDING(p)       ((p) & 0x1)
+/* bits 2:31 need to be preserved */
+/* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */
+#define        ER_IRQ_CLEAR(p)         ((p) & 0xfffffffe)
+#define        ER_IRQ_ENABLE(p)        ((ER_IRQ_CLEAR(p)) | 0x2)
+#define        ER_IRQ_DISABLE(p)       ((ER_IRQ_CLEAR(p)) & ~(0x2))
+
+/* irq_control bitmasks */
+/* Minimum interval between interrupts (in 250ns intervals).  The interval
+ * between interrupts will be longer if there are no events on the event ring.
+ * Default is 4000 (1 ms).
+ */
+#define ER_IRQ_INTERVAL_MASK   (0xffff)
+/* Counter used to count down the time to the next interrupt - HW use only */
+#define ER_IRQ_COUNTER_MASK    (0xffff << 16)
+
+/* erst_size bitmasks */
+/* Preserve bits 16:31 of erst_size */
+#define        ERST_SIZE_MASK          (0xffff << 16)
+
+/* erst_dequeue bitmasks */
+/* Dequeue ERST Segment Index (DESI) - Segment number (or alias)
+ * where the current dequeue pointer lies.  This is an optional HW hint.
+ */
+#define ERST_DESI_MASK         (0x7)
+/* Event Handler Busy (EHB) - is the event ring scheduled to be serviced by
+ * a work queue (or delayed service routine)?
+ */
+#define ERST_EHB               (1 << 3)
+#define ERST_PTR_MASK          (0xf)
+
+/**
+ * struct xhci_run_regs
+ * @microframe_index:
+ *             MFINDEX - current microframe number
+ *
+ * Section 5.5 Host Controller Runtime Registers:
+ * "Software should read and write these registers using only Dword (32 bit)
+ * or larger accesses"
+ */
+struct xhci_run_regs {
+       u32                     microframe_index;
+       u32                     rsvd[7];
+       struct xhci_intr_reg    ir_set[128];
+};
+
+/**
+ * struct doorbell_array
+ *
+ * Section 5.6
+ */
+struct xhci_doorbell_array {
+       u32     doorbell[256];
+};
+
+#define        DB_TARGET_MASK          0xFFFFFF00
+#define        DB_STREAM_ID_MASK       0x0000FFFF
+#define        DB_TARGET_HOST          0x0
+#define        DB_STREAM_ID_HOST       0x0
+#define        DB_MASK                 (0xff << 8)
+
+/* Endpoint Target - bits 0:7 */
+#define EPI_TO_DB(p)           (((p) + 1) & 0xff)
+
+
+/**
+ * struct xhci_slot_ctx
+ * @dev_info:  Route string, device speed, hub info, and last valid endpoint
+ * @dev_info2: Max exit latency for device number, root hub port number
+ * @tt_info:   tt_info is used to construct split transaction tokens
+ * @dev_state: slot state and device address
+ *
+ * Slot Context - section 6.2.1.1.  This assumes the HC uses 32-byte context
+ * structures.  If the HC uses 64-byte contexts, there is an additional 32 bytes
+ * reserved at the end of the slot context for HC internal use.
+ */
+struct xhci_slot_ctx {
+       u32     dev_info;
+       u32     dev_info2;
+       u32     tt_info;
+       u32     dev_state;
+       /* offset 0x10 to 0x1f reserved for HC internal use */
+       u32     reserved[4];
+};
+
+/* dev_info bitmasks */
+/* Route String - 0:19 */
+#define ROUTE_STRING_MASK      (0xfffff)
+/* Device speed - values defined by PORTSC Device Speed field - 20:23 */
+#define DEV_SPEED      (0xf << 20)
+/* bit 24 reserved */
+/* Is this LS/FS device connected through a HS hub? - bit 25 */
+#define DEV_MTT                (0x1 << 25)
+/* Set if the device is a hub - bit 26 */
+#define DEV_HUB                (0x1 << 26)
+/* Index of the last valid endpoint context in this device context - 27:31 */
+#define LAST_CTX_MASK  (0x1f << 27)
+#define LAST_CTX(p)    ((p) << 27)
+#define LAST_CTX_TO_EP_NUM(p)  (((p) >> 27) - 1)
+#define SLOT_FLAG      (1 << 0)
+#define EP0_FLAG       (1 << 1)
+
+/* dev_info2 bitmasks */
+/* Max Exit Latency (ms) - worst case time to wake up all links in dev path */
+#define MAX_EXIT       (0xffff)
+/* Root hub port number that is needed to access the USB device */
+#define ROOT_HUB_PORT(p)       (((p) & 0xff) << 16)
+
+/* tt_info bitmasks */
+/*
+ * TT Hub Slot ID - for low or full speed devices attached to a high-speed hub
+ * The Slot ID of the hub that isolates the high speed signaling from
+ * this low or full-speed device.  '0' if attached to root hub port.
+ */
+#define TT_SLOT                (0xff)
+/*
+ * The number of the downstream facing port of the high-speed hub
+ * '0' if the device is not low or full speed.
+ */
+#define TT_PORT                (0xff << 8)
+
+/* dev_state bitmasks */
+/* USB device address - assigned by the HC */
+#define DEV_ADDR_MASK  (0xff)
+/* bits 8:26 reserved */
+/* Slot state */
+#define SLOT_STATE     (0x1f << 27)
+#define GET_SLOT_STATE(p)      (((p) & (0x1f << 27)) >> 27)
+
+
+/**
+ * struct xhci_ep_ctx
+ * @ep_info:   endpoint state, streams, mult, and interval information.
+ * @ep_info2:  information on endpoint type, max packet size, max burst size,
+ *             error count, and whether the HC will force an event for all
+ *             transactions.
+ * @deq:       64-bit ring dequeue pointer address.  If the endpoint only
+ *             defines one stream, this points to the endpoint transfer ring.
+ *             Otherwise, it points to a stream context array, which has a
+ *             ring pointer for each flow.
+ * @tx_info:
+ *             Average TRB lengths for the endpoint ring and
+ *             max payload within an Endpoint Service Interval Time (ESIT).
+ *
+ * Endpoint Context - section 6.2.1.2.  This assumes the HC uses 32-byte context
+ * structures.  If the HC uses 64-byte contexts, there is an additional 32 bytes
+ * reserved at the end of the endpoint context for HC internal use.
+ */
+struct xhci_ep_ctx {
+       u32     ep_info;
+       u32     ep_info2;
+       u32     deq[2];
+       u32     tx_info;
+       /* offset 0x14 - 0x1f reserved for HC internal use */
+       u32     reserved[3];
+};
+
+/* ep_info bitmasks */
+/*
+ * Endpoint State - bits 0:2
+ * 0 - disabled
+ * 1 - running
+ * 2 - halted due to halt condition - ok to manipulate endpoint ring
+ * 3 - stopped
+ * 4 - TRB error
+ * 5-7 - reserved
+ */
+#define EP_STATE_MASK          (0xf)
+#define EP_STATE_DISABLED      0
+#define EP_STATE_RUNNING       1
+#define EP_STATE_HALTED                2
+#define EP_STATE_STOPPED       3
+#define EP_STATE_ERROR         4
+/* Mult - Max number of burtst within an interval, in EP companion desc. */
+#define EP_MULT(p)             ((p & 0x3) << 8)
+/* bits 10:14 are Max Primary Streams */
+/* bit 15 is Linear Stream Array */
+/* Interval - period between requests to an endpoint - 125u increments. */
+#define EP_INTERVAL(p)         ((p & 0xff) << 16)
+
+/* ep_info2 bitmasks */
+/*
+ * Force Event - generate transfer events for all TRBs for this endpoint
+ * This will tell the HC to ignore the IOC and ISP flags (for debugging only).
+ */
+#define        FORCE_EVENT     (0x1)
+#define ERROR_COUNT(p) (((p) & 0x3) << 1)
+#define EP_TYPE(p)     ((p) << 3)
+#define ISOC_OUT_EP    1
+#define BULK_OUT_EP    2
+#define INT_OUT_EP     3
+#define CTRL_EP                4
+#define ISOC_IN_EP     5
+#define BULK_IN_EP     6
+#define INT_IN_EP      7
+/* bit 6 reserved */
+/* bit 7 is Host Initiate Disable - for disabling stream selection */
+#define MAX_BURST(p)   (((p)&0xff) << 8)
+#define MAX_PACKET(p)  (((p)&0xffff) << 16)
+
+
+/**
+ * struct xhci_device_control
+ * Input/Output context; see section 6.2.5.
+ *
+ * @drop_context:      set the bit of the endpoint context you want to disable
+ * @add_context:       set the bit of the endpoint context you want to enable
+ */
+struct xhci_device_control {
+       u32     drop_flags;
+       u32     add_flags;
+       u32     rsvd[6];
+       struct xhci_slot_ctx    slot;
+       struct xhci_ep_ctx      ep[31];
+};
+
+/* drop context bitmasks */
+#define        DROP_EP(x)      (0x1 << x)
+/* add context bitmasks */
+#define        ADD_EP(x)       (0x1 << x)
+
+
+struct xhci_virt_device {
+       /*
+        * Commands to the hardware are passed an "input context" that
+        * tells the hardware what to change in its data structures.
+        * The hardware will return changes in an "output context" that
+        * software must allocate for the hardware.  We need to keep
+        * track of input and output contexts separately because
+        * these commands might fail and we don't trust the hardware.
+        */
+       struct xhci_device_control      *out_ctx;
+       dma_addr_t                      out_ctx_dma;
+       /* Used for addressing devices and configuration changes */
+       struct xhci_device_control      *in_ctx;
+       dma_addr_t                      in_ctx_dma;
+       /* FIXME when stream support is added */
+       struct xhci_ring                *ep_rings[31];
+       /* Temporary storage in case the configure endpoint command fails and we
+        * have to restore the device state to the previous state
+        */
+       struct xhci_ring                *new_ep_rings[31];
+       struct completion               cmd_completion;
+       /* Status of the last command issued for this device */
+       u32                             cmd_status;
+};
+
+
+/**
+ * struct xhci_device_context_array
+ * @dev_context_ptr    array of 64-bit DMA addresses for device contexts
+ */
+struct xhci_device_context_array {
+       /* 64-bit device addresses; we only write 32-bit addresses */
+       u32                     dev_context_ptrs[2*MAX_HC_SLOTS];
+       /* private xHCD pointers */
+       dma_addr_t      dma;
+};
+/* TODO: write function to set the 64-bit device DMA address */
+/*
+ * TODO: change this to be dynamically sized at HC mem init time since the HC
+ * might not be able to handle the maximum number of devices possible.
+ */
+
+
+struct xhci_stream_ctx {
+       /* 64-bit stream ring address, cycle state, and stream type */
+       u32     stream_ring[2];
+       /* offset 0x14 - 0x1f reserved for HC internal use */
+       u32     reserved[2];
+};
+
+
+struct xhci_transfer_event {
+       /* 64-bit buffer address, or immediate data */
+       u32     buffer[2];
+       u32     transfer_len;
+       /* This field is interpreted differently based on the type of TRB */
+       u32     flags;
+};
+
+/** Transfer Event bit fields **/
+#define        TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f)
+
+/* Completion Code - only applicable for some types of TRBs */
+#define        COMP_CODE_MASK          (0xff << 24)
+#define GET_COMP_CODE(p)       (((p) & COMP_CODE_MASK) >> 24)
+#define COMP_SUCCESS   1
+/* Data Buffer Error */
+#define COMP_DB_ERR    2
+/* Babble Detected Error */
+#define COMP_BABBLE    3
+/* USB Transaction Error */
+#define COMP_TX_ERR    4
+/* TRB Error - some TRB field is invalid */
+#define COMP_TRB_ERR   5
+/* Stall Error - USB device is stalled */
+#define COMP_STALL     6
+/* Resource Error - HC doesn't have memory for that device configuration */
+#define COMP_ENOMEM    7
+/* Bandwidth Error - not enough room in schedule for this dev config */
+#define COMP_BW_ERR    8
+/* No Slots Available Error - HC ran out of device slots */
+#define COMP_ENOSLOTS  9
+/* Invalid Stream Type Error */
+#define COMP_STREAM_ERR        10
+/* Slot Not Enabled Error - doorbell rung for disabled device slot */
+#define COMP_EBADSLT   11
+/* Endpoint Not Enabled Error */
+#define COMP_EBADEP    12
+/* Short Packet */
+#define COMP_SHORT_TX  13
+/* Ring Underrun - doorbell rung for an empty isoc OUT ep ring */
+#define COMP_UNDERRUN  14
+/* Ring Overrun - isoc IN ep ring is empty when ep is scheduled to RX */
+#define COMP_OVERRUN   15
+/* Virtual Function Event Ring Full Error */
+#define COMP_VF_FULL   16
+/* Parameter Error - Context parameter is invalid */
+#define COMP_EINVAL    17
+/* Bandwidth Overrun Error - isoc ep exceeded its allocated bandwidth */
+#define COMP_BW_OVER   18
+/* Context State Error - illegal context state transition requested */
+#define COMP_CTX_STATE 19
+/* No Ping Response Error - HC didn't get PING_RESPONSE in time to TX */
+#define COMP_PING_ERR  20
+/* Event Ring is full */
+#define COMP_ER_FULL   21
+/* Missed Service Error - HC couldn't service an isoc ep within interval */
+#define COMP_MISSED_INT        23
+/* Successfully stopped command ring */
+#define COMP_CMD_STOP  24
+/* Successfully aborted current command and stopped command ring */
+#define COMP_CMD_ABORT 25
+/* Stopped - transfer was terminated by a stop endpoint command */
+#define COMP_STOP      26
+/* Same as COMP_EP_STOPPED, but the transfered length in the event is invalid */
+#define COMP_STOP_INVAL        27
+/* Control Abort Error - Debug Capability - control pipe aborted */
+#define COMP_DBG_ABORT 28
+/* TRB type 29 and 30 reserved */
+/* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */
+#define COMP_BUFF_OVER 31
+/* Event Lost Error - xHC has an "internal event overrun condition" */
+#define COMP_ISSUES    32
+/* Undefined Error - reported when other error codes don't apply */
+#define COMP_UNKNOWN   33
+/* Invalid Stream ID Error */
+#define COMP_STRID_ERR 34
+/* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */
+/* FIXME - check for this */
+#define COMP_2ND_BW_ERR        35
+/* Split Transaction Error */
+#define        COMP_SPLIT_ERR  36
+
+struct xhci_link_trb {
+       /* 64-bit segment pointer*/
+       u32 segment_ptr[2];
+       u32 intr_target;
+       u32 control;
+};
+
+/* control bitfields */
+#define LINK_TOGGLE    (0x1<<1)
+
+/* Command completion event TRB */
+struct xhci_event_cmd {
+       /* Pointer to command TRB, or the value passed by the event data trb */
+       u32 cmd_trb[2];
+       u32 status;
+       u32 flags;
+};
+
+/* flags bitmasks */
+/* bits 16:23 are the virtual function ID */
+/* bits 24:31 are the slot ID */
+#define TRB_TO_SLOT_ID(p)      (((p) & (0xff<<24)) >> 24)
+#define SLOT_ID_FOR_TRB(p)     (((p) & 0xff) << 24)
+
+/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
+#define TRB_TO_EP_INDEX(p)             ((((p) & (0x1f << 16)) >> 16) - 1)
+#define        EP_ID_FOR_TRB(p)                ((((p) + 1) & 0x1f) << 16)
+
+
+/* Port Status Change Event TRB fields */
+/* Port ID - bits 31:24 */
+#define GET_PORT_ID(p)         (((p) & (0xff << 24)) >> 24)
+
+/* Normal TRB fields */
+/* transfer_len bitmasks - bits 0:16 */
+#define        TRB_LEN(p)              ((p) & 0x1ffff)
+/* TD size - number of bytes remaining in the TD (including this TRB):
+ * bits 17 - 21.  Shift the number of bytes by 10. */
+#define TD_REMAINDER(p)                ((((p) >> 10) & 0x1f) << 17)
+/* Interrupter Target - which MSI-X vector to target the completion event at */
+#define TRB_INTR_TARGET(p)     (((p) & 0x3ff) << 22)
+#define GET_INTR_TARGET(p)     (((p) >> 22) & 0x3ff)
+
+/* Cycle bit - indicates TRB ownership by HC or HCD */
+#define TRB_CYCLE              (1<<0)
+/*
+ * Force next event data TRB to be evaluated before task switch.
+ * Used to pass OS data back after a TD completes.
+ */
+#define TRB_ENT                        (1<<1)
+/* Interrupt on short packet */
+#define TRB_ISP                        (1<<2)
+/* Set PCIe no snoop attribute */
+#define TRB_NO_SNOOP           (1<<3)
+/* Chain multiple TRBs into a TD */
+#define TRB_CHAIN              (1<<4)
+/* Interrupt on completion */
+#define TRB_IOC                        (1<<5)
+/* The buffer pointer contains immediate data */
+#define TRB_IDT                        (1<<6)
+
+
+/* Control transfer TRB specific fields */
+#define TRB_DIR_IN             (1<<16)
+
+struct xhci_generic_trb {
+       u32 field[4];
+};
+
+union xhci_trb {
+       struct xhci_link_trb            link;
+       struct xhci_transfer_event      trans_event;
+       struct xhci_event_cmd           event_cmd;
+       struct xhci_generic_trb         generic;
+};
+
+/* TRB bit mask */
+#define        TRB_TYPE_BITMASK        (0xfc00)
+#define TRB_TYPE(p)            ((p) << 10)
+/* TRB type IDs */
+/* bulk, interrupt, isoc scatter/gather, and control data stage */
+#define TRB_NORMAL             1
+/* setup stage for control transfers */
+#define TRB_SETUP              2
+/* data stage for control transfers */
+#define TRB_DATA               3
+/* status stage for control transfers */
+#define TRB_STATUS             4
+/* isoc transfers */
+#define TRB_ISOC               5
+/* TRB for linking ring segments */
+#define TRB_LINK               6
+#define TRB_EVENT_DATA         7
+/* Transfer Ring No-op (not for the command ring) */
+#define TRB_TR_NOOP            8
+/* Command TRBs */
+/* Enable Slot Command */
+#define TRB_ENABLE_SLOT                9
+/* Disable Slot Command */
+#define TRB_DISABLE_SLOT       10
+/* Address Device Command */
+#define TRB_ADDR_DEV           11
+/* Configure Endpoint Command */
+#define TRB_CONFIG_EP          12
+/* Evaluate Context Command */
+#define TRB_EVAL_CONTEXT       13
+/* Reset Transfer Ring Command */
+#define TRB_RESET_RING         14
+/* Stop Transfer Ring Command */
+#define TRB_STOP_RING          15
+/* Set Transfer Ring Dequeue Pointer Command */
+#define TRB_SET_DEQ            16
+/* Reset Device Command */
+#define TRB_RESET_DEV          17
+/* Force Event Command (opt) */
+#define TRB_FORCE_EVENT                18
+/* Negotiate Bandwidth Command (opt) */
+#define TRB_NEG_BANDWIDTH      19
+/* Set Latency Tolerance Value Command (opt) */
+#define TRB_SET_LT             20
+/* Get port bandwidth Command */
+#define TRB_GET_BW             21
+/* Force Header Command - generate a transaction or link management packet */
+#define TRB_FORCE_HEADER       22
+/* No-op Command - not for transfer rings */
+#define TRB_CMD_NOOP           23
+/* TRB IDs 24-31 reserved */
+/* Event TRBS */
+/* Transfer Event */
+#define TRB_TRANSFER           32
+/* Command Completion Event */
+#define TRB_COMPLETION         33
+/* Port Status Change Event */
+#define TRB_PORT_STATUS                34
+/* Bandwidth Request Event (opt) */
+#define TRB_BANDWIDTH_EVENT    35
+/* Doorbell Event (opt) */
+#define TRB_DOORBELL           36
+/* Host Controller Event */
+#define TRB_HC_EVENT           37
+/* Device Notification Event - device sent function wake notification */
+#define TRB_DEV_NOTE           38
+/* MFINDEX Wrap Event - microframe counter wrapped */
+#define TRB_MFINDEX_WRAP       39
+/* TRB IDs 40-47 reserved, 48-63 is vendor-defined */
+
+/*
+ * TRBS_PER_SEGMENT must be a multiple of 4,
+ * since the command ring is 64-byte aligned.
+ * It must also be greater than 16.
+ */
+#define TRBS_PER_SEGMENT       64
+#define SEGMENT_SIZE           (TRBS_PER_SEGMENT*16)
+/* TRB buffer pointers can't cross 64KB boundaries */
+#define TRB_MAX_BUFF_SHIFT             16
+#define TRB_MAX_BUFF_SIZE      (1 << TRB_MAX_BUFF_SHIFT)
+
+struct xhci_segment {
+       union xhci_trb          *trbs;
+       /* private to HCD */
+       struct xhci_segment     *next;
+       dma_addr_t              dma;
+};
+
+struct xhci_td {
+       struct list_head        td_list;
+       struct list_head        cancelled_td_list;
+       struct urb              *urb;
+       struct xhci_segment     *start_seg;
+       union xhci_trb          *first_trb;
+       union xhci_trb          *last_trb;
+};
+
+struct xhci_ring {
+       struct xhci_segment     *first_seg;
+       union  xhci_trb         *enqueue;
+       struct xhci_segment     *enq_seg;
+       unsigned int            enq_updates;
+       union  xhci_trb         *dequeue;
+       struct xhci_segment     *deq_seg;
+       unsigned int            deq_updates;
+       struct list_head        td_list;
+       /* ----  Related to URB cancellation ---- */
+       struct list_head        cancelled_td_list;
+       unsigned int            cancels_pending;
+       unsigned int            state;
+#define SET_DEQ_PENDING                (1 << 0)
+       /* The TRB that was last reported in a stopped endpoint ring */
+       union xhci_trb          *stopped_trb;
+       struct xhci_td          *stopped_td;
+       /*
+        * Write the cycle state into the TRB cycle field to give ownership of
+        * the TRB to the host controller (if we are the producer), or to check
+        * if we own the TRB (if we are the consumer).  See section 4.9.1.
+        */
+       u32                     cycle_state;
+};
+
+struct xhci_erst_entry {
+       /* 64-bit event ring segment address */
+       u32     seg_addr[2];
+       u32     seg_size;
+       /* Set to zero */
+       u32     rsvd;
+};
+
+struct xhci_erst {
+       struct xhci_erst_entry  *entries;
+       unsigned int            num_entries;
+       /* xhci->event_ring keeps track of segment dma addresses */
+       dma_addr_t              erst_dma_addr;
+       /* Num entries the ERST can contain */
+       unsigned int            erst_size;
+};
+
+/*
+ * Each segment table entry is 4*32bits long.  1K seems like an ok size:
+ * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
+ * meaning 64 ring segments.
+ * Initial allocated size of the ERST, in number of entries */
+#define        ERST_NUM_SEGS   1
+/* Initial allocated size of the ERST, in number of entries */
+#define        ERST_SIZE       64
+/* Initial number of event segment rings allocated */
+#define        ERST_ENTRIES    1
+/* Poll every 60 seconds */
+#define        POLL_TIMEOUT    60
+/* XXX: Make these module parameters */
+
+
+/* There is one ehci_hci structure per controller */
+struct xhci_hcd {
+       /* glue to PCI and HCD framework */
+       struct xhci_cap_regs __iomem *cap_regs;
+       struct xhci_op_regs __iomem *op_regs;
+       struct xhci_run_regs __iomem *run_regs;
+       struct xhci_doorbell_array __iomem *dba;
+       /* Our HCD's current interrupter register set */
+       struct  xhci_intr_reg __iomem *ir_set;
+
+       /* Cached register copies of read-only HC data */
+       __u32           hcs_params1;
+       __u32           hcs_params2;
+       __u32           hcs_params3;
+       __u32           hcc_params;
+
+       spinlock_t      lock;
+
+       /* packed release number */
+       u8              sbrn;
+       u16             hci_version;
+       u8              max_slots;
+       u8              max_interrupters;
+       u8              max_ports;
+       u8              isoc_threshold;
+       int             event_ring_max;
+       int             addr_64;
+       /* 4KB min, 128MB max */
+       int             page_size;
+       /* Valid values are 12 to 20, inclusive */
+       int             page_shift;
+       /* only one MSI vector for now, but might need more later */
+       int             msix_count;
+       struct msix_entry       *msix_entries;
+       /* data structures */
+       struct xhci_device_context_array *dcbaa;
+       struct xhci_ring        *cmd_ring;
+       struct xhci_ring        *event_ring;
+       struct xhci_erst        erst;
+       /* slot enabling and address device helpers */
+       struct completion       addr_dev;
+       int slot_id;
+       /* Internal mirror of the HW's dcbaa */
+       struct xhci_virt_device *devs[MAX_HC_SLOTS];
+
+       /* DMA pools */
+       struct dma_pool *device_pool;
+       struct dma_pool *segment_pool;
+
+#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
+       /* Poll the rings - for debugging */
+       struct timer_list       event_ring_timer;
+       int                     zombie;
+#endif
+       /* Statistics */
+       int                     noops_submitted;
+       int                     noops_handled;
+       int                     error_bitmask;
+};
+
+/* For testing purposes */
+#define NUM_TEST_NOOPS 0
+
+/* convert between an HCD pointer and the corresponding EHCI_HCD */
+static inline struct xhci_hcd *hcd_to_xhci(struct usb_hcd *hcd)
+{
+       return (struct xhci_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci)
+{
+       return container_of((void *) xhci, struct usb_hcd, hcd_priv);
+}
+
+#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
+#define XHCI_DEBUG     1
+#else
+#define XHCI_DEBUG     0
+#endif
+
+#define xhci_dbg(xhci, fmt, args...) \
+       do { if (XHCI_DEBUG) dev_dbg(xhci_to_hcd(xhci)->self.controller , fmt , ## args); } while (0)
+#define xhci_info(xhci, fmt, args...) \
+       do { if (XHCI_DEBUG) dev_info(xhci_to_hcd(xhci)->self.controller , fmt , ## args); } while (0)
+#define xhci_err(xhci, fmt, args...) \
+       dev_err(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
+#define xhci_warn(xhci, fmt, args...) \
+       dev_warn(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
+
+/* TODO: copied from ehci.h - can be refactored? */
+/* xHCI spec says all registers are little endian */
+static inline unsigned int xhci_readl(const struct xhci_hcd *xhci,
+               __u32 __iomem *regs)
+{
+       return readl(regs);
+}
+static inline void xhci_writel(struct xhci_hcd *xhci,
+               const unsigned int val, __u32 __iomem *regs)
+{
+       if (!in_interrupt())
+               xhci_dbg(xhci,
+                        "`MEM_WRITE_DWORD(3'b000, 32'h%p, 32'h%0x, 4'hf);\n",
+                        regs, val);
+       writel(val, regs);
+}
+
+/* xHCI debugging */
+void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int set_num);
+void xhci_print_registers(struct xhci_hcd *xhci);
+void xhci_dbg_regs(struct xhci_hcd *xhci);
+void xhci_print_run_regs(struct xhci_hcd *xhci);
+void xhci_print_trb_offsets(struct xhci_hcd *xhci, union xhci_trb *trb);
+void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb);
+void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg);
+void xhci_debug_ring(struct xhci_hcd *xhci, struct xhci_ring *ring);
+void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst);
+void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci);
+void xhci_dbg_ring_ptrs(struct xhci_hcd *xhci, struct xhci_ring *ring);
+void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_device_control *ctx, dma_addr_t dma, unsigned int last_ep);
+
+/* xHCI memory managment */
+void xhci_mem_cleanup(struct xhci_hcd *xhci);
+int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags);
+void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id);
+int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags);
+int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev);
+unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc);
+unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc);
+void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep);
+int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev,
+               struct usb_device *udev, struct usb_host_endpoint *ep,
+               gfp_t mem_flags);
+void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
+
+#ifdef CONFIG_PCI
+/* xHCI PCI glue */
+int xhci_register_pci(void);
+void xhci_unregister_pci(void);
+#endif
+
+/* xHCI host controller glue */
+int xhci_halt(struct xhci_hcd *xhci);
+int xhci_reset(struct xhci_hcd *xhci);
+int xhci_init(struct usb_hcd *hcd);
+int xhci_run(struct usb_hcd *hcd);
+void xhci_stop(struct usb_hcd *hcd);
+void xhci_shutdown(struct usb_hcd *hcd);
+int xhci_get_frame(struct usb_hcd *hcd);
+irqreturn_t xhci_irq(struct usb_hcd *hcd);
+int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
+void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
+int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep);
+int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep);
+int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+
+/* xHCI ring, segment, TRB, and TD functions */
+dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
+void xhci_ring_cmd_db(struct xhci_hcd *xhci);
+void *xhci_setup_one_noop(struct xhci_hcd *xhci);
+void xhci_handle_event(struct xhci_hcd *xhci);
+void xhci_set_hc_event_deq(struct xhci_hcd *xhci);
+int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id);
+int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
+               u32 slot_id);
+int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
+               unsigned int ep_index);
+int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
+               int slot_id, unsigned int ep_index);
+int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
+               int slot_id, unsigned int ep_index);
+int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
+               u32 slot_id);
+
+/* xHCI roothub code */
+int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
+               char *buf, u16 wLength);
+int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
+
+#endif /* __LINUX_XHCI_HCD_H */
index 7603cbe..30ea7ca 100644 (file)
@@ -1,7 +1,7 @@
 
 config USB_SISUSBVGA
        tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)"
-       depends on USB && USB_EHCI_HCD
+       depends on USB && (USB_MUSB_HDRC || USB_EHCI_HCD)
         ---help---
          Say Y here if you intend to attach a USB2VGA dongle based on a
          Net2280 and a SiS315 chip.
index 5f1a19d..a9f06d7 100644 (file)
@@ -1072,23 +1072,34 @@ static int unlink1 (struct usbtest_dev *dev, int pipe, int size, int async)
         */
        msleep (jiffies % (2 * INTERRUPT_RATE));
        if (async) {
-retry:
-               retval = usb_unlink_urb (urb);
-               if (retval == -EBUSY || retval == -EIDRM) {
-                       /* we can't unlink urbs while they're completing.
-                        * or if they've completed, and we haven't resubmitted.
-                        * "normal" drivers would prevent resubmission, but
-                        * since we're testing unlink paths, we can't.
-                        */
-                       ERROR(dev,  "unlink retry\n");
-                       goto retry;
+               while (!completion_done(&completion)) {
+                       retval = usb_unlink_urb(urb);
+
+                       switch (retval) {
+                       case -EBUSY:
+                       case -EIDRM:
+                               /* we can't unlink urbs while they're completing
+                                * or if they've completed, and we haven't
+                                * resubmitted. "normal" drivers would prevent
+                                * resubmission, but since we're testing unlink
+                                * paths, we can't.
+                                */
+                               ERROR(dev, "unlink retry\n");
+                               continue;
+                       case 0:
+                       case -EINPROGRESS:
+                               break;
+
+                       default:
+                               dev_err(&dev->intf->dev,
+                                       "unlink fail %d\n", retval);
+                               return retval;
+                       }
+
+                       break;
                }
        } else
                usb_kill_urb (urb);
-       if (!(retval == 0 || retval == -EINPROGRESS)) {
-               dev_err(&dev->intf->dev, "unlink fail %d\n", retval);
-               return retval;
-       }
 
        wait_for_completion (&completion);
        retval = urb->status;
index 1f71543..a7eb4c9 100644 (file)
@@ -733,7 +733,7 @@ int __init mon_text_init(void)
 {
        struct dentry *mondir;
 
-       mondir = debugfs_create_dir("usbmon", NULL);
+       mondir = debugfs_create_dir("usbmon", usb_debug_root);
        if (IS_ERR(mondir)) {
                printk(KERN_NOTICE TAG ": debugfs is not available\n");
                return -ENODEV;
index b66e854..70073b1 100644 (file)
@@ -10,6 +10,7 @@ comment "Enable Host or Gadget support to see Inventra options"
 config USB_MUSB_HDRC
        depends on (USB || USB_GADGET) && HAVE_CLK
        depends on !SUPERH
+       select NOP_USB_XCEIV if ARCH_DAVINCI
        select TWL4030_USB if MACH_OMAP_3430SDP
        select USB_OTG_UTILS
        tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
@@ -55,6 +56,7 @@ comment "Blackfin high speed USB Support"
 config USB_TUSB6010
        boolean "TUSB 6010 support"
        depends on USB_MUSB_HDRC && !USB_MUSB_SOC
+       select NOP_USB_XCEIV
        default y
        help
          The TUSB 6010 chip, from Texas Instruments, connects a discrete
index 7861348..f2f66eb 100644 (file)
@@ -143,7 +143,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
        u16 val;
 
        spin_lock_irqsave(&musb->lock, flags);
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_IDLE:
        case OTG_STATE_A_WAIT_BCON:
                /* Start a new session */
@@ -154,7 +154,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
                val = musb_readw(musb->mregs, MUSB_DEVCTL);
                if (!(val & MUSB_DEVCTL_BDEVICE)) {
                        gpio_set_value(musb->config->gpio_vrsel, 1);
-                       musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
+                       musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
                } else {
                        gpio_set_value(musb->config->gpio_vrsel, 0);
 
@@ -247,6 +247,11 @@ int __init musb_platform_init(struct musb *musb)
        }
        gpio_direction_output(musb->config->gpio_vrsel, 0);
 
+       usb_nop_xceiv_register();
+       musb->xceiv = otg_get_transceiver();
+       if (!musb->xceiv)
+               return -ENODEV;
+
        if (ANOMALY_05000346) {
                bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
                SSYNC();
@@ -291,7 +296,7 @@ int __init musb_platform_init(struct musb *musb)
                        musb_conn_timer_handler, (unsigned long) musb);
        }
        if (is_peripheral_enabled(musb))
-               musb->xceiv.set_power = bfin_set_power;
+               musb->xceiv->set_power = bfin_set_power;
 
        musb->isr = blackfin_interrupt;
 
index 1976e9b..c3577bb 100644 (file)
@@ -6,6 +6,7 @@
  * The TUSB6020, using VLYNQ, has CPPI that looks much like DaVinci.
  */
 
+#include <linux/platform_device.h>
 #include <linux/usb.h>
 
 #include "musb_core.h"
@@ -1145,17 +1146,27 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
        return completed;
 }
 
-void cppi_completion(struct musb *musb, u32 rx, u32 tx)
+irqreturn_t cppi_interrupt(int irq, void *dev_id)
 {
-       void __iomem            *tibase;
-       int                     i, index;
+       struct musb             *musb = dev_id;
        struct cppi             *cppi;
+       void __iomem            *tibase;
        struct musb_hw_ep       *hw_ep = NULL;
+       u32                     rx, tx;
+       int                     i, index;
 
        cppi = container_of(musb->dma_controller, struct cppi, controller);
 
        tibase = musb->ctrl_base;
 
+       tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
+       rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
+
+       if (!tx && !rx)
+               return IRQ_NONE;
+
+       DBG(4, "CPPI IRQ Tx%x Rx%x\n", tx, rx);
+
        /* process TX channels */
        for (index = 0; tx; tx = tx >> 1, index++) {
                struct cppi_channel             *tx_ch;
@@ -1273,6 +1284,8 @@ void cppi_completion(struct musb *musb, u32 rx, u32 tx)
 
        /* write to CPPI EOI register to re-enable interrupts */
        musb_writel(tibase, DAVINCI_CPPI_EOI_REG, 0);
+
+       return IRQ_HANDLED;
 }
 
 /* Instantiate a software object representing a DMA controller. */
@@ -1280,6 +1293,9 @@ struct dma_controller *__init
 dma_controller_create(struct musb *musb, void __iomem *mregs)
 {
        struct cppi             *controller;
+       struct device           *dev = musb->controller;
+       struct platform_device  *pdev = to_platform_device(dev);
+       int                     irq = platform_get_irq(pdev, 1);
 
        controller = kzalloc(sizeof *controller, GFP_KERNEL);
        if (!controller)
@@ -1310,6 +1326,15 @@ dma_controller_create(struct musb *musb, void __iomem *mregs)
                return NULL;
        }
 
+       if (irq > 0) {
+               if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) {
+                       dev_err(dev, "request_irq %d failed!\n", irq);
+                       dma_controller_destroy(&controller->controller);
+                       return NULL;
+               }
+               controller->irq = irq;
+       }
+
        return &controller->controller;
 }
 
@@ -1322,6 +1347,9 @@ void dma_controller_destroy(struct dma_controller *c)
 
        cppi = container_of(c, struct cppi, controller);
 
+       if (cppi->irq)
+               free_irq(cppi->irq, cppi->musb);
+
        /* assert:  caller stopped the controller first */
        dma_pool_destroy(cppi->pool);
 
index 729b407..8a39de3 100644 (file)
@@ -119,6 +119,8 @@ struct cppi {
        void __iomem                    *mregs;         /* Mentor regs */
        void __iomem                    *tibase;        /* TI/CPPI regs */
 
+       int                             irq;
+
        struct cppi_channel             tx[4];
        struct cppi_channel             rx[4];
 
@@ -127,7 +129,7 @@ struct cppi {
        struct list_head                tx_complete;
 };
 
-/* irq handling hook */
-extern void cppi_completion(struct musb *, u32 rx, u32 tx);
+/* CPPI IRQ handler */
+extern irqreturn_t cppi_interrupt(int, void *);
 
 #endif                         /* end of ifndef _CPPI_DMA_H_ */
index 10d11ab..180d7da 100644 (file)
@@ -215,7 +215,7 @@ static void otg_timer(unsigned long _musb)
        DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb));
 
        spin_lock_irqsave(&musb->lock, flags);
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_WAIT_VFALL:
                /* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL
                 * seems to mis-handle session "start" otherwise (or in our
@@ -226,7 +226,7 @@ static void otg_timer(unsigned long _musb)
                        mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
                        break;
                }
-               musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+               musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
                musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
                        MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
                break;
@@ -251,7 +251,7 @@ static void otg_timer(unsigned long _musb)
                if (devctl & MUSB_DEVCTL_BDEVICE)
                        mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
                else
-                       musb->xceiv.state = OTG_STATE_A_IDLE;
+                       musb->xceiv->state = OTG_STATE_A_IDLE;
                break;
        default:
                break;
@@ -265,6 +265,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
        irqreturn_t     retval = IRQ_NONE;
        struct musb     *musb = __hci;
        void __iomem    *tibase = musb->ctrl_base;
+       struct cppi     *cppi;
        u32             tmp;
 
        spin_lock_irqsave(&musb->lock, flags);
@@ -281,16 +282,9 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
        /* CPPI interrupts share the same IRQ line, but have their own
         * mask, state, "vector", and EOI registers.
         */
-       if (is_cppi_enabled()) {
-               u32 cppi_tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
-               u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
-
-               if (cppi_tx || cppi_rx) {
-                       DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx);
-                       cppi_completion(musb, cppi_rx, cppi_tx);
-                       retval = IRQ_HANDLED;
-               }
-       }
+       cppi = container_of(musb->dma_controller, struct cppi, controller);
+       if (is_cppi_enabled() && musb->dma_controller && !cppi->irq)
+               retval = cppi_interrupt(irq, __hci);
 
        /* ack and handle non-CPPI interrupts */
        tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);
@@ -331,21 +325,21 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
                         * to stop registering in devctl.
                         */
                        musb->int_usb &= ~MUSB_INTR_VBUSERROR;
-                       musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
+                       musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
                        mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
                        WARNING("VBUS error workaround (delay coming)\n");
                } else if (is_host_enabled(musb) && drvvbus) {
                        musb->is_active = 1;
                        MUSB_HST_MODE(musb);
-                       musb->xceiv.default_a = 1;
-                       musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+                       musb->xceiv->default_a = 1;
+                       musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
                        portstate(musb->port1_status |= USB_PORT_STAT_POWER);
                        del_timer(&otg_workaround);
                } else {
                        musb->is_active = 0;
                        MUSB_DEV_MODE(musb);
-                       musb->xceiv.default_a = 0;
-                       musb->xceiv.state = OTG_STATE_B_IDLE;
+                       musb->xceiv->default_a = 0;
+                       musb->xceiv->state = OTG_STATE_B_IDLE;
                        portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
                }
 
@@ -367,17 +361,12 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
 
        /* poll for ID change */
        if (is_otg_enabled(musb)
-                       && musb->xceiv.state == OTG_STATE_B_IDLE)
+                       && musb->xceiv->state == OTG_STATE_B_IDLE)
                mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
 
        spin_unlock_irqrestore(&musb->lock, flags);
 
-       /* REVISIT we sometimes get unhandled IRQs
-        * (e.g. ep0).  not clear why...
-        */
-       if (retval != IRQ_HANDLED)
-               DBG(5, "unhandled? %08x\n", tmp);
-       return IRQ_HANDLED;
+       return retval;
 }
 
 int musb_platform_set_mode(struct musb *musb, u8 mode)
@@ -391,6 +380,11 @@ int __init musb_platform_init(struct musb *musb)
        void __iomem    *tibase = musb->ctrl_base;
        u32             revision;
 
+       usb_nop_xceiv_register();
+       musb->xceiv = otg_get_transceiver();
+       if (!musb->xceiv)
+               return -ENODEV;
+
        musb->mregs += DAVINCI_BASE_OFFSET;
 
        clk_enable(musb->clock);
@@ -398,7 +392,7 @@ int __init musb_platform_init(struct musb *musb)
        /* returns zero if e.g. not clocked */
        revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG);
        if (revision == 0)
-               return -ENODEV;
+               goto fail;
 
        if (is_host_enabled(musb))
                setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
@@ -432,6 +426,10 @@ int __init musb_platform_init(struct musb *musb)
 
        musb->isr = davinci_interrupt;
        return 0;
+
+fail:
+       usb_nop_xceiv_unregister();
+       return -ENODEV;
 }
 
 int musb_platform_exit(struct musb *musb)
@@ -442,7 +440,7 @@ int musb_platform_exit(struct musb *musb)
        davinci_source_power(musb, 0 /*off*/, 1);
 
        /* delay, to avoid problems with module reload */
-       if (is_host_enabled(musb) && musb->xceiv.default_a) {
+       if (is_host_enabled(musb) && musb->xceiv->default_a) {
                int     maxdelay = 30;
                u8      devctl, warn = 0;
 
@@ -471,5 +469,7 @@ int musb_platform_exit(struct musb *musb)
 
        clk_disable(musb->clock);
 
+       usb_nop_xceiv_unregister();
+
        return 0;
 }
index 4000cf6..554a414 100644 (file)
 #include "davinci.h"
 #endif
 
+#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
 
 
 unsigned musb_debug;
@@ -267,7 +268,7 @@ void musb_load_testpacket(struct musb *musb)
 
 const char *otg_state_string(struct musb *musb)
 {
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_IDLE:          return "a_idle";
        case OTG_STATE_A_WAIT_VRISE:    return "a_wait_vrise";
        case OTG_STATE_A_WAIT_BCON:     return "a_wait_bcon";
@@ -288,12 +289,6 @@ const char *otg_state_string(struct musb *musb)
 #ifdef CONFIG_USB_MUSB_OTG
 
 /*
- * See also USB_OTG_1-3.pdf 6.6.5 Timers
- * REVISIT: Are the other timers done in the hardware?
- */
-#define TB_ASE0_BRST           100     /* Min 3.125 ms */
-
-/*
  * Handles OTG hnp timeouts, such as b_ase0_brst
  */
 void musb_otg_timer_func(unsigned long data)
@@ -302,16 +297,18 @@ void musb_otg_timer_func(unsigned long data)
        unsigned long   flags;
 
        spin_lock_irqsave(&musb->lock, flags);
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_B_WAIT_ACON:
                DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n");
                musb_g_disconnect(musb);
-               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                musb->is_active = 0;
                break;
+       case OTG_STATE_A_SUSPEND:
        case OTG_STATE_A_WAIT_BCON:
-               DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n");
-               musb_hnp_stop(musb);
+               DBG(1, "HNP: %s timeout\n", otg_state_string(musb));
+               musb_set_vbus(musb, 0);
+               musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
                break;
        default:
                DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb));
@@ -320,10 +317,8 @@ void musb_otg_timer_func(unsigned long data)
        spin_unlock_irqrestore(&musb->lock, flags);
 }
 
-static DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0);
-
 /*
- * Stops the B-device HNP state. Caller must take care of locking.
+ * Stops the HNP transition. Caller must take care of locking.
  */
 void musb_hnp_stop(struct musb *musb)
 {
@@ -331,20 +326,17 @@ void musb_hnp_stop(struct musb *musb)
        void __iomem    *mbase = musb->mregs;
        u8      reg;
 
-       switch (musb->xceiv.state) {
+       DBG(1, "HNP: stop from %s\n", otg_state_string(musb));
+
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_PERIPHERAL:
-       case OTG_STATE_A_WAIT_VFALL:
-       case OTG_STATE_A_WAIT_BCON:
-               DBG(1, "HNP: Switching back to A-host\n");
                musb_g_disconnect(musb);
-               musb->xceiv.state = OTG_STATE_A_IDLE;
-               MUSB_HST_MODE(musb);
-               musb->is_active = 0;
+               DBG(1, "HNP: back to %s\n", otg_state_string(musb));
                break;
        case OTG_STATE_B_HOST:
                DBG(1, "HNP: Disabling HR\n");
                hcd->self.is_b_host = 0;
-               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                MUSB_DEV_MODE(musb);
                reg = musb_readb(mbase, MUSB_POWER);
                reg |= MUSB_POWER_SUSPENDM;
@@ -402,7 +394,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
 
                if (devctl & MUSB_DEVCTL_HM) {
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
-                       switch (musb->xceiv.state) {
+                       switch (musb->xceiv->state) {
                        case OTG_STATE_A_SUSPEND:
                                /* remote wakeup?  later, GetPortStatus
                                 * will stop RESUME signaling
@@ -425,12 +417,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                musb->rh_timer = jiffies
                                                + msecs_to_jiffies(20);
 
-                               musb->xceiv.state = OTG_STATE_A_HOST;
+                               musb->xceiv->state = OTG_STATE_A_HOST;
                                musb->is_active = 1;
                                usb_hcd_resume_root_hub(musb_to_hcd(musb));
                                break;
                        case OTG_STATE_B_WAIT_ACON:
-                               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                                musb->is_active = 1;
                                MUSB_DEV_MODE(musb);
                                break;
@@ -441,11 +433,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                        }
 #endif
                } else {
-                       switch (musb->xceiv.state) {
+                       switch (musb->xceiv->state) {
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
                        case OTG_STATE_A_SUSPEND:
                                /* possibly DISCONNECT is upcoming */
-                               musb->xceiv.state = OTG_STATE_A_HOST;
+                               musb->xceiv->state = OTG_STATE_A_HOST;
                                usb_hcd_resume_root_hub(musb_to_hcd(musb));
                                break;
 #endif
@@ -490,7 +482,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                 */
                musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
                musb->ep0_stage = MUSB_EP0_START;
-               musb->xceiv.state = OTG_STATE_A_IDLE;
+               musb->xceiv->state = OTG_STATE_A_IDLE;
                MUSB_HST_MODE(musb);
                musb_set_vbus(musb, 1);
 
@@ -516,7 +508,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                 * REVISIT:  do delays from lots of DEBUG_KERNEL checks
                 * make trouble here, keeping VBUS < 4.4V ?
                 */
-               switch (musb->xceiv.state) {
+               switch (musb->xceiv->state) {
                case OTG_STATE_A_HOST:
                        /* recovery is dicey once we've gotten past the
                         * initial stages of enumeration, but if VBUS
@@ -594,37 +586,40 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                if (devctl & MUSB_DEVCTL_LSDEV)
                        musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
 
-               if (hcd->status_urb)
-                       usb_hcd_poll_rh_status(hcd);
-               else
-                       usb_hcd_resume_root_hub(hcd);
-
-               MUSB_HST_MODE(musb);
-
                /* indicate new connection to OTG machine */
-               switch (musb->xceiv.state) {
+               switch (musb->xceiv->state) {
                case OTG_STATE_B_PERIPHERAL:
                        if (int_usb & MUSB_INTR_SUSPEND) {
                                DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n");
-                               musb->xceiv.state = OTG_STATE_B_HOST;
-                               hcd->self.is_b_host = 1;
                                int_usb &= ~MUSB_INTR_SUSPEND;
+                               goto b_host;
                        } else
                                DBG(1, "CONNECT as b_peripheral???\n");
                        break;
                case OTG_STATE_B_WAIT_ACON:
-                       DBG(1, "HNP: Waiting to switch to b_host state\n");
-                       musb->xceiv.state = OTG_STATE_B_HOST;
+                       DBG(1, "HNP: CONNECT, now b_host\n");
+b_host:
+                       musb->xceiv->state = OTG_STATE_B_HOST;
                        hcd->self.is_b_host = 1;
+                       musb->ignore_disconnect = 0;
+                       del_timer(&musb->otg_timer);
                        break;
                default:
                        if ((devctl & MUSB_DEVCTL_VBUS)
                                        == (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
-                               musb->xceiv.state = OTG_STATE_A_HOST;
+                               musb->xceiv->state = OTG_STATE_A_HOST;
                                hcd->self.is_b_host = 0;
                        }
                        break;
                }
+
+               /* poke the root hub */
+               MUSB_HST_MODE(musb);
+               if (hcd->status_urb)
+                       usb_hcd_poll_rh_status(hcd);
+               else
+                       usb_hcd_resume_root_hub(hcd);
+
                DBG(1, "CONNECT (%s) devctl %02x\n",
                                otg_state_string(musb), devctl);
        }
@@ -650,7 +645,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                        }
                } else if (is_peripheral_capable()) {
                        DBG(1, "BUS RESET as %s\n", otg_state_string(musb));
-                       switch (musb->xceiv.state) {
+                       switch (musb->xceiv->state) {
 #ifdef CONFIG_USB_OTG
                        case OTG_STATE_A_SUSPEND:
                                /* We need to ignore disconnect on suspend
@@ -661,24 +656,27 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                musb_g_reset(musb);
                                /* FALLTHROUGH */
                        case OTG_STATE_A_WAIT_BCON:     /* OPT TD.4.7-900ms */
-                               DBG(1, "HNP: Setting timer as %s\n",
-                                               otg_state_string(musb));
-                               musb_otg_timer.data = (unsigned long)musb;
-                               mod_timer(&musb_otg_timer, jiffies
-                                       + msecs_to_jiffies(100));
+                               /* never use invalid T(a_wait_bcon) */
+                               DBG(1, "HNP: in %s, %d msec timeout\n",
+                                               otg_state_string(musb),
+                                               TA_WAIT_BCON(musb));
+                               mod_timer(&musb->otg_timer, jiffies
+                                       + msecs_to_jiffies(TA_WAIT_BCON(musb)));
                                break;
                        case OTG_STATE_A_PERIPHERAL:
-                               musb_hnp_stop(musb);
+                               musb->ignore_disconnect = 0;
+                               del_timer(&musb->otg_timer);
+                               musb_g_reset(musb);
                                break;
                        case OTG_STATE_B_WAIT_ACON:
                                DBG(1, "HNP: RESET (%s), to b_peripheral\n",
                                        otg_state_string(musb));
-                               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                                musb_g_reset(musb);
                                break;
 #endif
                        case OTG_STATE_B_IDLE:
-                               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                                /* FALLTHROUGH */
                        case OTG_STATE_B_PERIPHERAL:
                                musb_g_reset(musb);
@@ -763,7 +761,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
                                MUSB_MODE(musb), devctl);
                handled = IRQ_HANDLED;
 
-               switch (musb->xceiv.state) {
+               switch (musb->xceiv->state) {
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
                case OTG_STATE_A_HOST:
                case OTG_STATE_A_SUSPEND:
@@ -776,7 +774,16 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
 #endif /* HOST */
 #ifdef CONFIG_USB_MUSB_OTG
                case OTG_STATE_B_HOST:
-                       musb_hnp_stop(musb);
+                       /* REVISIT this behaves for "real disconnect"
+                        * cases; make sure the other transitions from
+                        * from B_HOST act right too.  The B_HOST code
+                        * in hnp_stop() is currently not used...
+                        */
+                       musb_root_disconnect(musb);
+                       musb_to_hcd(musb)->self.is_b_host = 0;
+                       musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+                       MUSB_DEV_MODE(musb);
+                       musb_g_disconnect(musb);
                        break;
                case OTG_STATE_A_PERIPHERAL:
                        musb_hnp_stop(musb);
@@ -805,26 +812,35 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
                                otg_state_string(musb), devctl, power);
                handled = IRQ_HANDLED;
 
-               switch (musb->xceiv.state) {
+               switch (musb->xceiv->state) {
 #ifdef CONFIG_USB_MUSB_OTG
                case OTG_STATE_A_PERIPHERAL:
-                       /*
-                        * We cannot stop HNP here, devctl BDEVICE might be
-                        * still set.
+                       /* We also come here if the cable is removed, since
+                        * this silicon doesn't report ID-no-longer-grounded.
+                        *
+                        * We depend on T(a_wait_bcon) to shut us down, and
+                        * hope users don't do anything dicey during this
+                        * undesired detour through A_WAIT_BCON.
                         */
+                       musb_hnp_stop(musb);
+                       usb_hcd_resume_root_hub(musb_to_hcd(musb));
+                       musb_root_disconnect(musb);
+                       musb_platform_try_idle(musb, jiffies
+                                       + msecs_to_jiffies(musb->a_wait_bcon
+                                               ? : OTG_TIME_A_WAIT_BCON));
                        break;
 #endif
                case OTG_STATE_B_PERIPHERAL:
                        musb_g_suspend(musb);
                        musb->is_active = is_otg_enabled(musb)
-                                       && musb->xceiv.gadget->b_hnp_enable;
+                                       && musb->xceiv->gadget->b_hnp_enable;
                        if (musb->is_active) {
 #ifdef CONFIG_USB_MUSB_OTG
-                               musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
+                               musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
                                DBG(1, "HNP: Setting timer for b_ase0_brst\n");
-                               musb_otg_timer.data = (unsigned long)musb;
-                               mod_timer(&musb_otg_timer, jiffies
-                                       + msecs_to_jiffies(TB_ASE0_BRST));
+                               mod_timer(&musb->otg_timer, jiffies
+                                       + msecs_to_jiffies(
+                                                       OTG_TIME_B_ASE0_BRST));
 #endif
                        }
                        break;
@@ -834,9 +850,9 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
                                        + msecs_to_jiffies(musb->a_wait_bcon));
                        break;
                case OTG_STATE_A_HOST:
-                       musb->xceiv.state = OTG_STATE_A_SUSPEND;
+                       musb->xceiv->state = OTG_STATE_A_SUSPEND;
                        musb->is_active = is_otg_enabled(musb)
-                                       && musb->xceiv.host->b_hnp_enable;
+                                       && musb->xceiv->host->b_hnp_enable;
                        break;
                case OTG_STATE_B_HOST:
                        /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
@@ -1068,14 +1084,13 @@ static struct fifo_cfg __initdata mode_4_cfg[] = {
 { .hw_ep_num =  8, .style = FIFO_RX,   .maxpacket = 512, },
 { .hw_ep_num =  9, .style = FIFO_TX,   .maxpacket = 512, },
 { .hw_ep_num =  9, .style = FIFO_RX,   .maxpacket = 512, },
-{ .hw_ep_num = 10, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num = 10, .style = FIFO_RX,   .maxpacket = 512, },
-{ .hw_ep_num = 11, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num = 11, .style = FIFO_RX,   .maxpacket = 512, },
-{ .hw_ep_num = 12, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num = 12, .style = FIFO_RX,   .maxpacket = 512, },
-{ .hw_ep_num = 13, .style = FIFO_TX,   .maxpacket = 512, },
-{ .hw_ep_num = 13, .style = FIFO_RX,   .maxpacket = 512, },
+{ .hw_ep_num = 10, .style = FIFO_TX,   .maxpacket = 256, },
+{ .hw_ep_num = 10, .style = FIFO_RX,   .maxpacket = 64, },
+{ .hw_ep_num = 11, .style = FIFO_TX,   .maxpacket = 256, },
+{ .hw_ep_num = 11, .style = FIFO_RX,   .maxpacket = 64, },
+{ .hw_ep_num = 12, .style = FIFO_TX,   .maxpacket = 256, },
+{ .hw_ep_num = 12, .style = FIFO_RX,   .maxpacket = 64, },
+{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 4096, },
 { .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
 { .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
 };
@@ -1335,11 +1350,11 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
        }
        if (reg & MUSB_CONFIGDATA_HBRXE) {
                strcat(aInfo, ", HB-ISO Rx");
-               strcat(aInfo, " (X)");          /* no driver support */
+               musb->hb_iso_rx = true;
        }
        if (reg & MUSB_CONFIGDATA_HBTXE) {
                strcat(aInfo, ", HB-ISO Tx");
-               strcat(aInfo, " (X)");          /* no driver support */
+               musb->hb_iso_tx = true;
        }
        if (reg & MUSB_CONFIGDATA_SOFTCONE)
                strcat(aInfo, ", SoftConn");
@@ -1481,13 +1496,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
 
        spin_unlock_irqrestore(&musb->lock, flags);
 
-       /* REVISIT we sometimes get spurious IRQs on g_ep0
-        * not clear why...
-        */
-       if (retval != IRQ_HANDLED)
-               DBG(5, "spurious?\n");
-
-       return IRQ_HANDLED;
+       return retval;
 }
 
 #else
@@ -1687,8 +1696,9 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr,
        }
 
        spin_lock_irqsave(&musb->lock, flags);
-       musb->a_wait_bcon = val;
-       if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON)
+       /* force T(a_wait_bcon) to be zero/unlimited *OR* valid */
+       musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ;
+       if (musb->xceiv->state == OTG_STATE_A_WAIT_BCON)
                musb->is_active = 0;
        musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val));
        spin_unlock_irqrestore(&musb->lock, flags);
@@ -1706,10 +1716,13 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
 
        spin_lock_irqsave(&musb->lock, flags);
        val = musb->a_wait_bcon;
+       /* FIXME get_vbus_status() is normally #defined as false...
+        * and is effectively TUSB-specific.
+        */
        vbus = musb_platform_get_vbus_status(musb);
        spin_unlock_irqrestore(&musb->lock, flags);
 
-       return sprintf(buf, "Vbus %s, timeout %lu\n",
+       return sprintf(buf, "Vbus %s, timeout %lu msec\n",
                        vbus ? "on" : "off", val);
 }
 static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
@@ -1749,8 +1762,8 @@ static void musb_irq_work(struct work_struct *data)
        struct musb *musb = container_of(data, struct musb, irq_work);
        static int old_state;
 
-       if (musb->xceiv.state != old_state) {
-               old_state = musb->xceiv.state;
+       if (musb->xceiv->state != old_state) {
+               old_state = musb->xceiv->state;
                sysfs_notify(&musb->controller->kobj, NULL, "mode");
        }
 }
@@ -1782,6 +1795,7 @@ allocate_instance(struct device *dev,
        hcd->uses_new_polling = 1;
 
        musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
+       musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON;
 #else
        musb = kzalloc(sizeof *musb, GFP_KERNEL);
        if (!musb)
@@ -1847,7 +1861,7 @@ static void musb_free(struct musb *musb)
        }
 
 #ifdef CONFIG_USB_MUSB_OTG
-       put_device(musb->xceiv.dev);
+       put_device(musb->xceiv->dev);
 #endif
 
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
@@ -1928,10 +1942,18 @@ bad_config:
                }
        }
 
-       /* assume vbus is off */
-
-       /* platform adjusts musb->mregs and musb->isr if needed,
-        * and activates clocks
+       /* 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()
+        *   - activates clocks.
+        *   - stops powering VBUS
+        *   - assigns musb->board_set_vbus if host mode is enabled
+        *
+        * There are various transciever configurations.  Blackfin,
+        * DaVinci, TUSB60x0, and others integrate them.  OMAP3 uses
+        * external/discrete ones in various flavors (twl4030 family,
+        * isp1504, non-OTG, etc) mostly hooking up through ULPI.
         */
        musb->isr = generic_interrupt;
        status = musb_platform_init(musb);
@@ -1968,6 +1990,10 @@ bad_config:
        if (status < 0)
                goto fail2;
 
+#ifdef CONFIG_USB_OTG
+       setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb);
+#endif
+
        /* Init IRQ workqueue before request_irq */
        INIT_WORK(&musb->irq_work, musb_irq_work);
 
@@ -1999,17 +2025,17 @@ bad_config:
                                ? "DMA" : "PIO",
                        musb->nIrq);
 
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
-       /* host side needs more setup, except for no-host modes */
-       if (musb->board_mode != MUSB_PERIPHERAL) {
+       /* host side needs more setup */
+       if (is_host_enabled(musb)) {
                struct usb_hcd  *hcd = musb_to_hcd(musb);
 
-               if (musb->board_mode == MUSB_OTG)
+               otg_set_host(musb->xceiv, &hcd->self);
+
+               if (is_otg_enabled(musb))
                        hcd->self.otg_port = 1;
-               musb->xceiv.host = &hcd->self;
+               musb->xceiv->host = &hcd->self;
                hcd->power_budget = 2 * (plat->power ? : 250);
        }
-#endif                         /* CONFIG_USB_MUSB_HDRC_HCD */
 
        /* For the host-only role, we can activate right away.
         * (We expect the ID pin to be forcibly grounded!!)
@@ -2017,8 +2043,8 @@ bad_config:
         */
        if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
                MUSB_HST_MODE(musb);
-               musb->xceiv.default_a = 1;
-               musb->xceiv.state = OTG_STATE_A_IDLE;
+               musb->xceiv->default_a = 1;
+               musb->xceiv->state = OTG_STATE_A_IDLE;
 
                status = usb_add_hcd(musb_to_hcd(musb), -1, 0);
                if (status)
@@ -2033,8 +2059,8 @@ bad_config:
 
        } else /* peripheral is enabled */ {
                MUSB_DEV_MODE(musb);
-               musb->xceiv.default_a = 0;
-               musb->xceiv.state = OTG_STATE_B_IDLE;
+               musb->xceiv->default_a = 0;
+               musb->xceiv->state = OTG_STATE_B_IDLE;
 
                status = musb_gadget_setup(musb);
                if (status)
index efb39b5..f3772ca 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/interrupt.h>
 #include <linux/smp_lock.h>
 #include <linux/errno.h>
+#include <linux/timer.h>
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/usb/ch9.h>
@@ -171,7 +172,8 @@ enum musb_h_ep0_state {
 
 /* peripheral side ep0 states */
 enum musb_g_ep0_state {
-       MUSB_EP0_STAGE_SETUP,           /* idle, waiting for setup */
+       MUSB_EP0_STAGE_IDLE,            /* idle, waiting for SETUP */
+       MUSB_EP0_STAGE_SETUP,           /* received SETUP */
        MUSB_EP0_STAGE_TX,              /* IN data */
        MUSB_EP0_STAGE_RX,              /* OUT data */
        MUSB_EP0_STAGE_STATUSIN,        /* (after OUT data) */
@@ -179,10 +181,15 @@ enum musb_g_ep0_state {
        MUSB_EP0_STAGE_ACKWAIT,         /* after zlp, before statusin */
 } __attribute__ ((packed));
 
-/* OTG protocol constants */
+/*
+ * OTG protocol constants.  See USB OTG 1.3 spec,
+ * sections 5.5 "Device Timings" and 6.6.5 "Timers".
+ */
 #define OTG_TIME_A_WAIT_VRISE  100             /* msec (max) */
-#define OTG_TIME_A_WAIT_BCON   0               /* 0=infinite; min 1000 msec */
-#define OTG_TIME_A_IDLE_BDIS   200             /* msec (min) */
+#define OTG_TIME_A_WAIT_BCON   1100            /* min 1 second */
+#define OTG_TIME_A_AIDL_BDIS   200             /* min 200 msec */
+#define OTG_TIME_B_ASE0_BRST   100             /* min 3.125 ms */
+
 
 /*************************** REGISTER ACCESS ********************************/
 
@@ -331,6 +338,8 @@ struct musb {
        struct list_head        control;        /* of musb_qh */
        struct list_head        in_bulk;        /* of musb_qh */
        struct list_head        out_bulk;       /* of musb_qh */
+
+       struct timer_list       otg_timer;
 #endif
 
        /* called with IRQs blocked; ON/nonzero implies starting a session,
@@ -355,7 +364,7 @@ struct musb {
        u16                     int_rx;
        u16                     int_tx;
 
-       struct otg_transceiver  xceiv;
+       struct otg_transceiver  *xceiv;
 
        int nIrq;
        unsigned                irq_wake:1;
@@ -386,6 +395,9 @@ struct musb {
        unsigned is_multipoint:1;
        unsigned ignore_disconnect:1;   /* during bus resets */
 
+       unsigned                hb_iso_rx:1;    /* high bandwidth iso rx? */
+       unsigned                hb_iso_tx:1;    /* high bandwidth iso tx? */
+
 #ifdef C_MP_TX
        unsigned bulk_split:1;
 #define        can_bulk_split(musb,type) \
index f79440c..8b3c4e2 100644 (file)
@@ -310,7 +310,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
                        /* setup DMA, then program endpoint CSR */
                        request_size = min(request->length,
                                                musb_ep->dma->max_len);
-                       if (request_size <= musb_ep->packet_sz)
+                       if (request_size < musb_ep->packet_sz)
                                musb_ep->dma->desired_mode = 0;
                        else
                                musb_ep->dma->desired_mode = 1;
@@ -349,7 +349,8 @@ static void txstate(struct musb *musb, struct musb_request *req)
 #elif defined(CONFIG_USB_TI_CPPI_DMA)
                /* program endpoint CSR first, then setup DMA */
                csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
-               csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_DMAENAB;
+               csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE |
+                      MUSB_TXCSR_MODE;
                musb_writew(epio, MUSB_TXCSR,
                        (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN)
                                | csr);
@@ -1405,7 +1406,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
 
        spin_lock_irqsave(&musb->lock, flags);
 
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_B_PERIPHERAL:
                /* NOTE:  OTG state machine doesn't include B_SUSPENDED;
                 * that's part of the standard usb 1.1 state machine, and
@@ -1507,9 +1508,9 @@ static int musb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
 {
        struct musb     *musb = gadget_to_musb(gadget);
 
-       if (!musb->xceiv.set_power)
+       if (!musb->xceiv->set_power)
                return -EOPNOTSUPP;
-       return otg_set_power(&musb->xceiv, mA);
+       return otg_set_power(musb->xceiv, mA);
 }
 
 static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on)
@@ -1732,11 +1733,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
 
                spin_lock_irqsave(&musb->lock, flags);
 
-               /* REVISIT always use otg_set_peripheral(), handling
-                * issues including the root hub one below ...
-                */
-               musb->xceiv.gadget = &musb->g;
-               musb->xceiv.state = OTG_STATE_B_IDLE;
+               otg_set_peripheral(musb->xceiv, &musb->g);
                musb->is_active = 1;
 
                /* FIXME this ignores the softconnect flag.  Drivers are
@@ -1748,6 +1745,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
                if (!is_otg_enabled(musb))
                        musb_start(musb);
 
+               otg_set_peripheral(musb->xceiv, &musb->g);
+
                spin_unlock_irqrestore(&musb->lock, flags);
 
                if (is_otg_enabled(musb)) {
@@ -1761,8 +1760,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
                        if (retval < 0) {
                                DBG(1, "add_hcd failed, %d\n", retval);
                                spin_lock_irqsave(&musb->lock, flags);
-                               musb->xceiv.gadget = NULL;
-                               musb->xceiv.state = OTG_STATE_UNDEFINED;
+                               otg_set_peripheral(musb->xceiv, NULL);
                                musb->gadget_driver = NULL;
                                musb->g.dev.driver = NULL;
                                spin_unlock_irqrestore(&musb->lock, flags);
@@ -1845,8 +1843,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 
                (void) musb_gadget_vbus_draw(&musb->g, 0);
 
-               musb->xceiv.state = OTG_STATE_UNDEFINED;
+               musb->xceiv->state = OTG_STATE_UNDEFINED;
                stop_activity(musb, driver);
+               otg_set_peripheral(musb->xceiv, NULL);
 
                DBG(3, "unregistering driver %s\n", driver->function);
                spin_unlock_irqrestore(&musb->lock, flags);
@@ -1882,7 +1881,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
 void musb_g_resume(struct musb *musb)
 {
        musb->is_suspended = 0;
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_B_IDLE:
                break;
        case OTG_STATE_B_WAIT_ACON:
@@ -1908,10 +1907,10 @@ void musb_g_suspend(struct musb *musb)
        devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
        DBG(3, "devctl %02x\n", devctl);
 
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_B_IDLE:
                if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
-                       musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+                       musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                break;
        case OTG_STATE_B_PERIPHERAL:
                musb->is_suspended = 1;
@@ -1957,22 +1956,24 @@ void musb_g_disconnect(struct musb *musb)
                spin_lock(&musb->lock);
        }
 
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        default:
 #ifdef CONFIG_USB_MUSB_OTG
                DBG(2, "Unhandled disconnect %s, setting a_idle\n",
                        otg_state_string(musb));
-               musb->xceiv.state = OTG_STATE_A_IDLE;
+               musb->xceiv->state = OTG_STATE_A_IDLE;
+               MUSB_HST_MODE(musb);
                break;
        case OTG_STATE_A_PERIPHERAL:
-               musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
+               musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+               MUSB_HST_MODE(musb);
                break;
        case OTG_STATE_B_WAIT_ACON:
        case OTG_STATE_B_HOST:
 #endif
        case OTG_STATE_B_PERIPHERAL:
        case OTG_STATE_B_IDLE:
-               musb->xceiv.state = OTG_STATE_B_IDLE;
+               musb->xceiv->state = OTG_STATE_B_IDLE;
                break;
        case OTG_STATE_B_SRP_INIT:
                break;
@@ -2028,10 +2029,10 @@ __acquires(musb->lock)
         * or else after HNP, as A-Device
         */
        if (devctl & MUSB_DEVCTL_BDEVICE) {
-               musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+               musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                musb->g.is_a_peripheral = 0;
        } else if (is_otg_enabled(musb)) {
-               musb->xceiv.state = OTG_STATE_A_PERIPHERAL;
+               musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
                musb->g.is_a_peripheral = 1;
        } else
                WARN_ON(1);
index 3f5e30d..40ed50e 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2005 Mentor Graphics Corporation
  * Copyright (C) 2005-2006 by Texas Instruments
  * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -58,7 +59,8 @@
 static char *decode_ep0stage(u8 stage)
 {
        switch (stage) {
-       case MUSB_EP0_STAGE_SETUP:      return "idle";
+       case MUSB_EP0_STAGE_IDLE:       return "idle";
+       case MUSB_EP0_STAGE_SETUP:      return "setup";
        case MUSB_EP0_STAGE_TX:         return "in";
        case MUSB_EP0_STAGE_RX:         return "out";
        case MUSB_EP0_STAGE_ACKWAIT:    return "wait";
@@ -628,7 +630,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
                musb_writew(regs, MUSB_CSR0,
                                csr & ~MUSB_CSR0_P_SENTSTALL);
                retval = IRQ_HANDLED;
-               musb->ep0_state = MUSB_EP0_STAGE_SETUP;
+               musb->ep0_state = MUSB_EP0_STAGE_IDLE;
                csr = musb_readw(regs, MUSB_CSR0);
        }
 
@@ -636,7 +638,18 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
        if (csr & MUSB_CSR0_P_SETUPEND) {
                musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND);
                retval = IRQ_HANDLED;
-               musb->ep0_state = MUSB_EP0_STAGE_SETUP;
+               /* Transition into the early status phase */
+               switch (musb->ep0_state) {
+               case MUSB_EP0_STAGE_TX:
+                       musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT;
+                       break;
+               case MUSB_EP0_STAGE_RX:
+                       musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
+                       break;
+               default:
+                       ERR("SetupEnd came in a wrong ep0stage %s",
+                           decode_ep0stage(musb->ep0_state));
+               }
                csr = musb_readw(regs, MUSB_CSR0);
                /* NOTE:  request may need completion */
        }
@@ -697,11 +710,31 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
                        if (req)
                                musb_g_ep0_giveback(musb, req);
                }
+
+               /*
+                * In case when several interrupts can get coalesced,
+                * check to see if we've already received a SETUP packet...
+                */
+               if (csr & MUSB_CSR0_RXPKTRDY)
+                       goto setup;
+
+               retval = IRQ_HANDLED;
+               musb->ep0_state = MUSB_EP0_STAGE_IDLE;
+               break;
+
+       case MUSB_EP0_STAGE_IDLE:
+               /*
+                * This state is typically (but not always) indiscernible
+                * from the status states since the corresponding interrupts
+                * tend to happen within too little period of time (with only
+                * a zero-length packet in between) and so get coalesced...
+                */
                retval = IRQ_HANDLED;
                musb->ep0_state = MUSB_EP0_STAGE_SETUP;
                /* FALLTHROUGH */
 
        case MUSB_EP0_STAGE_SETUP:
+setup:
                if (csr & MUSB_CSR0_RXPKTRDY) {
                        struct usb_ctrlrequest  setup;
                        int                     handled = 0;
@@ -783,7 +816,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
 stall:
                                DBG(3, "stall (%d)\n", handled);
                                musb->ackpend |= MUSB_CSR0_P_SENDSTALL;
-                               musb->ep0_state = MUSB_EP0_STAGE_SETUP;
+                               musb->ep0_state = MUSB_EP0_STAGE_IDLE;
 finish:
                                musb_writew(regs, MUSB_CSR0,
                                                musb->ackpend);
@@ -803,7 +836,7 @@ finish:
                /* "can't happen" */
                WARN_ON(1);
                musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL);
-               musb->ep0_state = MUSB_EP0_STAGE_SETUP;
+               musb->ep0_state = MUSB_EP0_STAGE_IDLE;
                break;
        }
 
@@ -959,7 +992,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value)
 
                csr |= MUSB_CSR0_P_SENDSTALL;
                musb_writew(regs, MUSB_CSR0, csr);
-               musb->ep0_state = MUSB_EP0_STAGE_SETUP;
+               musb->ep0_state = MUSB_EP0_STAGE_IDLE;
                musb->ackpend = 0;
                break;
        default:
index db1b574..94a2a35 100644 (file)
@@ -181,6 +181,19 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
        musb_writew(ep->regs, MUSB_TXCSR, txcsr);
 }
 
+static void musb_ep_set_qh(struct musb_hw_ep *ep, int is_in, struct musb_qh *qh)
+{
+       if (is_in != 0 || ep->is_shared_fifo)
+               ep->in_qh  = qh;
+       if (is_in == 0 || ep->is_shared_fifo)
+               ep->out_qh = qh;
+}
+
+static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in)
+{
+       return is_in ? ep->in_qh : ep->out_qh;
+}
+
 /*
  * Start the URB at the front of an endpoint's queue
  * end must be claimed from the caller.
@@ -210,7 +223,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
        case USB_ENDPOINT_XFER_CONTROL:
                /* control transfers always start with SETUP */
                is_in = 0;
-               hw_ep->out_qh = qh;
                musb->ep0_stage = MUSB_EP0_START;
                buf = urb->setup_packet;
                len = 8;
@@ -239,10 +251,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
                        epnum, buf + offset, len);
 
        /* Configure endpoint */
-       if (is_in || hw_ep->is_shared_fifo)
-               hw_ep->in_qh = qh;
-       else
-               hw_ep->out_qh = qh;
+       musb_ep_set_qh(hw_ep, is_in, qh);
        musb_ep_program(musb, epnum, urb, !is_in, buf, offset, len);
 
        /* transmit may have more work: start it when it is time */
@@ -286,9 +295,8 @@ start:
        }
 }
 
-/* caller owns controller lock, irqs are blocked */
-static void
-__musb_giveback(struct musb *musb, struct urb *urb, int status)
+/* Context: caller owns controller lock, IRQs are blocked */
+static void musb_giveback(struct musb *musb, struct urb *urb, int status)
 __releases(musb->lock)
 __acquires(musb->lock)
 {
@@ -321,60 +329,57 @@ __acquires(musb->lock)
        spin_lock(&musb->lock);
 }
 
-/* for bulk/interrupt endpoints only */
-static inline void
-musb_save_toggle(struct musb_hw_ep *ep, int is_in, struct urb *urb)
+/* For bulk/interrupt endpoints only */
+static inline void musb_save_toggle(struct musb_qh *qh, int is_in,
+                                   struct urb *urb)
 {
-       struct usb_device       *udev = urb->dev;
+       void __iomem            *epio = qh->hw_ep->regs;
        u16                     csr;
-       void __iomem            *epio = ep->regs;
-       struct musb_qh          *qh;
 
-       /* FIXME:  the current Mentor DMA code seems to have
+       /*
+        * FIXME: the current Mentor DMA code seems to have
         * problems getting toggle correct.
         */
 
-       if (is_in || ep->is_shared_fifo)
-               qh = ep->in_qh;
+       if (is_in)
+               csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE;
        else
-               qh = ep->out_qh;
+               csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE;
 
-       if (!is_in) {
-               csr = musb_readw(epio, MUSB_TXCSR);
-               usb_settoggle(udev, qh->epnum, 1,
-                       (csr & MUSB_TXCSR_H_DATATOGGLE)
-                               ? 1 : 0);
-       } else {
-               csr = musb_readw(epio, MUSB_RXCSR);
-               usb_settoggle(udev, qh->epnum, 0,
-                       (csr & MUSB_RXCSR_H_DATATOGGLE)
-                               ? 1 : 0);
-       }
+       usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0);
 }
 
-/* caller owns controller lock, irqs are blocked */
-static struct musb_qh *
-musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
+/*
+ * Advance this hardware endpoint's queue, completing the specified URB and
+ * advancing to either the next URB queued to that qh, or else invalidating
+ * that qh and advancing to the next qh scheduled after the current one.
+ *
+ * Context: caller owns controller lock, IRQs are blocked
+ */
+static void musb_advance_schedule(struct musb *musb, struct urb *urb,
+                                 struct musb_hw_ep *hw_ep, int is_in)
 {
+       struct musb_qh          *qh = musb_ep_get_qh(hw_ep, is_in);
        struct musb_hw_ep       *ep = qh->hw_ep;
-       struct musb             *musb = ep->musb;
-       int                     is_in = usb_pipein(urb->pipe);
        int                     ready = qh->is_ready;
+       int                     status;
+
+       status = (urb->status == -EINPROGRESS) ? 0 : urb->status;
 
        /* save toggle eagerly, for paranoia */
        switch (qh->type) {
        case USB_ENDPOINT_XFER_BULK:
        case USB_ENDPOINT_XFER_INT:
-               musb_save_toggle(ep, is_in, urb);
+               musb_save_toggle(qh, is_in, urb);
                break;
        case USB_ENDPOINT_XFER_ISOC:
-               if (status == 0 && urb->error_count)
+               if (urb->error_count)
                        status = -EXDEV;
                break;
        }
 
        qh->is_ready = 0;
-       __musb_giveback(musb, urb, status);
+       musb_giveback(musb, urb, status);
        qh->is_ready = ready;
 
        /* reclaim resources (and bandwidth) ASAP; deschedule it, and
@@ -388,11 +393,8 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
                else
                        ep->tx_reinit = 1;
 
-               /* clobber old pointers to this qh */
-               if (is_in || ep->is_shared_fifo)
-                       ep->in_qh = NULL;
-               else
-                       ep->out_qh = NULL;
+               /* Clobber old pointers to this qh */
+               musb_ep_set_qh(ep, is_in, NULL);
                qh->hep->hcpriv = NULL;
 
                switch (qh->type) {
@@ -421,36 +423,10 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
                        break;
                }
        }
-       return qh;
-}
-
-/*
- * Advance this hardware endpoint's queue, completing the specified urb and
- * advancing to either the next urb queued to that qh, or else invalidating
- * that qh and advancing to the next qh scheduled after the current one.
- *
- * Context: caller owns controller lock, irqs are blocked
- */
-static void
-musb_advance_schedule(struct musb *musb, struct urb *urb,
-               struct musb_hw_ep *hw_ep, int is_in)
-{
-       struct musb_qh  *qh;
-
-       if (is_in || hw_ep->is_shared_fifo)
-               qh = hw_ep->in_qh;
-       else
-               qh = hw_ep->out_qh;
-
-       if (urb->status == -EINPROGRESS)
-               qh = musb_giveback(qh, urb, 0);
-       else
-               qh = musb_giveback(qh, urb, urb->status);
 
        if (qh != NULL && qh->is_ready) {
                DBG(4, "... next ep%d %cX urb %p\n",
-                               hw_ep->epnum, is_in ? 'R' : 'T',
-                               next_urb(qh));
+                   hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh));
                musb_start_urb(musb, is_in, qh);
        }
 }
@@ -629,7 +605,8 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
        musb_writeb(ep->regs, MUSB_RXTYPE, qh->type_reg);
        musb_writeb(ep->regs, MUSB_RXINTERVAL, qh->intv_reg);
        /* NOTE: bulk combining rewrites high bits of maxpacket */
-       musb_writew(ep->regs, MUSB_RXMAXP, qh->maxpacket);
+       musb_writew(ep->regs, MUSB_RXMAXP,
+                       qh->maxpacket | ((qh->hb_mult - 1) << 11));
 
        ep->rx_reinit = 0;
 }
@@ -651,9 +628,10 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
        csr = musb_readw(epio, MUSB_TXCSR);
        if (length > pkt_size) {
                mode = 1;
-               csr |= MUSB_TXCSR_AUTOSET
-                       | MUSB_TXCSR_DMAMODE
-                       | MUSB_TXCSR_DMAENAB;
+               csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
+               /* autoset shouldn't be set in high bandwidth */
+               if (qh->hb_mult == 1)
+                       csr |= MUSB_TXCSR_AUTOSET;
        } else {
                mode = 0;
                csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
@@ -703,15 +681,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
        void __iomem            *mbase = musb->mregs;
        struct musb_hw_ep       *hw_ep = musb->endpoints + epnum;
        void __iomem            *epio = hw_ep->regs;
-       struct musb_qh          *qh;
-       u16                     packet_sz;
-
-       if (!is_out || hw_ep->is_shared_fifo)
-               qh = hw_ep->in_qh;
-       else
-               qh = hw_ep->out_qh;
-
-       packet_sz = qh->maxpacket;
+       struct musb_qh          *qh = musb_ep_get_qh(hw_ep, !is_out);
+       u16                     packet_sz = qh->maxpacket;
 
        DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s "
                                "h_addr%02x h_port%02x bytes %d\n",
@@ -1129,17 +1100,14 @@ void musb_host_tx(struct musb *musb, u8 epnum)
        u16                     tx_csr;
        size_t                  length = 0;
        size_t                  offset = 0;
-       struct urb              *urb;
        struct musb_hw_ep       *hw_ep = musb->endpoints + epnum;
        void __iomem            *epio = hw_ep->regs;
-       struct musb_qh          *qh = hw_ep->is_shared_fifo ? hw_ep->in_qh
-                                                           : hw_ep->out_qh;
+       struct musb_qh          *qh = hw_ep->out_qh;
+       struct urb              *urb = next_urb(qh);
        u32                     status = 0;
        void __iomem            *mbase = musb->mregs;
        struct dma_channel      *dma;
 
-       urb = next_urb(qh);
-
        musb_ep_select(mbase, epnum);
        tx_csr = musb_readw(epio, MUSB_TXCSR);
 
@@ -1427,7 +1395,7 @@ static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep)
                        urb->actual_length += dma->actual_len;
                        dma->actual_len = 0L;
                }
-               musb_save_toggle(ep, 1, urb);
+               musb_save_toggle(cur_qh, 1, urb);
 
                /* move cur_qh to end of queue */
                list_move_tail(&cur_qh->ring, &musb->in_bulk);
@@ -1531,6 +1499,10 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                        /* packet error reported later */
                        iso_err = true;
                }
+       } else if (rx_csr & MUSB_RXCSR_INCOMPRX) {
+               DBG(3, "end %d high bandwidth incomplete ISO packet RX\n",
+                               epnum);
+               status = -EPROTO;
        }
 
        /* faults abort the transfer */
@@ -1738,7 +1710,11 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                                val &= ~MUSB_RXCSR_H_AUTOREQ;
                        else
                                val |= MUSB_RXCSR_H_AUTOREQ;
-                       val |= MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB;
+                       val |= MUSB_RXCSR_DMAENAB;
+
+                       /* autoclear shouldn't be set in high bandwidth */
+                       if (qh->hb_mult == 1)
+                               val |= MUSB_RXCSR_AUTOCLEAR;
 
                        musb_writew(epio, MUSB_RXCSR,
                                MUSB_RXCSR_H_WZC_BITS | val);
@@ -1817,19 +1793,17 @@ static int musb_schedule(
                        epnum++, hw_ep++) {
                int     diff;
 
-               if (is_in || hw_ep->is_shared_fifo) {
-                       if (hw_ep->in_qh  != NULL)
-                               continue;
-               } else  if (hw_ep->out_qh != NULL)
+               if (musb_ep_get_qh(hw_ep, is_in) != NULL)
                        continue;
 
                if (hw_ep == musb->bulk_ep)
                        continue;
 
                if (is_in)
-                       diff = hw_ep->max_packet_sz_rx - qh->maxpacket;
+                       diff = hw_ep->max_packet_sz_rx;
                else
-                       diff = hw_ep->max_packet_sz_tx - qh->maxpacket;
+                       diff = hw_ep->max_packet_sz_tx;
+               diff -= (qh->maxpacket * qh->hb_mult);
 
                if (diff >= 0 && best_diff > diff) {
                        best_diff = diff;
@@ -1932,15 +1906,27 @@ static int musb_urb_enqueue(
        qh->is_ready = 1;
 
        qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize);
+       qh->type = usb_endpoint_type(epd);
 
-       /* no high bandwidth support yet */
-       if (qh->maxpacket & ~0x7ff) {
-               ret = -EMSGSIZE;
-               goto done;
+       /* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier.
+        * Some musb cores don't support high bandwidth ISO transfers; and
+        * we don't (yet!) support high bandwidth interrupt transfers.
+        */
+       qh->hb_mult = 1 + ((qh->maxpacket >> 11) & 0x03);
+       if (qh->hb_mult > 1) {
+               int ok = (qh->type == USB_ENDPOINT_XFER_ISOC);
+
+               if (ok)
+                       ok = (usb_pipein(urb->pipe) && musb->hb_iso_rx)
+                               || (usb_pipeout(urb->pipe) && musb->hb_iso_tx);
+               if (!ok) {
+                       ret = -EMSGSIZE;
+                       goto done;
+               }
+               qh->maxpacket &= 0x7ff;
        }
 
        qh->epnum = usb_endpoint_num(epd);
-       qh->type = usb_endpoint_type(epd);
 
        /* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */
        qh->addr_reg = (u8) usb_pipedevice(urb->pipe);
@@ -2052,14 +2038,15 @@ done:
  * called with controller locked, irqs blocked
  * that hardware queue advances to the next transfer, unless prevented
  */
-static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh, int is_in)
+static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
 {
        struct musb_hw_ep       *ep = qh->hw_ep;
        void __iomem            *epio = ep->regs;
        unsigned                hw_end = ep->epnum;
        void __iomem            *regs = ep->musb->mregs;
-       u16                     csr;
+       int                     is_in = usb_pipein(urb->pipe);
        int                     status = 0;
+       u16                     csr;
 
        musb_ep_select(regs, hw_end);
 
@@ -2112,14 +2099,14 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 {
        struct musb             *musb = hcd_to_musb(hcd);
        struct musb_qh          *qh;
-       struct list_head        *sched;
        unsigned long           flags;
+       int                     is_in  = usb_pipein(urb->pipe);
        int                     ret;
 
        DBG(4, "urb=%p, dev%d ep%d%s\n", urb,
                        usb_pipedevice(urb->pipe),
                        usb_pipeendpoint(urb->pipe),
-                       usb_pipein(urb->pipe) ? "in" : "out");
+                       is_in ? "in" : "out");
 
        spin_lock_irqsave(&musb->lock, flags);
        ret = usb_hcd_check_unlink_urb(hcd, urb, status);
@@ -2130,47 +2117,25 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
        if (!qh)
                goto done;
 
-       /* Any URB not actively programmed into endpoint hardware can be
+       /*
+        * Any URB not actively programmed into endpoint hardware can be
         * immediately given back; that's any URB not at the head of an
         * endpoint queue, unless someday we get real DMA queues.  And even
         * if it's at the head, it might not be known to the hardware...
         *
-        * Otherwise abort current transfer, pending dma, etc.; urb->status
+        * Otherwise abort current transfer, pending DMA, etc.; urb->status
         * has already been updated.  This is a synchronous abort; it'd be
         * OK to hold off until after some IRQ, though.
+        *
+        * NOTE: qh is invalid unless !list_empty(&hep->urb_list)
         */
-       if (!qh->is_ready || urb->urb_list.prev != &qh->hep->urb_list)
-               ret = -EINPROGRESS;
-       else {
-               switch (qh->type) {
-               case USB_ENDPOINT_XFER_CONTROL:
-                       sched = &musb->control;
-                       break;
-               case USB_ENDPOINT_XFER_BULK:
-                       if (qh->mux == 1) {
-                               if (usb_pipein(urb->pipe))
-                                       sched = &musb->in_bulk;
-                               else
-                                       sched = &musb->out_bulk;
-                               break;
-                       }
-               default:
-                       /* REVISIT when we get a schedule tree, periodic
-                        * transfers won't always be at the head of a
-                        * singleton queue...
-                        */
-                       sched = NULL;
-                       break;
-               }
-       }
-
-       /* NOTE:  qh is invalid unless !list_empty(&hep->urb_list) */
-       if (ret < 0 || (sched && qh != first_qh(sched))) {
+       if (!qh->is_ready
+                       || urb->urb_list.prev != &qh->hep->urb_list
+                       || musb_ep_get_qh(qh->hw_ep, is_in) != qh) {
                int     ready = qh->is_ready;
 
-               ret = 0;
                qh->is_ready = 0;
-               __musb_giveback(musb, urb, 0);
+               musb_giveback(musb, urb, 0);
                qh->is_ready = ready;
 
                /* If nothing else (usually musb_giveback) is using it
@@ -2182,7 +2147,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                        kfree(qh);
                }
        } else
-               ret = musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN);
+               ret = musb_cleanup_urb(urb, qh);
 done:
        spin_unlock_irqrestore(&musb->lock, flags);
        return ret;
@@ -2192,13 +2157,11 @@ done:
 static void
 musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
 {
-       u8                      epnum = hep->desc.bEndpointAddress;
+       u8                      is_in = hep->desc.bEndpointAddress & USB_DIR_IN;
        unsigned long           flags;
        struct musb             *musb = hcd_to_musb(hcd);
-       u8                      is_in = epnum & USB_DIR_IN;
        struct musb_qh          *qh;
        struct urb              *urb;
-       struct list_head        *sched;
 
        spin_lock_irqsave(&musb->lock, flags);
 
@@ -2206,31 +2169,11 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
        if (qh == NULL)
                goto exit;
 
-       switch (qh->type) {
-       case USB_ENDPOINT_XFER_CONTROL:
-               sched = &musb->control;
-               break;
-       case USB_ENDPOINT_XFER_BULK:
-               if (qh->mux == 1) {
-                       if (is_in)
-                               sched = &musb->in_bulk;
-                       else
-                               sched = &musb->out_bulk;
-                       break;
-               }
-       default:
-               /* REVISIT when we get a schedule tree, periodic transfers
-                * won't always be at the head of a singleton queue...
-                */
-               sched = NULL;
-               break;
-       }
-
-       /* NOTE:  qh is invalid unless !list_empty(&hep->urb_list) */
+       /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */
 
-       /* kick first urb off the hardware, if needed */
+       /* Kick the first URB off the hardware, if needed */
        qh->is_ready = 0;
-       if (!sched || qh == first_qh(sched)) {
+       if (musb_ep_get_qh(qh->hw_ep, is_in) == qh) {
                urb = next_urb(qh);
 
                /* make software (then hardware) stop ASAP */
@@ -2238,7 +2181,7 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
                        urb->status = -ESHUTDOWN;
 
                /* cleanup */
-               musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN);
+               musb_cleanup_urb(urb, qh);
 
                /* Then nuke all the others ... and advance the
                 * queue on hw_ep (e.g. bulk ring) when we're done.
@@ -2254,7 +2197,7 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
                 * will activate any of these as it advances.
                 */
                while (!list_empty(&hep->urb_list))
-                       __musb_giveback(musb, next_urb(qh), -ESHUTDOWN);
+                       musb_giveback(musb, next_urb(qh), -ESHUTDOWN);
 
                hep->hcpriv = NULL;
                list_del(&qh->ring);
@@ -2293,7 +2236,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
 {
        struct musb     *musb = hcd_to_musb(hcd);
 
-       if (musb->xceiv.state == OTG_STATE_A_SUSPEND)
+       if (musb->xceiv->state == OTG_STATE_A_SUSPEND)
                return 0;
 
        if (is_host_active(musb) && musb->is_active) {
index 0b7fbcd..14b0077 100644 (file)
@@ -67,6 +67,7 @@ struct musb_qh {
        u8                      is_ready;       /* safe to modify hw_ep */
        u8                      type;           /* XFERTYPE_* */
        u8                      epnum;
+       u8                      hb_mult;        /* high bandwidth pkts per uf */
        u16                     maxpacket;
        u16                     frame;          /* for periodic schedule */
        unsigned                iso_idx;        /* in urb->iso_frame_desc[] */
index bf677ac..bfe5fe4 100644 (file)
@@ -78,18 +78,22 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
                DBG(3, "Root port suspended, power %02x\n", power);
 
                musb->port1_status |= USB_PORT_STAT_SUSPEND;
-               switch (musb->xceiv.state) {
+               switch (musb->xceiv->state) {
                case OTG_STATE_A_HOST:
-                       musb->xceiv.state = OTG_STATE_A_SUSPEND;
+                       musb->xceiv->state = OTG_STATE_A_SUSPEND;
                        musb->is_active = is_otg_enabled(musb)
-                                       && musb->xceiv.host->b_hnp_enable;
+                                       && musb->xceiv->host->b_hnp_enable;
+                       if (musb->is_active)
+                               mod_timer(&musb->otg_timer, jiffies
+                                       + msecs_to_jiffies(
+                                               OTG_TIME_A_AIDL_BDIS));
                        musb_platform_try_idle(musb, 0);
                        break;
 #ifdef CONFIG_USB_MUSB_OTG
                case OTG_STATE_B_HOST:
-                       musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
+                       musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
                        musb->is_active = is_otg_enabled(musb)
-                                       && musb->xceiv.host->b_hnp_enable;
+                                       && musb->xceiv->host->b_hnp_enable;
                        musb_platform_try_idle(musb, 0);
                        break;
 #endif
@@ -116,7 +120,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset)
        void __iomem    *mbase = musb->mregs;
 
 #ifdef CONFIG_USB_MUSB_OTG
-       if (musb->xceiv.state == OTG_STATE_B_IDLE) {
+       if (musb->xceiv->state == OTG_STATE_B_IDLE) {
                DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n");
                musb->port1_status &= ~USB_PORT_STAT_RESET;
                return;
@@ -186,14 +190,23 @@ void musb_root_disconnect(struct musb *musb)
        usb_hcd_poll_rh_status(musb_to_hcd(musb));
        musb->is_active = 0;
 
-       switch (musb->xceiv.state) {
-       case OTG_STATE_A_HOST:
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_SUSPEND:
-               musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
+#ifdef CONFIG_USB_MUSB_OTG
+               if (is_otg_enabled(musb)
+                               && musb->xceiv->host->b_hnp_enable) {
+                       musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
+                       musb->g.is_a_peripheral = 1;
+                       break;
+               }
+#endif
+               /* FALLTHROUGH */
+       case OTG_STATE_A_HOST:
+               musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
                musb->is_active = 0;
                break;
        case OTG_STATE_A_WAIT_VFALL:
-               musb->xceiv.state = OTG_STATE_B_IDLE;
+               musb->xceiv->state = OTG_STATE_B_IDLE;
                break;
        default:
                DBG(1, "host disconnect (%s)\n", otg_state_string(musb));
@@ -332,7 +345,7 @@ int musb_hub_control(
                        musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
                        usb_hcd_poll_rh_status(musb_to_hcd(musb));
                        /* NOTE: it might really be A_WAIT_BCON ... */
-                       musb->xceiv.state = OTG_STATE_A_HOST;
+                       musb->xceiv->state = OTG_STATE_A_HOST;
                }
 
                put_unaligned(cpu_to_le32(musb->port1_status
index 60924ce..3487520 100644 (file)
@@ -44,7 +44,6 @@
 #define        get_cpu_rev()   2
 #endif
 
-#define MUSB_TIMEOUT_A_WAIT_BCON       1100
 
 static struct timer_list musb_idle_timer;
 
@@ -61,17 +60,17 @@ static void musb_do_idle(unsigned long _musb)
 
        devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
 
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_WAIT_BCON:
                devctl &= ~MUSB_DEVCTL_SESSION;
                musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
 
                devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
                if (devctl & MUSB_DEVCTL_BDEVICE) {
-                       musb->xceiv.state = OTG_STATE_B_IDLE;
+                       musb->xceiv->state = OTG_STATE_B_IDLE;
                        MUSB_DEV_MODE(musb);
                } else {
-                       musb->xceiv.state = OTG_STATE_A_IDLE;
+                       musb->xceiv->state = OTG_STATE_A_IDLE;
                        MUSB_HST_MODE(musb);
                }
                break;
@@ -89,7 +88,7 @@ static void musb_do_idle(unsigned long _musb)
                        musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
                        usb_hcd_poll_rh_status(musb_to_hcd(musb));
                        /* NOTE: it might really be A_WAIT_BCON ... */
-                       musb->xceiv.state = OTG_STATE_A_HOST;
+                       musb->xceiv->state = OTG_STATE_A_HOST;
                }
                break;
 #endif
@@ -97,9 +96,9 @@ static void musb_do_idle(unsigned long _musb)
        case OTG_STATE_A_HOST:
                devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
                if (devctl &  MUSB_DEVCTL_BDEVICE)
-                       musb->xceiv.state = OTG_STATE_B_IDLE;
+                       musb->xceiv->state = OTG_STATE_B_IDLE;
                else
-                       musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
+                       musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
 #endif
        default:
                break;
@@ -118,7 +117,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
 
        /* Never idle if active, or when VBUS timeout is not set as host */
        if (musb->is_active || ((musb->a_wait_bcon == 0)
-                       && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
+                       && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
                DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
                del_timer(&musb_idle_timer);
                last_timer = jiffies;
@@ -163,8 +162,8 @@ static void omap_set_vbus(struct musb *musb, int is_on)
 
        if (is_on) {
                musb->is_active = 1;
-               musb->xceiv.default_a = 1;
-               musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+               musb->xceiv->default_a = 1;
+               musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
                devctl |= MUSB_DEVCTL_SESSION;
 
                MUSB_HST_MODE(musb);
@@ -175,8 +174,8 @@ static void omap_set_vbus(struct musb *musb, int is_on)
                 * jumping right to B_IDLE...
                 */
 
-               musb->xceiv.default_a = 0;
-               musb->xceiv.state = OTG_STATE_B_IDLE;
+               musb->xceiv->default_a = 0;
+               musb->xceiv->state = OTG_STATE_B_IDLE;
                devctl &= ~MUSB_DEVCTL_SESSION;
 
                MUSB_DEV_MODE(musb);
@@ -188,10 +187,6 @@ static void omap_set_vbus(struct musb *musb, int is_on)
                otg_state_string(musb),
                musb_readb(musb->mregs, MUSB_DEVCTL));
 }
-static int omap_set_power(struct otg_transceiver *x, unsigned mA)
-{
-       return 0;
-}
 
 static int musb_platform_resume(struct musb *musb);
 
@@ -202,24 +197,6 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
        devctl |= MUSB_DEVCTL_SESSION;
        musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
 
-       switch (musb_mode) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
-       case MUSB_HOST:
-               otg_set_host(&musb->xceiv, musb->xceiv.host);
-               break;
-#endif
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
-       case MUSB_PERIPHERAL:
-               otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget);
-               break;
-#endif
-#ifdef CONFIG_USB_MUSB_OTG
-       case MUSB_OTG:
-               break;
-#endif
-       default:
-               return -EINVAL;
-       }
        return 0;
 }
 
@@ -231,6 +208,16 @@ int __init musb_platform_init(struct musb *musb)
        omap_cfg_reg(AE5_2430_USB0HS_STP);
 #endif
 
+       /* We require some kind of external transceiver, hooked
+        * up through ULPI.  TWL4030-family PMICs include one,
+        * which needs a driver, drivers aren't always needed.
+        */
+       musb->xceiv = otg_get_transceiver();
+       if (!musb->xceiv) {
+               pr_err("HS USB OTG: no transceiver configured\n");
+               return -ENODEV;
+       }
+
        musb_platform_resume(musb);
 
        l = omap_readl(OTG_SYSCONFIG);
@@ -240,7 +227,12 @@ int __init musb_platform_init(struct musb *musb)
        l &= ~AUTOIDLE;         /* disable auto idle */
        l &= ~NOIDLE;           /* remove possible noidle */
        l |= SMARTIDLE;         /* enable smart idle */
-       l |= AUTOIDLE;          /* enable auto idle */
+       /*
+        * MUSB AUTOIDLE don't work in 3430.
+        * Workaround by Richard Woodruff/TI
+        */
+       if (!cpu_is_omap3430())
+               l |= AUTOIDLE;          /* enable auto idle */
        omap_writel(l, OTG_SYSCONFIG);
 
        l = omap_readl(OTG_INTERFSEL);
@@ -257,9 +249,6 @@ int __init musb_platform_init(struct musb *musb)
 
        if (is_host_enabled(musb))
                musb->board_set_vbus = omap_set_vbus;
-       if (is_peripheral_enabled(musb))
-               musb->xceiv.set_power = omap_set_power;
-       musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON;
 
        setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
 
@@ -282,8 +271,7 @@ int musb_platform_suspend(struct musb *musb)
        l |= ENABLEWAKEUP;      /* enable wakeup */
        omap_writel(l, OTG_SYSCONFIG);
 
-       if (musb->xceiv.set_suspend)
-               musb->xceiv.set_suspend(&musb->xceiv, 1);
+       otg_set_suspend(musb->xceiv, 1);
 
        if (musb->set_clock)
                musb->set_clock(musb->clock, 0);
@@ -300,8 +288,7 @@ static int musb_platform_resume(struct musb *musb)
        if (!musb->clock)
                return 0;
 
-       if (musb->xceiv.set_suspend)
-               musb->xceiv.set_suspend(&musb->xceiv, 0);
+       otg_set_suspend(musb->xceiv, 0);
 
        if (musb->set_clock)
                musb->set_clock(musb->clock, 1);
index 4ac1477..88b587c 100644 (file)
@@ -259,6 +259,8 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
                tusb_fifo_read_unaligned(fifo, buf, len);
 }
 
+static struct musb *the_musb;
+
 #ifdef CONFIG_USB_GADGET_MUSB_HDRC
 
 /* This is used by gadget drivers, and OTG transceiver logic, allowing
@@ -269,7 +271,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
  */
 static int tusb_draw_power(struct otg_transceiver *x, unsigned mA)
 {
-       struct musb     *musb = container_of(x, struct musb, xceiv);
+       struct musb     *musb = the_musb;
        void __iomem    *tbase = musb->ctrl_base;
        u32             reg;
 
@@ -419,7 +421,7 @@ static void musb_do_idle(unsigned long _musb)
 
        spin_lock_irqsave(&musb->lock, flags);
 
-       switch (musb->xceiv.state) {
+       switch (musb->xceiv->state) {
        case OTG_STATE_A_WAIT_BCON:
                if ((musb->a_wait_bcon != 0)
                        && (musb->idle_timeout == 0
@@ -483,7 +485,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
 
        /* Never idle if active, or when VBUS timeout is not set as host */
        if (musb->is_active || ((musb->a_wait_bcon == 0)
-                       && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
+                       && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
                DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
                del_timer(&musb_idle_timer);
                last_timer = jiffies;
@@ -532,8 +534,8 @@ static void tusb_source_power(struct musb *musb, int is_on)
                if (musb->set_clock)
                        musb->set_clock(musb->clock, 1);
                timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
-               musb->xceiv.default_a = 1;
-               musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+               musb->xceiv->default_a = 1;
+               musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
                devctl |= MUSB_DEVCTL_SESSION;
 
                conf |= TUSB_DEV_CONF_USB_HOST_MODE;
@@ -546,24 +548,24 @@ static void tusb_source_power(struct musb *musb, int is_on)
                /* If ID pin is grounded, we want to be a_idle */
                otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
                if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) {
-                       switch (musb->xceiv.state) {
+                       switch (musb->xceiv->state) {
                        case OTG_STATE_A_WAIT_VRISE:
                        case OTG_STATE_A_WAIT_BCON:
-                               musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
+                               musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
                                break;
                        case OTG_STATE_A_WAIT_VFALL:
-                               musb->xceiv.state = OTG_STATE_A_IDLE;
+                               musb->xceiv->state = OTG_STATE_A_IDLE;
                                break;
                        default:
-                               musb->xceiv.state = OTG_STATE_A_IDLE;
+                               musb->xceiv->state = OTG_STATE_A_IDLE;
                        }
                        musb->is_active = 0;
-                       musb->xceiv.default_a = 1;
+                       musb->xceiv->default_a = 1;
                        MUSB_HST_MODE(musb);
                } else {
                        musb->is_active = 0;
-                       musb->xceiv.default_a = 0;
-                       musb->xceiv.state = OTG_STATE_B_IDLE;
+                       musb->xceiv->default_a = 0;
+                       musb->xceiv->state = OTG_STATE_B_IDLE;
                        MUSB_DEV_MODE(musb);
                }
 
@@ -674,7 +676,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
                else
                        default_a = is_host_enabled(musb);
                DBG(2, "Default-%c\n", default_a ? 'A' : 'B');
-               musb->xceiv.default_a = default_a;
+               musb->xceiv->default_a = default_a;
                tusb_source_power(musb, default_a);
 
                /* Don't allow idling immediately */
@@ -686,7 +688,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
        if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) {
 
                /* B-dev state machine:  no vbus ~= disconnect */
-               if ((is_otg_enabled(musb) && !musb->xceiv.default_a)
+               if ((is_otg_enabled(musb) && !musb->xceiv->default_a)
                                || !is_host_enabled(musb)) {
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
                        /* ? musb_root_disconnect(musb); */
@@ -701,9 +703,9 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
 
                        if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
                                DBG(1, "Forcing disconnect (no interrupt)\n");
-                               if (musb->xceiv.state != OTG_STATE_B_IDLE) {
+                               if (musb->xceiv->state != OTG_STATE_B_IDLE) {
                                        /* INTR_DISCONNECT can hide... */
-                                       musb->xceiv.state = OTG_STATE_B_IDLE;
+                                       musb->xceiv->state = OTG_STATE_B_IDLE;
                                        musb->int_usb |= MUSB_INTR_DISCONNECT;
                                }
                                musb->is_active = 0;
@@ -717,7 +719,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
                        DBG(2, "vbus change, %s, otg %03x\n",
                                otg_state_string(musb), otg_stat);
 
-                       switch (musb->xceiv.state) {
+                       switch (musb->xceiv->state) {
                        case OTG_STATE_A_IDLE:
                                DBG(2, "Got SRP, turning on VBUS\n");
                                musb_set_vbus(musb, 1);
@@ -765,7 +767,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
 
                DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat);
 
-               switch (musb->xceiv.state) {
+               switch (musb->xceiv->state) {
                case OTG_STATE_A_WAIT_VRISE:
                        /* VBUS has probably been valid for a while now,
                         * but may well have bounced out of range a bit
@@ -777,7 +779,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
                                        DBG(2, "devctl %02x\n", devctl);
                                        break;
                                }
-                               musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
+                               musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
                                musb->is_active = 0;
                                idle_timeout = jiffies
                                        + msecs_to_jiffies(musb->a_wait_bcon);
@@ -1093,9 +1095,14 @@ int __init musb_platform_init(struct musb *musb)
 {
        struct platform_device  *pdev;
        struct resource         *mem;
-       void __iomem            *sync;
+       void __iomem            *sync = NULL;
        int                     ret;
 
+       usb_nop_xceiv_register();
+       musb->xceiv = otg_get_transceiver();
+       if (!musb->xceiv)
+               return -ENODEV;
+
        pdev = to_platform_device(musb->controller);
 
        /* dma address for async dma */
@@ -1106,14 +1113,16 @@ int __init musb_platform_init(struct musb *musb)
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
        if (!mem) {
                pr_debug("no sync dma resource?\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto done;
        }
        musb->sync = mem->start;
 
        sync = ioremap(mem->start, mem->end - mem->start + 1);
        if (!sync) {
                pr_debug("ioremap for sync failed\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto done;
        }
        musb->sync_va = sync;
 
@@ -1126,28 +1135,37 @@ int __init musb_platform_init(struct musb *musb)
        if (ret) {
                printk(KERN_ERR "Could not start tusb6010 (%d)\n",
                                ret);
-               return -ENODEV;
+               goto done;
        }
        musb->isr = tusb_interrupt;
 
        if (is_host_enabled(musb))
                musb->board_set_vbus = tusb_source_power;
-       if (is_peripheral_enabled(musb))
-               musb->xceiv.set_power = tusb_draw_power;
+       if (is_peripheral_enabled(musb)) {
+               musb->xceiv->set_power = tusb_draw_power;
+               the_musb = musb;
+       }
 
        setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
 
+done:
+       if (ret < 0) {
+               if (sync)
+                       iounmap(sync);
+               usb_nop_xceiv_unregister();
+       }
        return ret;
 }
 
 int musb_platform_exit(struct musb *musb)
 {
        del_timer_sync(&musb_idle_timer);
+       the_musb = NULL;
 
        if (musb->board_set_power)
                musb->board_set_power(0);
 
        iounmap(musb->sync_va);
-
+       usb_nop_xceiv_unregister();
        return 0;
 }
index aa884d0..69feeec 100644 (file)
@@ -59,4 +59,18 @@ config NOP_USB_XCEIV
         built-in with usb ip or which are autonomous and doesn't require any
         phy programming such as ISP1x04 etc.
 
+config USB_LANGWELL_OTG
+       tristate "Intel Langwell USB OTG dual-role support"
+       depends on USB && MRST
+       select USB_OTG
+       select USB_OTG_UTILS
+       help
+         Say Y here if you want to build Intel Langwell USB OTG
+         transciever driver in kernel. This driver implements role
+         switch between EHCI host driver and Langwell USB OTG
+         client driver.
+
+         To compile this driver as a module, choose M here: the
+         module will be called langwell_otg.
+
 endif # USB || OTG
index 2081678..6d1abdd 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS)     += otg.o
 obj-$(CONFIG_USB_GPIO_VBUS)    += gpio_vbus.o
 obj-$(CONFIG_ISP1301_OMAP)     += isp1301_omap.o
 obj-$(CONFIG_TWL4030_USB)      += twl4030-usb.o
+obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o
 obj-$(CONFIG_NOP_USB_XCEIV)    += nop-usb-xceiv.o
 
 ccflags-$(CONFIG_USB_DEBUG)    += -DDEBUG
diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c
new file mode 100644 (file)
index 0000000..6f628d0
--- /dev/null
@@ -0,0 +1,1915 @@
+/*
+ * Intel Langwell USB OTG transceiver driver
+ * Copyright (C) 2008 - 2009, Intel Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+/* This driver helps to switch Langwell OTG controller function between host
+ * and peripheral. It works with EHCI driver and Langwell client controller
+ * driver together.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/notifier.h>
+#include <asm/ipc_defs.h>
+#include <linux/delay.h>
+#include "../core/hcd.h"
+
+#include <linux/usb/langwell_otg.h>
+
+#define        DRIVER_DESC             "Intel Langwell USB OTG transceiver driver"
+#define        DRIVER_VERSION          "3.0.0.32L.0002"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Henry Yuan <hang.yuan@intel.com>, Hao Wu <hao.wu@intel.com>");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+static const char driver_name[] = "langwell_otg";
+
+static int langwell_otg_probe(struct pci_dev *pdev,
+                       const struct pci_device_id *id);
+static void langwell_otg_remove(struct pci_dev *pdev);
+static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message);
+static int langwell_otg_resume(struct pci_dev *pdev);
+
+static int langwell_otg_set_host(struct otg_transceiver *otg,
+                               struct usb_bus *host);
+static int langwell_otg_set_peripheral(struct otg_transceiver *otg,
+                               struct usb_gadget *gadget);
+static int langwell_otg_start_srp(struct otg_transceiver *otg);
+
+static const struct pci_device_id pci_ids[] = {{
+       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class_mask =   ~0,
+       .vendor =       0x8086,
+       .device =       0x0811,
+       .subvendor =    PCI_ANY_ID,
+       .subdevice =    PCI_ANY_ID,
+}, { /* end: all zeroes */ }
+};
+
+static struct pci_driver otg_pci_driver = {
+       .name =         (char *) driver_name,
+       .id_table =     pci_ids,
+
+       .probe =        langwell_otg_probe,
+       .remove =       langwell_otg_remove,
+
+       .suspend =      langwell_otg_suspend,
+       .resume =       langwell_otg_resume,
+};
+
+static const char *state_string(enum usb_otg_state state)
+{
+       switch (state) {
+       case OTG_STATE_A_IDLE:
+               return "a_idle";
+       case OTG_STATE_A_WAIT_VRISE:
+               return "a_wait_vrise";
+       case OTG_STATE_A_WAIT_BCON:
+               return "a_wait_bcon";
+       case OTG_STATE_A_HOST:
+               return "a_host";
+       case OTG_STATE_A_SUSPEND:
+               return "a_suspend";
+       case OTG_STATE_A_PERIPHERAL:
+               return "a_peripheral";
+       case OTG_STATE_A_WAIT_VFALL:
+               return "a_wait_vfall";
+       case OTG_STATE_A_VBUS_ERR:
+               return "a_vbus_err";
+       case OTG_STATE_B_IDLE:
+               return "b_idle";
+       case OTG_STATE_B_SRP_INIT:
+               return "b_srp_init";
+       case OTG_STATE_B_PERIPHERAL:
+               return "b_peripheral";
+       case OTG_STATE_B_WAIT_ACON:
+               return "b_wait_acon";
+       case OTG_STATE_B_HOST:
+               return "b_host";
+       default:
+               return "UNDEFINED";
+       }
+}
+
+/* HSM timers */
+static inline struct langwell_otg_timer *otg_timer_initializer
+(void (*function)(unsigned long), unsigned long expires, unsigned long data)
+{
+       struct langwell_otg_timer *timer;
+       timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL);
+       timer->function = function;
+       timer->expires = expires;
+       timer->data = data;
+       return timer;
+}
+
+static struct langwell_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr,
+       *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_res_tmr,
+       *b_bus_suspend_tmr;
+
+static struct list_head active_timers;
+
+static struct langwell_otg *the_transceiver;
+
+/* host/client notify transceiver when event affects HNP state */
+void langwell_update_transceiver()
+{
+       otg_dbg("transceiver driver is notified\n");
+       queue_work(the_transceiver->qwork, &the_transceiver->work);
+}
+EXPORT_SYMBOL(langwell_update_transceiver);
+
+static int langwell_otg_set_host(struct otg_transceiver *otg,
+                                       struct usb_bus *host)
+{
+       otg->host = host;
+
+       return 0;
+}
+
+static int langwell_otg_set_peripheral(struct otg_transceiver *otg,
+                                       struct usb_gadget *gadget)
+{
+       otg->gadget = gadget;
+
+       return 0;
+}
+
+static int langwell_otg_set_power(struct otg_transceiver *otg,
+                               unsigned mA)
+{
+       return 0;
+}
+
+/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/
+static void langwell_otg_drv_vbus(int on)
+{
+       struct ipc_pmic_reg_data        pmic_data = {0};
+       struct ipc_pmic_reg_data        battery_data;
+
+       /* Check if battery is attached or not */
+       battery_data.pmic_reg_data[0].register_address = 0xd2;
+       battery_data.ioc = 0;
+       battery_data.num_entries = 1;
+       if (ipc_pmic_register_read(&battery_data)) {
+               otg_dbg("Failed to read PMIC register 0xd2.\n");
+               return;
+       }
+
+       if ((battery_data.pmic_reg_data[0].value & 0x20) == 0) {
+               otg_dbg("no battery attached\n");
+               return;
+       }
+
+       /* Workaround for battery attachment issue */
+       if (battery_data.pmic_reg_data[0].value == 0x34) {
+               otg_dbg("battery \n");
+               return;
+       }
+
+       otg_dbg("battery attached\n");
+
+       pmic_data.ioc = 0;
+       pmic_data.pmic_reg_data[0].register_address = 0xD4;
+       pmic_data.num_entries = 1;
+       if (on)
+               pmic_data.pmic_reg_data[0].value = 0x20;
+       else
+               pmic_data.pmic_reg_data[0].value = 0xc0;
+
+       if (ipc_pmic_register_write(&pmic_data, TRUE))
+               otg_dbg("Failed to write PMIC.\n");
+
+}
+
+/* charge vbus or discharge vbus through a resistor to ground */
+static void langwell_otg_chrg_vbus(int on)
+{
+
+       u32     val;
+
+       val = readl(the_transceiver->regs + CI_OTGSC);
+
+       if (on)
+               writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC,
+                               the_transceiver->regs + CI_OTGSC);
+       else
+               writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD,
+                               the_transceiver->regs + CI_OTGSC);
+
+}
+
+/* Start SRP */
+static int langwell_otg_start_srp(struct otg_transceiver *otg)
+{
+       u32     val;
+
+       otg_dbg("Start SRP ->\n");
+
+       val = readl(the_transceiver->regs + CI_OTGSC);
+
+       writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP,
+               the_transceiver->regs + CI_OTGSC);
+
+       /* Check if the data plus is finished or not */
+       msleep(8);
+       val = readl(the_transceiver->regs + CI_OTGSC);
+       if (val & (OTGSC_HADP | OTGSC_DP))
+               otg_dbg("DataLine SRP Error\n");
+
+       /* FIXME: VBus SRP */
+
+       return 0;
+}
+
+
+/* stop SOF via bus_suspend */
+static void langwell_otg_loc_sof(int on)
+{
+       struct usb_hcd  *hcd;
+       int             err;
+
+       otg_dbg("loc_sof -> %d\n", on);
+
+       hcd = bus_to_hcd(the_transceiver->otg.host);
+       if (on)
+               err = hcd->driver->bus_resume(hcd);
+       else
+               err = hcd->driver->bus_suspend(hcd);
+
+       if (err)
+               otg_dbg("Failed to resume/suspend bus - %d\n", err);
+}
+
+static void langwell_otg_phy_low_power(int on)
+{
+       u32     val;
+
+       otg_dbg("phy low power mode-> %d\n", on);
+
+       val = readl(the_transceiver->regs + CI_HOSTPC1);
+       if (on)
+               writel(val | HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1);
+       else
+               writel(val & ~HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1);
+}
+
+/* Enable/Disable OTG interrupt */
+static void langwell_otg_intr(int on)
+{
+       u32 val;
+
+       otg_dbg("interrupt -> %d\n", on);
+
+       val = readl(the_transceiver->regs + CI_OTGSC);
+       if (on) {
+               val = val | (OTGSC_INTEN_MASK | OTGSC_IDPU);
+               writel(val, the_transceiver->regs + CI_OTGSC);
+       } else {
+               val = val & ~(OTGSC_INTEN_MASK | OTGSC_IDPU);
+               writel(val, the_transceiver->regs + CI_OTGSC);
+       }
+}
+
+/* set HAAR: Hardware Assist Auto-Reset */
+static void langwell_otg_HAAR(int on)
+{
+       u32     val;
+
+       otg_dbg("HAAR -> %d\n", on);
+
+       val = readl(the_transceiver->regs + CI_OTGSC);
+       if (on)
+               writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR,
+                               the_transceiver->regs + CI_OTGSC);
+       else
+               writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR,
+                               the_transceiver->regs + CI_OTGSC);
+}
+
+/* set HABA: Hardware Assist B-Disconnect to A-Connect */
+static void langwell_otg_HABA(int on)
+{
+       u32     val;
+
+       otg_dbg("HABA -> %d\n", on);
+
+       val = readl(the_transceiver->regs + CI_OTGSC);
+       if (on)
+               writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA,
+                               the_transceiver->regs + CI_OTGSC);
+       else
+               writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA,
+                               the_transceiver->regs + CI_OTGSC);
+}
+
+static int langwell_otg_check_se0_srp(int on)
+{
+       u32 val;
+
+       int delay_time = TB_SE0_SRP * 10; /* step is 100us */
+
+       otg_dbg("check_se0_srp -> \n");
+
+       do {
+               udelay(100);
+               if (!delay_time--)
+                       break;
+               val = readl(the_transceiver->regs + CI_PORTSC1);
+               val &= PORTSC_LS;
+       } while (!val);
+
+       otg_dbg("check_se0_srp <- \n");
+       return val;
+}
+
+/* The timeout callback function to set time out bit */
+static void set_tmout(unsigned long indicator)
+{
+       *(int *)indicator = 1;
+}
+
+void langwell_otg_nsf_msg(unsigned long indicator)
+{
+       switch (indicator) {
+       case 2:
+       case 4:
+       case 6:
+       case 7:
+               printk(KERN_ERR "OTG:NSF-%lu - deivce not responding\n",
+                               indicator);
+               break;
+       case 3:
+               printk(KERN_ERR "OTG:NSF-%lu - deivce not supported\n",
+                               indicator);
+               break;
+       default:
+               printk(KERN_ERR "Do not have this kind of NSF\n");
+               break;
+       }
+}
+
+/* Initialize timers */
+static void langwell_otg_init_timers(struct otg_hsm *hsm)
+{
+       /* HSM used timers */
+       a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
+                               (unsigned long)&hsm->a_wait_vrise_tmout);
+       a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON,
+                               (unsigned long)&hsm->a_wait_bcon_tmout);
+       a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
+                               (unsigned long)&hsm->a_aidl_bdis_tmout);
+       b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST,
+                               (unsigned long)&hsm->b_ase0_brst_tmout);
+       b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
+                               (unsigned long)&hsm->b_se0_srp);
+       b_srp_res_tmr = otg_timer_initializer(&set_tmout, TB_SRP_RES,
+                               (unsigned long)&hsm->b_srp_res_tmout);
+       b_bus_suspend_tmr = otg_timer_initializer(&set_tmout, TB_BUS_SUSPEND,
+                               (unsigned long)&hsm->b_bus_suspend_tmout);
+}
+
+/* Free timers */
+static void langwell_otg_free_timers(void)
+{
+       kfree(a_wait_vrise_tmr);
+       kfree(a_wait_bcon_tmr);
+       kfree(a_aidl_bdis_tmr);
+       kfree(b_ase0_brst_tmr);
+       kfree(b_se0_srp_tmr);
+       kfree(b_srp_res_tmr);
+       kfree(b_bus_suspend_tmr);
+}
+
+/* Add timer to timer list */
+static void langwell_otg_add_timer(void *gtimer)
+{
+       struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer;
+       struct langwell_otg_timer *tmp_timer;
+       u32     val32;
+
+       /* Check if the timer is already in the active list,
+        * if so update timer count
+        */
+       list_for_each_entry(tmp_timer, &active_timers, list)
+               if (tmp_timer == timer) {
+                       timer->count = timer->expires;
+                       return;
+               }
+       timer->count = timer->expires;
+
+       if (list_empty(&active_timers)) {
+               val32 = readl(the_transceiver->regs + CI_OTGSC);
+               writel(val32 | OTGSC_1MSE, the_transceiver->regs + CI_OTGSC);
+       }
+
+       list_add_tail(&timer->list, &active_timers);
+}
+
+/* Remove timer from the timer list; clear timeout status */
+static void langwell_otg_del_timer(void *gtimer)
+{
+       struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer;
+       struct langwell_otg_timer *tmp_timer, *del_tmp;
+       u32 val32;
+
+       list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list)
+               if (tmp_timer == timer)
+                       list_del(&timer->list);
+
+       if (list_empty(&active_timers)) {
+               val32 = readl(the_transceiver->regs + CI_OTGSC);
+               writel(val32 & ~OTGSC_1MSE, the_transceiver->regs + CI_OTGSC);
+       }
+}
+
+/* Reduce timer count by 1, and find timeout conditions.*/
+static int langwell_otg_tick_timer(u32 *int_sts)
+{
+       struct langwell_otg_timer *tmp_timer, *del_tmp;
+       int expired = 0;
+
+       list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
+               tmp_timer->count--;
+               /* check if timer expires */
+               if (!tmp_timer->count) {
+                       list_del(&tmp_timer->list);
+                       tmp_timer->function(tmp_timer->data);
+                       expired = 1;
+               }
+       }
+
+       if (list_empty(&active_timers)) {
+               otg_dbg("tick timer: disable 1ms int\n");
+               *int_sts = *int_sts & ~OTGSC_1MSE;
+       }
+       return expired;
+}
+
+static void reset_otg(void)
+{
+       u32     val;
+       int     delay_time = 1000;
+
+       otg_dbg("reseting OTG controller ...\n");
+       val = readl(the_transceiver->regs + CI_USBCMD);
+       writel(val | USBCMD_RST, the_transceiver->regs + CI_USBCMD);
+       do {
+               udelay(100);
+               if (!delay_time--)
+                       otg_dbg("reset timeout\n");
+               val = readl(the_transceiver->regs + CI_USBCMD);
+               val &= USBCMD_RST;
+       } while (val != 0);
+       otg_dbg("reset done.\n");
+}
+
+static void set_host_mode(void)
+{
+       u32     val;
+
+       reset_otg();
+       val = readl(the_transceiver->regs + CI_USBMODE);
+       val = (val & (~USBMODE_CM)) | USBMODE_HOST;
+       writel(val, the_transceiver->regs + CI_USBMODE);
+}
+
+static void set_client_mode(void)
+{
+       u32     val;
+
+       reset_otg();
+       val = readl(the_transceiver->regs + CI_USBMODE);
+       val = (val & (~USBMODE_CM)) | USBMODE_DEVICE;
+       writel(val, the_transceiver->regs + CI_USBMODE);
+}
+
+static void init_hsm(void)
+{
+       struct langwell_otg     *langwell = the_transceiver;
+       u32                     val32;
+
+       /* read OTGSC after reset */
+       val32 = readl(langwell->regs + CI_OTGSC);
+       otg_dbg("%s: OTGSC init value = 0x%x\n", __func__, val32);
+
+       /* set init state */
+       if (val32 & OTGSC_ID) {
+               langwell->hsm.id = 1;
+               langwell->otg.default_a = 0;
+               set_client_mode();
+               langwell->otg.state = OTG_STATE_B_IDLE;
+               langwell_otg_drv_vbus(0);
+       } else {
+               langwell->hsm.id = 0;
+               langwell->otg.default_a = 1;
+               set_host_mode();
+               langwell->otg.state = OTG_STATE_A_IDLE;
+       }
+
+       /* set session indicator */
+       if (val32 & OTGSC_BSE)
+               langwell->hsm.b_sess_end = 1;
+       if (val32 & OTGSC_BSV)
+               langwell->hsm.b_sess_vld = 1;
+       if (val32 & OTGSC_ASV)
+               langwell->hsm.a_sess_vld = 1;
+       if (val32 & OTGSC_AVV)
+               langwell->hsm.a_vbus_vld = 1;
+
+       /* defautly power the bus */
+       langwell->hsm.a_bus_req = 1;
+       langwell->hsm.a_bus_drop = 0;
+       /* defautly don't request bus as B device */
+       langwell->hsm.b_bus_req = 0;
+       /* no system error */
+       langwell->hsm.a_clr_err = 0;
+}
+
+static irqreturn_t otg_dummy_irq(int irq, void *_dev)
+{
+       void __iomem    *reg_base = _dev;
+       u32     val;
+       u32     int_mask = 0;
+
+       val = readl(reg_base + CI_USBMODE);
+       if ((val & USBMODE_CM) != USBMODE_DEVICE)
+               return IRQ_NONE;
+
+       val = readl(reg_base + CI_USBSTS);
+       int_mask = val & INTR_DUMMY_MASK;
+
+       if (int_mask == 0)
+               return IRQ_NONE;
+
+       /* clear hsm.b_conn here since host driver can't detect it
+       *  otg_dummy_irq called means B-disconnect happened.
+       */
+       if (the_transceiver->hsm.b_conn) {
+               the_transceiver->hsm.b_conn = 0;
+               if (spin_trylock(&the_transceiver->wq_lock)) {
+                       queue_work(the_transceiver->qwork,
+                               &the_transceiver->work);
+                       spin_unlock(&the_transceiver->wq_lock);
+               }
+       }
+       /* Clear interrupts */
+       writel(int_mask, reg_base + CI_USBSTS);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t otg_irq(int irq, void *_dev)
+{
+       struct  langwell_otg *langwell = _dev;
+       u32     int_sts, int_en;
+       u32     int_mask = 0;
+       int     flag = 0;
+
+       int_sts = readl(langwell->regs + CI_OTGSC);
+       int_en = (int_sts & OTGSC_INTEN_MASK) >> 8;
+       int_mask = int_sts & int_en;
+       if (int_mask == 0)
+               return IRQ_NONE;
+
+       if (int_mask & OTGSC_IDIS) {
+               otg_dbg("%s: id change int\n", __func__);
+               langwell->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0;
+               flag = 1;
+       }
+       if (int_mask & OTGSC_DPIS) {
+               otg_dbg("%s: data pulse int\n", __func__);
+               langwell->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0;
+               flag = 1;
+       }
+       if (int_mask & OTGSC_BSEIS) {
+               otg_dbg("%s: b session end int\n", __func__);
+               langwell->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0;
+               flag = 1;
+       }
+       if (int_mask & OTGSC_BSVIS) {
+               otg_dbg("%s: b session valid int\n", __func__);
+               langwell->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0;
+               flag = 1;
+       }
+       if (int_mask & OTGSC_ASVIS) {
+               otg_dbg("%s: a session valid int\n", __func__);
+               langwell->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0;
+               flag = 1;
+       }
+       if (int_mask & OTGSC_AVVIS) {
+               otg_dbg("%s: a vbus valid int\n", __func__);
+               langwell->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0;
+               flag = 1;
+       }
+
+       if (int_mask & OTGSC_1MSS) {
+               /* need to schedule otg_work if any timer is expired */
+               if (langwell_otg_tick_timer(&int_sts))
+                       flag = 1;
+       }
+
+       writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask,
+                       langwell->regs + CI_OTGSC);
+       if (flag)
+               queue_work(langwell->qwork, &langwell->work);
+
+       return IRQ_HANDLED;
+}
+
+static void langwell_otg_work(struct work_struct *work)
+{
+       struct langwell_otg *langwell = container_of(work,
+                                       struct langwell_otg, work);
+       int     retval;
+
+       otg_dbg("%s: old state = %s\n", __func__,
+                       state_string(langwell->otg.state));
+
+       switch (langwell->otg.state) {
+       case OTG_STATE_UNDEFINED:
+       case OTG_STATE_B_IDLE:
+               if (!langwell->hsm.id) {
+                       langwell_otg_del_timer(b_srp_res_tmr);
+                       langwell->otg.default_a = 1;
+                       langwell->hsm.a_srp_det = 0;
+
+                       langwell_otg_chrg_vbus(0);
+                       langwell_otg_drv_vbus(0);
+
+                       set_host_mode();
+                       langwell->otg.state = OTG_STATE_A_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (langwell->hsm.b_srp_res_tmout) {
+                       langwell->hsm.b_srp_res_tmout = 0;
+                       langwell->hsm.b_bus_req = 0;
+                       langwell_otg_nsf_msg(6);
+               } else if (langwell->hsm.b_sess_vld) {
+                       langwell_otg_del_timer(b_srp_res_tmr);
+                       langwell->hsm.b_sess_end = 0;
+                       langwell->hsm.a_bus_suspend = 0;
+
+                       langwell_otg_chrg_vbus(0);
+                       if (langwell->client_ops) {
+                               langwell->client_ops->resume(langwell->pdev);
+                               langwell->otg.state = OTG_STATE_B_PERIPHERAL;
+                       } else
+                               otg_dbg("client driver not loaded.\n");
+
+               } else if (langwell->hsm.b_bus_req &&
+                               (langwell->hsm.b_sess_end)) {
+                       /* workaround for b_se0_srp detection */
+                       retval = langwell_otg_check_se0_srp(0);
+                       if (retval) {
+                               langwell->hsm.b_bus_req = 0;
+                               otg_dbg("LS is not SE0, try again later\n");
+                       } else {
+                               /* Start SRP */
+                               langwell_otg_start_srp(&langwell->otg);
+                               langwell_otg_add_timer(b_srp_res_tmr);
+                       }
+               }
+               break;
+       case OTG_STATE_B_SRP_INIT:
+               if (!langwell->hsm.id) {
+                       langwell->otg.default_a = 1;
+                       langwell->hsm.a_srp_det = 0;
+
+                       langwell_otg_drv_vbus(0);
+                       langwell_otg_chrg_vbus(0);
+
+                       langwell->otg.state = OTG_STATE_A_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (langwell->hsm.b_sess_vld) {
+                       langwell_otg_chrg_vbus(0);
+                       if (langwell->client_ops) {
+                               langwell->client_ops->resume(langwell->pdev);
+                               langwell->otg.state = OTG_STATE_B_PERIPHERAL;
+                       } else
+                               otg_dbg("client driver not loaded.\n");
+               }
+               break;
+       case OTG_STATE_B_PERIPHERAL:
+               if (!langwell->hsm.id) {
+                       langwell->otg.default_a = 1;
+                       langwell->hsm.a_srp_det = 0;
+
+                       langwell_otg_drv_vbus(0);
+                       langwell_otg_chrg_vbus(0);
+                       set_host_mode();
+
+                       if (langwell->client_ops) {
+                               langwell->client_ops->suspend(langwell->pdev,
+                                       PMSG_FREEZE);
+                       } else
+                               otg_dbg("client driver has been removed.\n");
+
+                       langwell->otg.state = OTG_STATE_A_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (!langwell->hsm.b_sess_vld) {
+                       langwell->hsm.b_hnp_enable = 0;
+
+                       if (langwell->client_ops) {
+                               langwell->client_ops->suspend(langwell->pdev,
+                                       PMSG_FREEZE);
+                       } else
+                               otg_dbg("client driver has been removed.\n");
+
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+               } else if (langwell->hsm.b_bus_req && langwell->hsm.b_hnp_enable
+                       && langwell->hsm.a_bus_suspend) {
+
+                       if (langwell->client_ops) {
+                               langwell->client_ops->suspend(langwell->pdev,
+                                       PMSG_FREEZE);
+                       } else
+                               otg_dbg("client driver has been removed.\n");
+
+                       langwell_otg_HAAR(1);
+                       langwell->hsm.a_conn = 0;
+
+                       if (langwell->host_ops) {
+                               langwell->host_ops->probe(langwell->pdev,
+                                       langwell->host_ops->id_table);
+                               langwell->otg.state = OTG_STATE_B_WAIT_ACON;
+                       } else
+                               otg_dbg("host driver not loaded.\n");
+
+                       langwell->hsm.a_bus_resume = 0;
+                       langwell->hsm.b_ase0_brst_tmout = 0;
+                       langwell_otg_add_timer(b_ase0_brst_tmr);
+               }
+               break;
+
+       case OTG_STATE_B_WAIT_ACON:
+               if (!langwell->hsm.id) {
+                       langwell_otg_del_timer(b_ase0_brst_tmr);
+                       langwell->otg.default_a = 1;
+                       langwell->hsm.a_srp_det = 0;
+
+                       langwell_otg_drv_vbus(0);
+                       langwell_otg_chrg_vbus(0);
+                       set_host_mode();
+
+                       langwell_otg_HAAR(0);
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell->otg.state = OTG_STATE_A_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (!langwell->hsm.b_sess_vld) {
+                       langwell_otg_del_timer(b_ase0_brst_tmr);
+                       langwell->hsm.b_hnp_enable = 0;
+                       langwell->hsm.b_bus_req = 0;
+                       langwell_otg_chrg_vbus(0);
+                       langwell_otg_HAAR(0);
+
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+               } else if (langwell->hsm.a_conn) {
+                       langwell_otg_del_timer(b_ase0_brst_tmr);
+                       langwell_otg_HAAR(0);
+                       langwell->otg.state = OTG_STATE_B_HOST;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (langwell->hsm.a_bus_resume ||
+                               langwell->hsm.b_ase0_brst_tmout) {
+                       langwell_otg_del_timer(b_ase0_brst_tmr);
+                       langwell_otg_HAAR(0);
+                       langwell_otg_nsf_msg(7);
+
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+
+                       langwell->hsm.a_bus_suspend = 0;
+                       langwell->hsm.b_bus_req = 0;
+
+                       if (langwell->client_ops)
+                               langwell->client_ops->resume(langwell->pdev);
+                       else
+                               otg_dbg("client driver not loaded.\n");
+
+                       langwell->otg.state = OTG_STATE_B_PERIPHERAL;
+               }
+               break;
+
+       case OTG_STATE_B_HOST:
+               if (!langwell->hsm.id) {
+                       langwell->otg.default_a = 1;
+                       langwell->hsm.a_srp_det = 0;
+
+                       langwell_otg_drv_vbus(0);
+                       langwell_otg_chrg_vbus(0);
+                       set_host_mode();
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell->otg.state = OTG_STATE_A_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (!langwell->hsm.b_sess_vld) {
+                       langwell->hsm.b_hnp_enable = 0;
+                       langwell->hsm.b_bus_req = 0;
+                       langwell_otg_chrg_vbus(0);
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+               } else if ((!langwell->hsm.b_bus_req) ||
+                               (!langwell->hsm.a_conn)) {
+                       langwell->hsm.b_bus_req = 0;
+                       langwell_otg_loc_sof(0);
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+
+                       langwell->hsm.a_bus_suspend = 0;
+
+                       if (langwell->client_ops)
+                               langwell->client_ops->resume(langwell->pdev);
+                       else
+                               otg_dbg("client driver not loaded.\n");
+
+                       langwell->otg.state = OTG_STATE_B_PERIPHERAL;
+               }
+               break;
+
+       case OTG_STATE_A_IDLE:
+               langwell->otg.default_a = 1;
+               if (langwell->hsm.id) {
+                       langwell->otg.default_a = 0;
+                       langwell->hsm.b_bus_req = 0;
+                       langwell_otg_drv_vbus(0);
+                       langwell_otg_chrg_vbus(0);
+
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (langwell->hsm.a_sess_vld) {
+                       langwell_otg_drv_vbus(1);
+                       langwell->hsm.a_srp_det = 1;
+                       langwell->hsm.a_wait_vrise_tmout = 0;
+                       langwell_otg_add_timer(a_wait_vrise_tmr);
+                       langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (!langwell->hsm.a_bus_drop &&
+                       (langwell->hsm.a_srp_det || langwell->hsm.a_bus_req)) {
+                       langwell_otg_drv_vbus(1);
+                       langwell->hsm.a_wait_vrise_tmout = 0;
+                       langwell_otg_add_timer(a_wait_vrise_tmr);
+                       langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
+                       queue_work(langwell->qwork, &langwell->work);
+               }
+               break;
+       case OTG_STATE_A_WAIT_VRISE:
+               if (langwell->hsm.id) {
+                       langwell_otg_del_timer(a_wait_vrise_tmr);
+                       langwell->hsm.b_bus_req = 0;
+                       langwell->otg.default_a = 0;
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+               } else if (langwell->hsm.a_vbus_vld) {
+                       langwell_otg_del_timer(a_wait_vrise_tmr);
+                       if (langwell->host_ops)
+                               langwell->host_ops->probe(langwell->pdev,
+                                               langwell->host_ops->id_table);
+                       else
+                               otg_dbg("host driver not loaded.\n");
+                       langwell->hsm.b_conn = 0;
+                       langwell->hsm.a_set_b_hnp_en = 0;
+                       langwell->hsm.a_wait_bcon_tmout = 0;
+                       langwell_otg_add_timer(a_wait_bcon_tmr);
+                       langwell->otg.state = OTG_STATE_A_WAIT_BCON;
+               } else if (langwell->hsm.a_wait_vrise_tmout) {
+                       if (langwell->hsm.a_vbus_vld) {
+                               if (langwell->host_ops)
+                                       langwell->host_ops->probe(
+                                               langwell->pdev,
+                                               langwell->host_ops->id_table);
+                               else
+                                       otg_dbg("host driver not loaded.\n");
+                               langwell->hsm.b_conn = 0;
+                               langwell->hsm.a_set_b_hnp_en = 0;
+                               langwell->hsm.a_wait_bcon_tmout = 0;
+                               langwell_otg_add_timer(a_wait_bcon_tmr);
+                               langwell->otg.state = OTG_STATE_A_WAIT_BCON;
+                       } else {
+                               langwell_otg_drv_vbus(0);
+                               langwell->otg.state = OTG_STATE_A_VBUS_ERR;
+                       }
+               }
+               break;
+       case OTG_STATE_A_WAIT_BCON:
+               if (langwell->hsm.id) {
+                       langwell_otg_del_timer(a_wait_bcon_tmr);
+
+                       langwell->otg.default_a = 0;
+                       langwell->hsm.b_bus_req = 0;
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (!langwell->hsm.a_vbus_vld) {
+                       langwell_otg_del_timer(a_wait_bcon_tmr);
+
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_A_VBUS_ERR;
+               } else if (langwell->hsm.a_bus_drop ||
+                               (langwell->hsm.a_wait_bcon_tmout &&
+                               !langwell->hsm.a_bus_req)) {
+                       langwell_otg_del_timer(a_wait_bcon_tmr);
+
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
+               } else if (langwell->hsm.b_conn) {
+                       langwell_otg_del_timer(a_wait_bcon_tmr);
+
+                       langwell->hsm.a_suspend_req = 0;
+                       langwell->otg.state = OTG_STATE_A_HOST;
+                       if (!langwell->hsm.a_bus_req &&
+                               langwell->hsm.a_set_b_hnp_en) {
+                               /* It is not safe enough to do a fast
+                                * transistion from A_WAIT_BCON to
+                                * A_SUSPEND */
+                               msleep(10000);
+                               if (langwell->hsm.a_bus_req)
+                                       break;
+
+                               if (request_irq(langwell->pdev->irq,
+                                       otg_dummy_irq, IRQF_SHARED,
+                                       driver_name, langwell->regs) != 0) {
+                                       otg_dbg("request interrupt %d fail\n",
+                                       langwell->pdev->irq);
+                               }
+
+                               langwell_otg_HABA(1);
+                               langwell->hsm.b_bus_resume = 0;
+                               langwell->hsm.a_aidl_bdis_tmout = 0;
+                               langwell_otg_add_timer(a_aidl_bdis_tmr);
+
+                               langwell_otg_loc_sof(0);
+                               langwell->otg.state = OTG_STATE_A_SUSPEND;
+                       } else if (!langwell->hsm.a_bus_req &&
+                               !langwell->hsm.a_set_b_hnp_en) {
+                               struct pci_dev *pdev = langwell->pdev;
+                               if (langwell->host_ops)
+                                       langwell->host_ops->remove(pdev);
+                               else
+                                       otg_dbg("host driver removed.\n");
+                               langwell_otg_drv_vbus(0);
+                               langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
+                       }
+               }
+               break;
+       case OTG_STATE_A_HOST:
+               if (langwell->hsm.id) {
+                       langwell->otg.default_a = 0;
+                       langwell->hsm.b_bus_req = 0;
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (langwell->hsm.a_bus_drop ||
+               (!langwell->hsm.a_set_b_hnp_en && !langwell->hsm.a_bus_req)) {
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
+               } else if (!langwell->hsm.a_vbus_vld) {
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_A_VBUS_ERR;
+               } else if (langwell->hsm.a_set_b_hnp_en
+                               && !langwell->hsm.a_bus_req) {
+                       /* Set HABA to enable hardware assistance to signal
+                        *  A-connect after receiver B-disconnect. Hardware
+                        *  will then set client mode and enable URE, SLE and
+                        *  PCE after the assistance. otg_dummy_irq is used to
+                        *  clean these ints when client driver is not resumed.
+                        */
+                       if (request_irq(langwell->pdev->irq,
+                               otg_dummy_irq, IRQF_SHARED, driver_name,
+                               langwell->regs) != 0) {
+                               otg_dbg("request interrupt %d failed\n",
+                                               langwell->pdev->irq);
+                       }
+
+                       /* set HABA */
+                       langwell_otg_HABA(1);
+                       langwell->hsm.b_bus_resume = 0;
+                       langwell->hsm.a_aidl_bdis_tmout = 0;
+                       langwell_otg_add_timer(a_aidl_bdis_tmr);
+                       langwell_otg_loc_sof(0);
+                       langwell->otg.state = OTG_STATE_A_SUSPEND;
+               } else if (!langwell->hsm.b_conn || !langwell->hsm.a_bus_req) {
+                       langwell->hsm.a_wait_bcon_tmout = 0;
+                       langwell->hsm.a_set_b_hnp_en = 0;
+                       langwell_otg_add_timer(a_wait_bcon_tmr);
+                       langwell->otg.state = OTG_STATE_A_WAIT_BCON;
+               }
+               break;
+       case OTG_STATE_A_SUSPEND:
+               if (langwell->hsm.id) {
+                       langwell_otg_del_timer(a_aidl_bdis_tmr);
+                       langwell_otg_HABA(0);
+                       free_irq(langwell->pdev->irq, langwell->regs);
+                       langwell->otg.default_a = 0;
+                       langwell->hsm.b_bus_req = 0;
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (langwell->hsm.a_bus_req ||
+                               langwell->hsm.b_bus_resume) {
+                       langwell_otg_del_timer(a_aidl_bdis_tmr);
+                       langwell_otg_HABA(0);
+                       free_irq(langwell->pdev->irq, langwell->regs);
+                       langwell->hsm.a_suspend_req = 0;
+                       langwell_otg_loc_sof(1);
+                       langwell->otg.state = OTG_STATE_A_HOST;
+               } else if (langwell->hsm.a_aidl_bdis_tmout ||
+                               langwell->hsm.a_bus_drop) {
+                       langwell_otg_del_timer(a_aidl_bdis_tmr);
+                       langwell_otg_HABA(0);
+                       free_irq(langwell->pdev->irq, langwell->regs);
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
+               } else if (!langwell->hsm.b_conn &&
+                               langwell->hsm.a_set_b_hnp_en) {
+                       langwell_otg_del_timer(a_aidl_bdis_tmr);
+                       langwell_otg_HABA(0);
+                       free_irq(langwell->pdev->irq, langwell->regs);
+
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+
+                       langwell->hsm.b_bus_suspend = 0;
+                       langwell->hsm.b_bus_suspend_vld = 0;
+                       langwell->hsm.b_bus_suspend_tmout = 0;
+
+                       /* msleep(200); */
+                       if (langwell->client_ops)
+                               langwell->client_ops->resume(langwell->pdev);
+                       else
+                               otg_dbg("client driver not loaded.\n");
+
+                       langwell_otg_add_timer(b_bus_suspend_tmr);
+                       langwell->otg.state = OTG_STATE_A_PERIPHERAL;
+                       break;
+               } else if (!langwell->hsm.a_vbus_vld) {
+                       langwell_otg_del_timer(a_aidl_bdis_tmr);
+                       langwell_otg_HABA(0);
+                       free_irq(langwell->pdev->irq, langwell->regs);
+                       if (langwell->host_ops)
+                               langwell->host_ops->remove(langwell->pdev);
+                       else
+                               otg_dbg("host driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_A_VBUS_ERR;
+               }
+               break;
+       case OTG_STATE_A_PERIPHERAL:
+               if (langwell->hsm.id) {
+                       langwell_otg_del_timer(b_bus_suspend_tmr);
+                       langwell->otg.default_a = 0;
+                       langwell->hsm.b_bus_req = 0;
+                       if (langwell->client_ops)
+                               langwell->client_ops->suspend(langwell->pdev,
+                                       PMSG_FREEZE);
+                       else
+                               otg_dbg("client driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (!langwell->hsm.a_vbus_vld) {
+                       langwell_otg_del_timer(b_bus_suspend_tmr);
+                       if (langwell->client_ops)
+                               langwell->client_ops->suspend(langwell->pdev,
+                                       PMSG_FREEZE);
+                       else
+                               otg_dbg("client driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_A_VBUS_ERR;
+               } else if (langwell->hsm.a_bus_drop) {
+                       langwell_otg_del_timer(b_bus_suspend_tmr);
+                       if (langwell->client_ops)
+                               langwell->client_ops->suspend(langwell->pdev,
+                                       PMSG_FREEZE);
+                       else
+                               otg_dbg("client driver has been removed.\n");
+                       langwell_otg_drv_vbus(0);
+                       langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
+               } else if (langwell->hsm.b_bus_suspend) {
+                       langwell_otg_del_timer(b_bus_suspend_tmr);
+                       if (langwell->client_ops)
+                               langwell->client_ops->suspend(langwell->pdev,
+                                       PMSG_FREEZE);
+                       else
+                               otg_dbg("client driver has been removed.\n");
+
+                       if (langwell->host_ops)
+                               langwell->host_ops->probe(langwell->pdev,
+                                               langwell->host_ops->id_table);
+                       else
+                               otg_dbg("host driver not loaded.\n");
+                       langwell->hsm.a_set_b_hnp_en = 0;
+                       langwell->hsm.a_wait_bcon_tmout = 0;
+                       langwell_otg_add_timer(a_wait_bcon_tmr);
+                       langwell->otg.state = OTG_STATE_A_WAIT_BCON;
+               } else if (langwell->hsm.b_bus_suspend_tmout) {
+                       u32     val;
+                       val = readl(langwell->regs + CI_PORTSC1);
+                       if (!(val & PORTSC_SUSP))
+                               break;
+                       if (langwell->client_ops)
+                               langwell->client_ops->suspend(langwell->pdev,
+                                               PMSG_FREEZE);
+                       else
+                               otg_dbg("client driver has been removed.\n");
+                       if (langwell->host_ops)
+                               langwell->host_ops->probe(langwell->pdev,
+                                               langwell->host_ops->id_table);
+                       else
+                               otg_dbg("host driver not loaded.\n");
+                       langwell->hsm.a_set_b_hnp_en = 0;
+                       langwell->hsm.a_wait_bcon_tmout = 0;
+                       langwell_otg_add_timer(a_wait_bcon_tmr);
+                       langwell->otg.state = OTG_STATE_A_WAIT_BCON;
+               }
+               break;
+       case OTG_STATE_A_VBUS_ERR:
+               if (langwell->hsm.id) {
+                       langwell->otg.default_a = 0;
+                       langwell->hsm.a_clr_err = 0;
+                       langwell->hsm.a_srp_det = 0;
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (langwell->hsm.a_clr_err) {
+                       langwell->hsm.a_clr_err = 0;
+                       langwell->hsm.a_srp_det = 0;
+                       reset_otg();
+                       init_hsm();
+                       if (langwell->otg.state == OTG_STATE_A_IDLE)
+                               queue_work(langwell->qwork, &langwell->work);
+               }
+               break;
+       case OTG_STATE_A_WAIT_VFALL:
+               if (langwell->hsm.id) {
+                       langwell->otg.default_a = 0;
+                       langwell->otg.state = OTG_STATE_B_IDLE;
+                       queue_work(langwell->qwork, &langwell->work);
+               } else if (langwell->hsm.a_bus_req) {
+                       langwell_otg_drv_vbus(1);
+                       langwell->hsm.a_wait_vrise_tmout = 0;
+                       langwell_otg_add_timer(a_wait_vrise_tmr);
+                       langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
+               } else if (!langwell->hsm.a_sess_vld) {
+                       langwell->hsm.a_srp_det = 0;
+                       langwell_otg_drv_vbus(0);
+                       set_host_mode();
+                       langwell->otg.state = OTG_STATE_A_IDLE;
+               }
+               break;
+       default:
+               ;
+       }
+
+       otg_dbg("%s: new state = %s\n", __func__,
+                       state_string(langwell->otg.state));
+}
+
+       static ssize_t
+show_registers(struct device *_dev, struct device_attribute *attr, char *buf)
+{
+       struct langwell_otg *langwell;
+       char *next;
+       unsigned size;
+       unsigned t;
+
+       langwell = the_transceiver;
+       next = buf;
+       size = PAGE_SIZE;
+
+       t = scnprintf(next, size,
+               "\n"
+               "USBCMD = 0x%08x \n"
+               "USBSTS = 0x%08x \n"
+               "USBINTR = 0x%08x \n"
+               "ASYNCLISTADDR = 0x%08x \n"
+               "PORTSC1 = 0x%08x \n"
+               "HOSTPC1 = 0x%08x \n"
+               "OTGSC = 0x%08x \n"
+               "USBMODE = 0x%08x \n",
+               readl(langwell->regs + 0x30),
+               readl(langwell->regs + 0x34),
+               readl(langwell->regs + 0x38),
+               readl(langwell->regs + 0x48),
+               readl(langwell->regs + 0x74),
+               readl(langwell->regs + 0xb4),
+               readl(langwell->regs + 0xf4),
+               readl(langwell->regs + 0xf8)
+               );
+       size -= t;
+       next += t;
+
+       return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
+
+static ssize_t
+show_hsm(struct device *_dev, struct device_attribute *attr, char *buf)
+{
+       struct langwell_otg *langwell;
+       char *next;
+       unsigned size;
+       unsigned t;
+
+       langwell = the_transceiver;
+       next = buf;
+       size = PAGE_SIZE;
+
+       t = scnprintf(next, size,
+               "\n"
+               "current state = %s\n"
+               "a_bus_resume = \t%d\n"
+               "a_bus_suspend = \t%d\n"
+               "a_conn = \t%d\n"
+               "a_sess_vld = \t%d\n"
+               "a_srp_det = \t%d\n"
+               "a_vbus_vld = \t%d\n"
+               "b_bus_resume = \t%d\n"
+               "b_bus_suspend = \t%d\n"
+               "b_conn = \t%d\n"
+               "b_se0_srp = \t%d\n"
+               "b_sess_end = \t%d\n"
+               "b_sess_vld = \t%d\n"
+               "id = \t%d\n"
+               "a_set_b_hnp_en = \t%d\n"
+               "b_srp_done = \t%d\n"
+               "b_hnp_enable = \t%d\n"
+               "a_wait_vrise_tmout = \t%d\n"
+               "a_wait_bcon_tmout = \t%d\n"
+               "a_aidl_bdis_tmout = \t%d\n"
+               "b_ase0_brst_tmout = \t%d\n"
+               "a_bus_drop = \t%d\n"
+               "a_bus_req = \t%d\n"
+               "a_clr_err = \t%d\n"
+               "a_suspend_req = \t%d\n"
+               "b_bus_req = \t%d\n"
+               "b_bus_suspend_tmout = \t%d\n"
+               "b_bus_suspend_vld = \t%d\n",
+               state_string(langwell->otg.state),
+               langwell->hsm.a_bus_resume,
+               langwell->hsm.a_bus_suspend,
+               langwell->hsm.a_conn,
+               langwell->hsm.a_sess_vld,
+               langwell->hsm.a_srp_det,
+               langwell->hsm.a_vbus_vld,
+               langwell->hsm.b_bus_resume,
+               langwell->hsm.b_bus_suspend,
+               langwell->hsm.b_conn,
+               langwell->hsm.b_se0_srp,
+               langwell->hsm.b_sess_end,
+               langwell->hsm.b_sess_vld,
+               langwell->hsm.id,
+               langwell->hsm.a_set_b_hnp_en,
+               langwell->hsm.b_srp_done,
+               langwell->hsm.b_hnp_enable,
+               langwell->hsm.a_wait_vrise_tmout,
+               langwell->hsm.a_wait_bcon_tmout,
+               langwell->hsm.a_aidl_bdis_tmout,
+               langwell->hsm.b_ase0_brst_tmout,
+               langwell->hsm.a_bus_drop,
+               langwell->hsm.a_bus_req,
+               langwell->hsm.a_clr_err,
+               langwell->hsm.a_suspend_req,
+               langwell->hsm.b_bus_req,
+               langwell->hsm.b_bus_suspend_tmout,
+               langwell->hsm.b_bus_suspend_vld
+               );
+       size -= t;
+       next += t;
+
+       return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL);
+
+static ssize_t
+get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct langwell_otg *langwell;
+       char *next;
+       unsigned size;
+       unsigned t;
+
+       langwell =  the_transceiver;
+       next = buf;
+       size = PAGE_SIZE;
+
+       t = scnprintf(next, size, "%d", langwell->hsm.a_bus_req);
+       size -= t;
+       next += t;
+
+       return PAGE_SIZE - size;
+}
+
+static ssize_t
+set_a_bus_req(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct langwell_otg *langwell;
+       langwell = the_transceiver;
+       if (!langwell->otg.default_a)
+               return -1;
+       if (count > 2)
+               return -1;
+
+       if (buf[0] == '0') {
+               langwell->hsm.a_bus_req = 0;
+               otg_dbg("a_bus_req = 0\n");
+       } else if (buf[0] == '1') {
+               /* If a_bus_drop is TRUE, a_bus_req can't be set */
+               if (langwell->hsm.a_bus_drop)
+                       return -1;
+               langwell->hsm.a_bus_req = 1;
+               otg_dbg("a_bus_req = 1\n");
+       }
+       if (spin_trylock(&langwell->wq_lock)) {
+               queue_work(langwell->qwork, &langwell->work);
+               spin_unlock(&langwell->wq_lock);
+       }
+       return count;
+}
+static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req);
+
+static ssize_t
+get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct langwell_otg *langwell;
+       char *next;
+       unsigned size;
+       unsigned t;
+
+       langwell =  the_transceiver;
+       next = buf;
+       size = PAGE_SIZE;
+
+       t = scnprintf(next, size, "%d", langwell->hsm.a_bus_drop);
+       size -= t;
+       next += t;
+
+       return PAGE_SIZE - size;
+}
+
+static ssize_t
+set_a_bus_drop(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct langwell_otg *langwell;
+       langwell = the_transceiver;
+       if (!langwell->otg.default_a)
+               return -1;
+       if (count > 2)
+               return -1;
+
+       if (buf[0] == '0') {
+               langwell->hsm.a_bus_drop = 0;
+               otg_dbg("a_bus_drop = 0\n");
+       } else if (buf[0] == '1') {
+               langwell->hsm.a_bus_drop = 1;
+               langwell->hsm.a_bus_req = 0;
+               otg_dbg("a_bus_drop = 1, then a_bus_req = 0\n");
+       }
+       if (spin_trylock(&langwell->wq_lock)) {
+               queue_work(langwell->qwork, &langwell->work);
+               spin_unlock(&langwell->wq_lock);
+       }
+       return count;
+}
+static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO,
+       get_a_bus_drop, set_a_bus_drop);
+
+static ssize_t
+get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct langwell_otg *langwell;
+       char *next;
+       unsigned size;
+       unsigned t;
+
+       langwell =  the_transceiver;
+       next = buf;
+       size = PAGE_SIZE;
+
+       t = scnprintf(next, size, "%d", langwell->hsm.b_bus_req);
+       size -= t;
+       next += t;
+
+       return PAGE_SIZE - size;
+}
+
+static ssize_t
+set_b_bus_req(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct langwell_otg *langwell;
+       langwell = the_transceiver;
+
+       if (langwell->otg.default_a)
+               return -1;
+
+       if (count > 2)
+               return -1;
+
+       if (buf[0] == '0') {
+               langwell->hsm.b_bus_req = 0;
+               otg_dbg("b_bus_req = 0\n");
+       } else if (buf[0] == '1') {
+               langwell->hsm.b_bus_req = 1;
+               otg_dbg("b_bus_req = 1\n");
+       }
+       if (spin_trylock(&langwell->wq_lock)) {
+               queue_work(langwell->qwork, &langwell->work);
+               spin_unlock(&langwell->wq_lock);
+       }
+       return count;
+}
+static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req);
+
+static ssize_t
+set_a_clr_err(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct langwell_otg *langwell;
+       langwell = the_transceiver;
+
+       if (!langwell->otg.default_a)
+               return -1;
+       if (count > 2)
+               return -1;
+
+       if (buf[0] == '1') {
+               langwell->hsm.a_clr_err = 1;
+               otg_dbg("a_clr_err = 1\n");
+       }
+       if (spin_trylock(&langwell->wq_lock)) {
+               queue_work(langwell->qwork, &langwell->work);
+               spin_unlock(&langwell->wq_lock);
+       }
+       return count;
+}
+static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err);
+
+static struct attribute *inputs_attrs[] = {
+       &dev_attr_a_bus_req.attr,
+       &dev_attr_a_bus_drop.attr,
+       &dev_attr_b_bus_req.attr,
+       &dev_attr_a_clr_err.attr,
+       NULL,
+};
+
+static struct attribute_group debug_dev_attr_group = {
+       .name = "inputs",
+       .attrs = inputs_attrs,
+};
+
+int langwell_register_host(struct pci_driver *host_driver)
+{
+       int     ret = 0;
+
+       the_transceiver->host_ops = host_driver;
+       queue_work(the_transceiver->qwork, &the_transceiver->work);
+       otg_dbg("host controller driver is registered\n");
+
+       return ret;
+}
+EXPORT_SYMBOL(langwell_register_host);
+
+void langwell_unregister_host(struct pci_driver *host_driver)
+{
+       if (the_transceiver->host_ops)
+               the_transceiver->host_ops->remove(the_transceiver->pdev);
+       the_transceiver->host_ops = NULL;
+       the_transceiver->hsm.a_bus_drop = 1;
+       queue_work(the_transceiver->qwork, &the_transceiver->work);
+       otg_dbg("host controller driver is unregistered\n");
+}
+EXPORT_SYMBOL(langwell_unregister_host);
+
+int langwell_register_peripheral(struct pci_driver *client_driver)
+{
+       int     ret = 0;
+
+       if (client_driver)
+               ret = client_driver->probe(the_transceiver->pdev,
+                               client_driver->id_table);
+       if (!ret) {
+               the_transceiver->client_ops = client_driver;
+               queue_work(the_transceiver->qwork, &the_transceiver->work);
+               otg_dbg("client controller driver is registered\n");
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(langwell_register_peripheral);
+
+void langwell_unregister_peripheral(struct pci_driver *client_driver)
+{
+       if (the_transceiver->client_ops)
+               the_transceiver->client_ops->remove(the_transceiver->pdev);
+       the_transceiver->client_ops = NULL;
+       the_transceiver->hsm.b_bus_req = 0;
+       queue_work(the_transceiver->qwork, &the_transceiver->work);
+       otg_dbg("client controller driver is unregistered\n");
+}
+EXPORT_SYMBOL(langwell_unregister_peripheral);
+
+static int langwell_otg_probe(struct pci_dev *pdev,
+               const struct pci_device_id *id)
+{
+       unsigned long           resource, len;
+       void __iomem            *base = NULL;
+       int                     retval;
+       u32                     val32;
+       struct langwell_otg     *langwell;
+       char                    qname[] = "langwell_otg_queue";
+
+       retval = 0;
+       otg_dbg("\notg controller is detected.\n");
+       if (pci_enable_device(pdev) < 0) {
+               retval = -ENODEV;
+               goto done;
+       }
+
+       langwell = kzalloc(sizeof *langwell, GFP_KERNEL);
+       if (langwell == NULL) {
+               retval = -ENOMEM;
+               goto done;
+       }
+       the_transceiver = langwell;
+
+       /* control register: BAR 0 */
+       resource = pci_resource_start(pdev, 0);
+       len = pci_resource_len(pdev, 0);
+       if (!request_mem_region(resource, len, driver_name)) {
+               retval = -EBUSY;
+               goto err;
+       }
+       langwell->region = 1;
+
+       base = ioremap_nocache(resource, len);
+       if (base == NULL) {
+               retval = -EFAULT;
+               goto err;
+       }
+       langwell->regs = base;
+
+       if (!pdev->irq) {
+               otg_dbg("No IRQ.\n");
+               retval = -ENODEV;
+               goto err;
+       }
+
+       langwell->qwork = create_workqueue(qname);
+       if (!langwell->qwork) {
+               otg_dbg("cannot create workqueue %s\n", qname);
+               retval = -ENOMEM;
+               goto err;
+       }
+       INIT_WORK(&langwell->work, langwell_otg_work);
+
+       /* OTG common part */
+       langwell->pdev = pdev;
+       langwell->otg.dev = &pdev->dev;
+       langwell->otg.label = driver_name;
+       langwell->otg.set_host = langwell_otg_set_host;
+       langwell->otg.set_peripheral = langwell_otg_set_peripheral;
+       langwell->otg.set_power = langwell_otg_set_power;
+       langwell->otg.start_srp = langwell_otg_start_srp;
+       langwell->otg.state = OTG_STATE_UNDEFINED;
+       if (otg_set_transceiver(&langwell->otg)) {
+               otg_dbg("can't set transceiver\n");
+               retval = -EBUSY;
+               goto err;
+       }
+
+       reset_otg();
+       init_hsm();
+
+       spin_lock_init(&langwell->lock);
+       spin_lock_init(&langwell->wq_lock);
+       INIT_LIST_HEAD(&active_timers);
+       langwell_otg_init_timers(&langwell->hsm);
+
+       if (request_irq(pdev->irq, otg_irq, IRQF_SHARED,
+                               driver_name, langwell) != 0) {
+               otg_dbg("request interrupt %d failed\n", pdev->irq);
+               retval = -EBUSY;
+               goto err;
+       }
+
+       /* enable OTGSC int */
+       val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE |
+               OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU;
+       writel(val32, langwell->regs + CI_OTGSC);
+
+       retval = device_create_file(&pdev->dev, &dev_attr_registers);
+       if (retval < 0) {
+               otg_dbg("Can't register sysfs attribute: %d\n", retval);
+               goto err;
+       }
+
+       retval = device_create_file(&pdev->dev, &dev_attr_hsm);
+       if (retval < 0) {
+               otg_dbg("Can't hsm sysfs attribute: %d\n", retval);
+               goto err;
+       }
+
+       retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group);
+       if (retval < 0) {
+               otg_dbg("Can't register sysfs attr group: %d\n", retval);
+               goto err;
+       }
+
+       if (langwell->otg.state == OTG_STATE_A_IDLE)
+               queue_work(langwell->qwork, &langwell->work);
+
+       return 0;
+
+err:
+       if (the_transceiver)
+               langwell_otg_remove(pdev);
+done:
+       return retval;
+}
+
+static void langwell_otg_remove(struct pci_dev *pdev)
+{
+       struct langwell_otg *langwell;
+
+       langwell = the_transceiver;
+
+       if (langwell->qwork) {
+               flush_workqueue(langwell->qwork);
+               destroy_workqueue(langwell->qwork);
+       }
+       langwell_otg_free_timers();
+
+       /* disable OTGSC interrupt as OTGSC doesn't change in reset */
+       writel(0, langwell->regs + CI_OTGSC);
+
+       if (pdev->irq)
+               free_irq(pdev->irq, langwell);
+       if (langwell->regs)
+               iounmap(langwell->regs);
+       if (langwell->region)
+               release_mem_region(pci_resource_start(pdev, 0),
+                               pci_resource_len(pdev, 0));
+
+       otg_set_transceiver(NULL);
+       pci_disable_device(pdev);
+       sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group);
+       device_remove_file(&pdev->dev, &dev_attr_hsm);
+       device_remove_file(&pdev->dev, &dev_attr_registers);
+       kfree(langwell);
+       langwell = NULL;
+}
+
+static void transceiver_suspend(struct pci_dev *pdev)
+{
+       pci_save_state(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+       langwell_otg_phy_low_power(1);
+}
+
+static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
+{
+       int     ret = 0;
+       struct langwell_otg *langwell;
+
+       langwell = the_transceiver;
+
+       /* Disbale OTG interrupts */
+       langwell_otg_intr(0);
+
+       if (pdev->irq)
+               free_irq(pdev->irq, langwell);
+
+       /* Prevent more otg_work */
+       flush_workqueue(langwell->qwork);
+       spin_lock(&langwell->wq_lock);
+
+       /* start actions */
+       switch (langwell->otg.state) {
+       case OTG_STATE_A_IDLE:
+       case OTG_STATE_B_IDLE:
+       case OTG_STATE_A_WAIT_VFALL:
+       case OTG_STATE_A_VBUS_ERR:
+               transceiver_suspend(pdev);
+               break;
+       case OTG_STATE_A_WAIT_VRISE:
+               langwell_otg_del_timer(a_wait_vrise_tmr);
+               langwell->hsm.a_srp_det = 0;
+               langwell_otg_drv_vbus(0);
+               langwell->otg.state = OTG_STATE_A_IDLE;
+               transceiver_suspend(pdev);
+               break;
+       case OTG_STATE_A_WAIT_BCON:
+               langwell_otg_del_timer(a_wait_bcon_tmr);
+               if (langwell->host_ops)
+                       ret = langwell->host_ops->suspend(pdev, message);
+               langwell_otg_drv_vbus(0);
+               break;
+       case OTG_STATE_A_HOST:
+               if (langwell->host_ops)
+                       ret = langwell->host_ops->suspend(pdev, message);
+               langwell_otg_drv_vbus(0);
+               langwell_otg_phy_low_power(1);
+               break;
+       case OTG_STATE_A_SUSPEND:
+               langwell_otg_del_timer(a_aidl_bdis_tmr);
+               langwell_otg_HABA(0);
+               if (langwell->host_ops)
+                       langwell->host_ops->remove(pdev);
+               else
+                       otg_dbg("host driver has been removed.\n");
+               langwell_otg_drv_vbus(0);
+               transceiver_suspend(pdev);
+               langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
+               break;
+       case OTG_STATE_A_PERIPHERAL:
+               if (langwell->client_ops)
+                       ret = langwell->client_ops->suspend(pdev, message);
+               else
+                       otg_dbg("client driver has been removed.\n");
+               langwell_otg_drv_vbus(0);
+               transceiver_suspend(pdev);
+               langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
+               break;
+       case OTG_STATE_B_HOST:
+               if (langwell->host_ops)
+                       langwell->host_ops->remove(pdev);
+               else
+                       otg_dbg("host driver has been removed.\n");
+               langwell->hsm.b_bus_req = 0;
+               transceiver_suspend(pdev);
+               langwell->otg.state = OTG_STATE_B_IDLE;
+               break;
+       case OTG_STATE_B_PERIPHERAL:
+               if (langwell->client_ops)
+                       ret = langwell->client_ops->suspend(pdev, message);
+               else
+                       otg_dbg("client driver has been removed.\n");
+               break;
+       case OTG_STATE_B_WAIT_ACON:
+               langwell_otg_del_timer(b_ase0_brst_tmr);
+               langwell_otg_HAAR(0);
+               if (langwell->host_ops)
+                       langwell->host_ops->remove(pdev);
+               else
+                       otg_dbg("host driver has been removed.\n");
+               langwell->hsm.b_bus_req = 0;
+               langwell->otg.state = OTG_STATE_B_IDLE;
+               transceiver_suspend(pdev);
+               break;
+       default:
+               otg_dbg("error state before suspend\n ");
+               break;
+       }
+       spin_unlock(&langwell->wq_lock);
+
+       return ret;
+}
+
+static void transceiver_resume(struct pci_dev *pdev)
+{
+       pci_restore_state(pdev);
+       pci_set_power_state(pdev, PCI_D0);
+       langwell_otg_phy_low_power(0);
+}
+
+static int langwell_otg_resume(struct pci_dev *pdev)
+{
+       int     ret = 0;
+       struct langwell_otg *langwell;
+
+       langwell = the_transceiver;
+
+       spin_lock(&langwell->wq_lock);
+
+       switch (langwell->otg.state) {
+       case OTG_STATE_A_IDLE:
+       case OTG_STATE_B_IDLE:
+       case OTG_STATE_A_WAIT_VFALL:
+       case OTG_STATE_A_VBUS_ERR:
+               transceiver_resume(pdev);
+               break;
+       case OTG_STATE_A_WAIT_BCON:
+               langwell_otg_add_timer(a_wait_bcon_tmr);
+               langwell_otg_drv_vbus(1);
+               if (langwell->host_ops)
+                       ret = langwell->host_ops->resume(pdev);
+               break;
+       case OTG_STATE_A_HOST:
+               langwell_otg_drv_vbus(1);
+               langwell_otg_phy_low_power(0);
+               if (langwell->host_ops)
+                       ret = langwell->host_ops->resume(pdev);
+               break;
+       case OTG_STATE_B_PERIPHERAL:
+               if (langwell->client_ops)
+                       ret = langwell->client_ops->resume(pdev);
+               else
+                       otg_dbg("client driver not loaded.\n");
+               break;
+       default:
+               otg_dbg("error state before suspend\n ");
+               break;
+       }
+
+       if (request_irq(pdev->irq, otg_irq, IRQF_SHARED,
+                               driver_name, the_transceiver) != 0) {
+               otg_dbg("request interrupt %d failed\n", pdev->irq);
+               ret = -EBUSY;
+       }
+
+       /* enable OTG interrupts */
+       langwell_otg_intr(1);
+
+       spin_unlock(&langwell->wq_lock);
+
+       queue_work(langwell->qwork, &langwell->work);
+
+
+       return ret;
+}
+
+static int __init langwell_otg_init(void)
+{
+       return pci_register_driver(&otg_pci_driver);
+}
+module_init(langwell_otg_init);
+
+static void __exit langwell_otg_cleanup(void)
+{
+       pci_unregister_driver(&otg_pci_driver);
+}
+module_exit(langwell_otg_cleanup);
index c567168..9ed5ea5 100644 (file)
@@ -22,8 +22,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * Current status:
- *     this is to add "nop" transceiver for all those phy which is
- *     autonomous such as isp1504 etc.
+ *     This provides a "nop" transceiver for PHYs which are
+ *     autonomous such as isp1504, isp1707, etc.
  */
 
 #include <linux/module.h>
@@ -36,30 +36,25 @@ struct nop_usb_xceiv {
        struct device           *dev;
 };
 
-static u64 nop_xceiv_dmamask = DMA_BIT_MASK(32);
-
-static struct platform_device nop_xceiv_device = {
-       .name           = "nop_usb_xceiv",
-       .id             = -1,
-       .dev = {
-               .dma_mask               = &nop_xceiv_dmamask,
-               .coherent_dma_mask      = DMA_BIT_MASK(32),
-               .platform_data          = NULL,
-       },
-};
+static struct platform_device *pd;
 
 void usb_nop_xceiv_register(void)
 {
-       if (platform_device_register(&nop_xceiv_device) < 0) {
+       if (pd)
+               return;
+       pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0);
+       if (!pd) {
                printk(KERN_ERR "Unable to register usb nop transceiver\n");
                return;
        }
 }
+EXPORT_SYMBOL(usb_nop_xceiv_register);
 
 void usb_nop_xceiv_unregister(void)
 {
-       platform_device_unregister(&nop_xceiv_device);
+       platform_device_unregister(pd);
 }
+EXPORT_SYMBOL(usb_nop_xceiv_unregister);
 
 static inline struct nop_usb_xceiv *xceiv_to_nop(struct otg_transceiver *x)
 {
index d9478d0..9e3e7a5 100644 (file)
 
 /* In module TWL4030_MODULE_PM_MASTER */
 #define PROTECT_KEY                    0x0E
+#define STS_HW_CONDITIONS              0x0F
 
 /* In module TWL4030_MODULE_PM_RECEIVER */
 #define VUSB_DEDICATED1                        0x7D
@@ -351,15 +352,26 @@ static enum linkstat twl4030_usb_linkstat(struct twl4030_usb *twl)
        int     status;
        int     linkstat = USB_LINK_UNKNOWN;
 
-       /* STS_HW_CONDITIONS */
-       status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER, 0x0f);
+       /*
+        * For ID/VBUS sensing, see manual section 15.4.8 ...
+        * except when using only battery backup power, two
+        * comparators produce VBUS_PRES and ID_PRES signals,
+        * which don't match docs elsewhere.  But ... BIT(7)
+        * and BIT(2) of STS_HW_CONDITIONS, respectively, do
+        * seem to match up.  If either is true the USB_PRES
+        * signal is active, the OTG module is activated, and
+        * its interrupt may be raised (may wake the system).
+        */
+       status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER,
+                       STS_HW_CONDITIONS);
        if (status < 0)
                dev_err(twl->dev, "USB link status err %d\n", status);
-       else if (status & BIT(7))
-               linkstat = USB_LINK_VBUS;
-       else if (status & BIT(2))
-               linkstat = USB_LINK_ID;
-       else
+       else if (status & (BIT(7) | BIT(2))) {
+               if (status & BIT(2))
+                       linkstat = USB_LINK_ID;
+               else
+                       linkstat = USB_LINK_VBUS;
+       } else
                linkstat = USB_LINK_NONE;
 
        dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
@@ -641,7 +653,7 @@ static int twl4030_set_host(struct otg_transceiver *x, struct usb_bus *host)
        return 0;
 }
 
-static int __init twl4030_usb_probe(struct platform_device *pdev)
+static int __devinit twl4030_usb_probe(struct platform_device *pdev)
 {
        struct twl4030_usb_data *pdata = pdev->dev.platform_data;
        struct twl4030_usb      *twl;
index 6d106e7..2cbfab3 100644 (file)
@@ -364,7 +364,7 @@ static int aircable_attach(struct usb_serial *serial)
        return 0;
 }
 
-static void aircable_shutdown(struct usb_serial *serial)
+static void aircable_release(struct usb_serial *serial)
 {
 
        struct usb_serial_port *port = serial->port[0];
@@ -375,7 +375,6 @@ static void aircable_shutdown(struct usb_serial *serial)
        if (priv) {
                serial_buf_free(priv->tx_buf);
                serial_buf_free(priv->rx_buf);
-               usb_set_serial_port_data(port, NULL);
                kfree(priv);
        }
 }
@@ -601,7 +600,7 @@ static struct usb_serial_driver aircable_device = {
        .num_ports =            1,
        .attach =               aircable_attach,
        .probe =                aircable_probe,
-       .shutdown =             aircable_shutdown,
+       .release =              aircable_release,
        .write =                aircable_write,
        .write_room =           aircable_write_room,
        .write_bulk_callback =  aircable_write_bulk_callback,
index 2bfd6dd..7033b03 100644 (file)
@@ -90,7 +90,7 @@ static int debug;
 
 /* function prototypes for a Belkin USB Serial Adapter F5U103 */
 static int  belkin_sa_startup(struct usb_serial *serial);
-static void belkin_sa_shutdown(struct usb_serial *serial);
+static void belkin_sa_release(struct usb_serial *serial);
 static int  belkin_sa_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp);
 static void belkin_sa_close(struct usb_serial_port *port);
@@ -142,7 +142,7 @@ static struct usb_serial_driver belkin_device = {
        .tiocmget =             belkin_sa_tiocmget,
        .tiocmset =             belkin_sa_tiocmset,
        .attach =               belkin_sa_startup,
-       .shutdown =             belkin_sa_shutdown,
+       .release =              belkin_sa_release,
 };
 
 
@@ -197,14 +197,13 @@ static int belkin_sa_startup(struct usb_serial *serial)
 }
 
 
-static void belkin_sa_shutdown(struct usb_serial *serial)
+static void belkin_sa_release(struct usb_serial *serial)
 {
        struct belkin_sa_private *priv;
        int i;
 
        dbg("%s", __func__);
 
-       /* stop reads and writes on all ports */
        for (i = 0; i < serial->num_ports; ++i) {
                /* My special items, the standard routines free my urbs */
                priv = usb_get_serial_port_data(serial->port[i]);
index 83bbb5b..ba555c5 100644 (file)
@@ -59,23 +59,22 @@ static int usb_serial_device_probe(struct device *dev)
                retval = -ENODEV;
                goto exit;
        }
+       if (port->dev_state != PORT_REGISTERING)
+               goto exit;
 
        driver = port->serial->type;
        if (driver->port_probe) {
-               if (!try_module_get(driver->driver.owner)) {
-                       dev_err(dev, "module get failed, exiting\n");
-                       retval = -EIO;
-                       goto exit;
-               }
                retval = driver->port_probe(port);
-               module_put(driver->driver.owner);
                if (retval)
                        goto exit;
        }
 
        retval = device_create_file(dev, &dev_attr_port_number);
-       if (retval)
+       if (retval) {
+               if (driver->port_remove)
+                       retval = driver->port_remove(port);
                goto exit;
+       }
 
        minor = port->number;
        tty_register_device(usb_serial_tty_driver, minor, dev);
@@ -98,19 +97,15 @@ static int usb_serial_device_remove(struct device *dev)
        if (!port)
                return -ENODEV;
 
+       if (port->dev_state != PORT_UNREGISTERING)
+               return retval;
+
        device_remove_file(&port->dev, &dev_attr_port_number);
 
        driver = port->serial->type;
-       if (driver->port_remove) {
-               if (!try_module_get(driver->driver.owner)) {
-                       dev_err(dev, "module get failed, exiting\n");
-                       retval = -EIO;
-                       goto exit;
-               }
+       if (driver->port_remove)
                retval = driver->port_remove(port);
-               module_put(driver->driver.owner);
-       }
-exit:
+
        minor = port->number;
        tty_unregister_device(usb_serial_tty_driver, minor);
        dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
index 16a154d..2b9eeda 100644 (file)
@@ -50,7 +50,7 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *,
                unsigned int, unsigned int);
 static void cp210x_break_ctl(struct tty_struct *, int);
 static int cp210x_startup(struct usb_serial *);
-static void cp210x_shutdown(struct usb_serial *);
+static void cp210x_disconnect(struct usb_serial *);
 
 static int debug;
 
@@ -137,7 +137,7 @@ static struct usb_serial_driver cp210x_device = {
        .tiocmget               = cp210x_tiocmget,
        .tiocmset               = cp210x_tiocmset,
        .attach                 = cp210x_startup,
-       .shutdown               = cp210x_shutdown,
+       .disconnect             = cp210x_disconnect,
 };
 
 /* Config request types */
@@ -792,7 +792,7 @@ static int cp210x_startup(struct usb_serial *serial)
        return 0;
 }
 
-static void cp210x_shutdown(struct usb_serial *serial)
+static void cp210x_disconnect(struct usb_serial *serial)
 {
        int i;
 
index 933ba91..336523f 100644 (file)
@@ -58,7 +58,8 @@ static int debug;
 
 /* Function prototypes */
 static int cyberjack_startup(struct usb_serial *serial);
-static void cyberjack_shutdown(struct usb_serial *serial);
+static void cyberjack_disconnect(struct usb_serial *serial);
+static void cyberjack_release(struct usb_serial *serial);
 static int  cyberjack_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp);
 static void cyberjack_close(struct usb_serial_port *port);
@@ -94,7 +95,8 @@ static struct usb_serial_driver cyberjack_device = {
        .id_table =             id_table,
        .num_ports =            1,
        .attach =               cyberjack_startup,
-       .shutdown =             cyberjack_shutdown,
+       .disconnect =           cyberjack_disconnect,
+       .release =              cyberjack_release,
        .open =                 cyberjack_open,
        .close =                cyberjack_close,
        .write =                cyberjack_write,
@@ -148,17 +150,25 @@ static int cyberjack_startup(struct usb_serial *serial)
        return 0;
 }
 
-static void cyberjack_shutdown(struct usb_serial *serial)
+static void cyberjack_disconnect(struct usb_serial *serial)
 {
        int i;
 
        dbg("%s", __func__);
 
-       for (i = 0; i < serial->num_ports; ++i) {
+       for (i = 0; i < serial->num_ports; ++i)
                usb_kill_urb(serial->port[i]->interrupt_in_urb);
+}
+
+static void cyberjack_release(struct usb_serial *serial)
+{
+       int i;
+
+       dbg("%s", __func__);
+
+       for (i = 0; i < serial->num_ports; ++i) {
                /* My special items, the standard routines free my urbs */
                kfree(usb_get_serial_port_data(serial->port[i]));
-               usb_set_serial_port_data(serial->port[i], NULL);
        }
 }
 
index 669f938..9734085 100644 (file)
@@ -171,7 +171,7 @@ struct cypress_buf {
 static int  cypress_earthmate_startup(struct usb_serial *serial);
 static int  cypress_hidcom_startup(struct usb_serial *serial);
 static int  cypress_ca42v2_startup(struct usb_serial *serial);
-static void cypress_shutdown(struct usb_serial *serial);
+static void cypress_release(struct usb_serial *serial);
 static int  cypress_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp);
 static void cypress_close(struct usb_serial_port *port);
@@ -215,7 +215,7 @@ static struct usb_serial_driver cypress_earthmate_device = {
        .id_table =                     id_table_earthmate,
        .num_ports =                    1,
        .attach =                       cypress_earthmate_startup,
-       .shutdown =                     cypress_shutdown,
+       .release =                      cypress_release,
        .open =                         cypress_open,
        .close =                        cypress_close,
        .dtr_rts =                      cypress_dtr_rts,
@@ -242,7 +242,7 @@ static struct usb_serial_driver cypress_hidcom_device = {
        .id_table =                     id_table_cyphidcomrs232,
        .num_ports =                    1,
        .attach =                       cypress_hidcom_startup,
-       .shutdown =                     cypress_shutdown,
+       .release =                      cypress_release,
        .open =                         cypress_open,
        .close =                        cypress_close,
        .dtr_rts =                      cypress_dtr_rts,
@@ -269,7 +269,7 @@ static struct usb_serial_driver cypress_ca42v2_device = {
        .id_table =                     id_table_nokiaca42v2,
        .num_ports =                    1,
        .attach =                       cypress_ca42v2_startup,
-       .shutdown =                     cypress_shutdown,
+       .release =                      cypress_release,
        .open =                         cypress_open,
        .close =                        cypress_close,
        .dtr_rts =                      cypress_dtr_rts,
@@ -616,7 +616,7 @@ static int cypress_ca42v2_startup(struct usb_serial *serial)
 } /* cypress_ca42v2_startup */
 
 
-static void cypress_shutdown(struct usb_serial *serial)
+static void cypress_release(struct usb_serial *serial)
 {
        struct cypress_private *priv;
 
@@ -629,7 +629,6 @@ static void cypress_shutdown(struct usb_serial *serial)
        if (priv) {
                cypress_buf_free(priv->buf);
                kfree(priv);
-               usb_set_serial_port_data(serial->port[0], NULL);
        }
 }
 
index 30f5140..f480809 100644 (file)
@@ -460,7 +460,8 @@ static int digi_carrier_raised(struct usb_serial_port *port);
 static void digi_dtr_rts(struct usb_serial_port *port, int on);
 static int digi_startup_device(struct usb_serial *serial);
 static int digi_startup(struct usb_serial *serial);
-static void digi_shutdown(struct usb_serial *serial);
+static void digi_disconnect(struct usb_serial *serial);
+static void digi_release(struct usb_serial *serial);
 static void digi_read_bulk_callback(struct urb *urb);
 static int digi_read_inb_callback(struct urb *urb);
 static int digi_read_oob_callback(struct urb *urb);
@@ -524,7 +525,8 @@ static struct usb_serial_driver digi_acceleport_2_device = {
        .tiocmget =                     digi_tiocmget,
        .tiocmset =                     digi_tiocmset,
        .attach =                       digi_startup,
-       .shutdown =                     digi_shutdown,
+       .disconnect =                   digi_disconnect,
+       .release =                      digi_release,
 };
 
 static struct usb_serial_driver digi_acceleport_4_device = {
@@ -550,7 +552,8 @@ static struct usb_serial_driver digi_acceleport_4_device = {
        .tiocmget =                     digi_tiocmget,
        .tiocmset =                     digi_tiocmset,
        .attach =                       digi_startup,
-       .shutdown =                     digi_shutdown,
+       .disconnect =                   digi_disconnect,
+       .release =                      digi_release,
 };
 
 
@@ -1556,16 +1559,23 @@ static int digi_startup(struct usb_serial *serial)
 }
 
 
-static void digi_shutdown(struct usb_serial *serial)
+static void digi_disconnect(struct usb_serial *serial)
 {
        int i;
-       dbg("digi_shutdown: TOP, in_interrupt()=%ld", in_interrupt());
+       dbg("digi_disconnect: TOP, in_interrupt()=%ld", in_interrupt());
 
        /* stop reads and writes on all ports */
        for (i = 0; i < serial->type->num_ports + 1; i++) {
                usb_kill_urb(serial->port[i]->read_urb);
                usb_kill_urb(serial->port[i]->write_urb);
        }
+}
+
+
+static void digi_release(struct usb_serial *serial)
+{
+       int i;
+       dbg("digi_release: TOP, in_interrupt()=%ld", in_interrupt());
 
        /* free the private data structures for all ports */
        /* number of regular ports + 1 for the out-of-band port */
index 2b141cc..80cb347 100644 (file)
@@ -90,7 +90,6 @@ static int  empeg_chars_in_buffer(struct tty_struct *tty);
 static void empeg_throttle(struct tty_struct *tty);
 static void empeg_unthrottle(struct tty_struct *tty);
 static int  empeg_startup(struct usb_serial *serial);
-static void empeg_shutdown(struct usb_serial *serial);
 static void empeg_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios);
 static void empeg_write_bulk_callback(struct urb *urb);
@@ -124,7 +123,6 @@ static struct usb_serial_driver empeg_device = {
        .throttle =             empeg_throttle,
        .unthrottle =           empeg_unthrottle,
        .attach =               empeg_startup,
-       .shutdown =             empeg_shutdown,
        .set_termios =          empeg_set_termios,
        .write =                empeg_write,
        .write_room =           empeg_write_room,
@@ -427,12 +425,6 @@ static int  empeg_startup(struct usb_serial *serial)
 }
 
 
-static void empeg_shutdown(struct usb_serial *serial)
-{
-       dbg("%s", __func__);
-}
-
-
 static void empeg_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios)
 {
index 683304d..3dc3768 100644 (file)
@@ -47,7 +47,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.4.3"
+#define DRIVER_VERSION "v1.5.0"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>"
 #define DRIVER_DESC "USB FTDI Serial Converters Driver"
 
@@ -82,7 +82,8 @@ struct ftdi_private {
        int rx_processed;
        unsigned long rx_bytes;
 
-       __u16 interface;        /* FT2232C port interface (0 for FT232/245) */
+       __u16 interface;        /* FT2232C, FT2232H or FT4232H port interface
+                                  (0 for FT232/245) */
 
        speed_t force_baud;     /* if non-zero, force the baud rate to
                                   this value */
@@ -94,6 +95,7 @@ struct ftdi_private {
        unsigned long tx_bytes;
        unsigned long tx_outstanding_bytes;
        unsigned long tx_outstanding_urbs;
+       unsigned short max_packet_size;
 };
 
 /* struct ftdi_sio_quirk is used by devices requiring special attention. */
@@ -164,6 +166,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_4232H_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) },
@@ -673,6 +676,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
        { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+       { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) },
        { },                                    /* Optional parameter entry */
        { }                                     /* Terminating entry */
 };
@@ -693,12 +697,13 @@ static const char *ftdi_chip_name[] = {
        [FT232BM] = "FT232BM",
        [FT2232C] = "FT2232C",
        [FT232RL] = "FT232RL",
+       [FT2232H] = "FT2232H",
+       [FT4232H] = "FT4232H"
 };
 
 
 /* Constants for read urb and write urb */
 #define BUFSZ 512
-#define PKTSZ 64
 
 /* rx_flags */
 #define THROTTLED              0x01
@@ -715,7 +720,6 @@ static const char *ftdi_chip_name[] = {
 /* function prototypes for a FTDI serial converter */
 static int  ftdi_sio_probe(struct usb_serial *serial,
                                        const struct usb_device_id *id);
-static void ftdi_shutdown(struct usb_serial *serial);
 static int  ftdi_sio_port_probe(struct usb_serial_port *port);
 static int  ftdi_sio_port_remove(struct usb_serial_port *port);
 static int  ftdi_open(struct tty_struct *tty,
@@ -744,6 +748,8 @@ static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
 static unsigned short int ftdi_232am_baud_to_divisor(int baud);
 static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base);
 static __u32 ftdi_232bm_baud_to_divisor(int baud);
+static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base);
+static __u32 ftdi_2232h_baud_to_divisor(int baud);
 
 static struct usb_serial_driver ftdi_sio_device = {
        .driver = {
@@ -772,7 +778,6 @@ static struct usb_serial_driver ftdi_sio_device = {
        .ioctl =                ftdi_ioctl,
        .set_termios =          ftdi_set_termios,
        .break_ctl =            ftdi_break_ctl,
-       .shutdown =             ftdi_shutdown,
 };
 
 
@@ -838,6 +843,36 @@ static __u32 ftdi_232bm_baud_to_divisor(int baud)
         return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
 }
 
+static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
+{
+       static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+       __u32 divisor;
+       int divisor3;
+
+       /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
+       divisor3 = (base / 10 / baud) * 8;
+
+       divisor = divisor3 >> 3;
+       divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
+       /* Deal with special cases for highest baud rates. */
+       if (divisor == 1)
+               divisor = 0;
+       else if (divisor == 0x4001)
+               divisor = 1;
+       /*
+        * Set this bit to turn off a divide by 2.5 on baud rate generator
+        * This enables baud rates up to 12Mbaud but cannot reach below 1200
+        * baud with this bit set
+        */
+       divisor |= 0x00020000;
+       return divisor;
+}
+
+static __u32 ftdi_2232h_baud_to_divisor(int baud)
+{
+        return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
+}
+
 #define set_mctrl(port, set)           update_mctrl((port), (set), 0)
 #define clear_mctrl(port, clear)       update_mctrl((port), 0, (clear))
 
@@ -996,6 +1031,19 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
                        baud = 9600;
                }
                break;
+       case FT2232H: /* FT2232H chip */
+       case FT4232H: /* FT4232H chip */
+               if ((baud <= 12000000) & (baud >= 1200)) {
+                       div_value = ftdi_2232h_baud_to_divisor(baud);
+               } else if (baud < 1200) {
+                       div_value = ftdi_232bm_baud_to_divisor(baud);
+               } else {
+                       dbg("%s - Baud rate too high!", __func__);
+                       div_value = ftdi_232bm_baud_to_divisor(9600);
+                       div_okay = 0;
+                       baud = 9600;
+               }
+               break;
        } /* priv->chip_type */
 
        if (div_okay) {
@@ -1196,14 +1244,29 @@ static void ftdi_determine_type(struct usb_serial_port *port)
        if (interfaces > 1) {
                int inter;
 
-               /* Multiple interfaces.  Assume FT2232C. */
-               priv->chip_type = FT2232C;
+               /* Multiple interfaces.*/
+               if (version == 0x0800) {
+                       priv->chip_type = FT4232H;
+                       /* Hi-speed - baud clock runs at 120MHz */
+                       priv->baud_base = 120000000 / 2;
+               } else if (version == 0x0700) {
+                       priv->chip_type = FT2232H;
+                       /* Hi-speed - baud clock runs at 120MHz */
+                       priv->baud_base = 120000000 / 2;
+               } else
+                       priv->chip_type = FT2232C;
+
                /* Determine interface code. */
                inter = serial->interface->altsetting->desc.bInterfaceNumber;
-               if (inter == 0)
-                       priv->interface = PIT_SIOA;
-               else
-                       priv->interface = PIT_SIOB;
+               if (inter == 0) {
+                       priv->interface = INTERFACE_A;
+               } else  if (inter == 1) {
+                       priv->interface = INTERFACE_B;
+               } else  if (inter == 2) {
+                       priv->interface = INTERFACE_C;
+               } else  if (inter == 3) {
+                       priv->interface = INTERFACE_D;
+               }
                /* BM-type devices have a bug where bcdDevice gets set
                 * to 0x200 when iSerialNumber is 0.  */
                if (version < 0x500) {
@@ -1231,6 +1294,45 @@ static void ftdi_determine_type(struct usb_serial_port *port)
 }
 
 
+/* Determine the maximum packet size for the device.  This depends on the chip
+ * type and the USB host capabilities.  The value should be obtained from the
+ * device descriptor as the chip will use the appropriate values for the host.*/
+static void ftdi_set_max_packet_size(struct usb_serial_port *port)
+{
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       struct usb_serial *serial = port->serial;
+       struct usb_device *udev = serial->dev;
+
+       struct usb_interface *interface = serial->interface;
+       struct usb_endpoint_descriptor *ep_desc = &interface->cur_altsetting->endpoint[1].desc;
+
+       unsigned num_endpoints;
+       int i = 0;
+
+       num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
+       dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints);
+
+       /* NOTE: some customers have programmed FT232R/FT245R devices
+        * with an endpoint size of 0 - not good.  In this case, we
+        * want to override the endpoint descriptor setting and use a
+        * value of 64 for wMaxPacketSize */
+       for (i = 0; i < num_endpoints; i++) {
+               dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1,
+                       interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+               ep_desc = &interface->cur_altsetting->endpoint[i].desc;
+               if (ep_desc->wMaxPacketSize == 0) {
+                       ep_desc->wMaxPacketSize = cpu_to_le16(0x40);
+                       dev_info(&udev->dev, "Overriding wMaxPacketSize on endpoint %d\n", i);
+               }
+       }
+
+       /* set max packet size based on descriptor */
+       priv->max_packet_size = ep_desc->wMaxPacketSize;
+
+       dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size);
+}
+
+
 /*
  * ***************************************************************************
  * Sysfs Attribute
@@ -1314,7 +1416,9 @@ static int create_sysfs_attrs(struct usb_serial_port *port)
                if ((!retval) &&
                    (priv->chip_type == FT232BM ||
                     priv->chip_type == FT2232C ||
-                    priv->chip_type == FT232RL)) {
+                    priv->chip_type == FT232RL ||
+                    priv->chip_type == FT2232H ||
+                    priv->chip_type == FT4232H)) {
                        retval = device_create_file(&port->dev,
                                                    &dev_attr_latency_timer);
                }
@@ -1333,7 +1437,9 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
                device_remove_file(&port->dev, &dev_attr_event_char);
                if (priv->chip_type == FT232BM ||
                    priv->chip_type == FT2232C ||
-                   priv->chip_type == FT232RL) {
+                   priv->chip_type == FT232RL ||
+                   priv->chip_type == FT2232H ||
+                   priv->chip_type == FT4232H) {
                        device_remove_file(&port->dev, &dev_attr_latency_timer);
                }
        }
@@ -1416,6 +1522,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
        usb_set_serial_port_data(port, priv);
 
        ftdi_determine_type(port);
+       ftdi_set_max_packet_size(port);
        read_latency_timer(port);
        create_sysfs_attrs(port);
        return 0;
@@ -1485,18 +1592,6 @@ static int ftdi_mtxorb_hack_setup(struct usb_serial *serial)
        return 0;
 }
 
-/* ftdi_shutdown is called from usbserial:usb_serial_disconnect
- *   it is called when the usb device is disconnected
- *
- *   usbserial:usb_serial_disconnect
- *      calls __serial_close for each open of the port
- *      shutdown is called then (ie ftdi_shutdown)
- */
-static void ftdi_shutdown(struct usb_serial *serial)
-{
-       dbg("%s", __func__);
-}
-
 static void ftdi_sio_priv_release(struct kref *k)
 {
        struct ftdi_private *priv = container_of(k, struct ftdi_private, kref);
@@ -1671,8 +1766,8 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,
        if (data_offset > 0) {
                /* Original sio needs control bytes too... */
                transfer_size += (data_offset *
-                               ((count + (PKTSZ - 1 - data_offset)) /
-                                (PKTSZ - data_offset)));
+                               ((count + (priv->max_packet_size - 1 - data_offset)) /
+                                (priv->max_packet_size - data_offset)));
        }
 
        buffer = kmalloc(transfer_size, GFP_ATOMIC);
@@ -1694,7 +1789,7 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,
        if (data_offset > 0) {
                /* Original sio requires control byte at start of
                   each packet. */
-               int user_pktsz = PKTSZ - data_offset;
+               int user_pktsz = priv->max_packet_size - data_offset;
                int todo = count;
                unsigned char *first_byte = buffer;
                const unsigned char *current_position = buf;
@@ -1775,11 +1870,6 @@ static void ftdi_write_bulk_callback(struct urb *urb)
 
        dbg("%s - port %d", __func__, port->number);
 
-       if (status) {
-               dbg("nonzero write bulk status received: %d", status);
-               return;
-       }
-
        priv = usb_get_serial_port_data(port);
        if (!priv) {
                dbg("%s - bad port private data pointer - exiting", __func__);
@@ -1790,13 +1880,18 @@ static void ftdi_write_bulk_callback(struct urb *urb)
        data_offset = priv->write_offset;
        if (data_offset > 0) {
                /* Subtract the control bytes */
-               countback -= (data_offset * DIV_ROUND_UP(countback, PKTSZ));
+               countback -= (data_offset * DIV_ROUND_UP(countback, priv->max_packet_size));
        }
        spin_lock_irqsave(&priv->tx_lock, flags);
        --priv->tx_outstanding_urbs;
        priv->tx_outstanding_bytes -= countback;
        spin_unlock_irqrestore(&priv->tx_lock, flags);
 
+       if (status) {
+               dbg("nonzero write bulk status received: %d", status);
+               return;
+       }
+
        usb_serial_port_softint(port);
 } /* ftdi_write_bulk_callback */
 
@@ -1892,7 +1987,7 @@ static void ftdi_read_bulk_callback(struct urb *urb)
 
        /* count data bytes, but not status bytes */
        countread = urb->actual_length;
-       countread -= 2 * DIV_ROUND_UP(countread, PKTSZ);
+       countread -= 2 * DIV_ROUND_UP(countread, priv->max_packet_size);
        spin_lock_irqsave(&priv->rx_lock, flags);
        priv->rx_bytes += countread;
        spin_unlock_irqrestore(&priv->rx_lock, flags);
@@ -1965,7 +2060,7 @@ static void ftdi_process_read(struct work_struct *work)
 
        need_flip = 0;
        for (packet_offset = priv->rx_processed;
-               packet_offset < urb->actual_length; packet_offset += PKTSZ) {
+               packet_offset < urb->actual_length; packet_offset += priv->max_packet_size) {
                int length;
 
                /* Compare new line status to the old one, signal if different/
@@ -1980,7 +2075,7 @@ static void ftdi_process_read(struct work_struct *work)
                        priv->prev_status = new_status;
                }
 
-               length = min_t(u32, PKTSZ, urb->actual_length-packet_offset)-2;
+               length = min_t(u32, priv->max_packet_size, urb->actual_length-packet_offset)-2;
                if (length < 0) {
                        dev_err(&port->dev, "%s - bad packet length: %d\n",
                                __func__, length+2);
@@ -2011,6 +2106,7 @@ static void ftdi_process_read(struct work_struct *work)
                if (data[packet_offset+1] & FTDI_RS_BI) {
                        error_flag = TTY_BREAK;
                        dbg("BREAK received");
+                       usb_serial_handle_break(port);
                }
                if (data[packet_offset+1] & FTDI_RS_PE) {
                        error_flag = TTY_PARITY;
@@ -2025,8 +2121,11 @@ static void ftdi_process_read(struct work_struct *work)
                                /* Note that the error flag is duplicated for
                                   every character received since we don't know
                                   which character it applied to */
-                               tty_insert_flip_char(tty,
-                                       data[packet_offset + i], error_flag);
+                               if (!usb_serial_handle_sysrq_char(port,
+                                               data[packet_offset + i]))
+                                       tty_insert_flip_char(tty,
+                                               data[packet_offset + i],
+                                               error_flag);
                        }
                        need_flip = 1;
                }
@@ -2332,6 +2431,8 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)
        case FT232BM:
        case FT2232C:
        case FT232RL:
+       case FT2232H:
+       case FT4232H:
                /* the 8U232AM returns a two byte value (the sio is a 1 byte
                   value) - in the same format as the data returned from the in
                   point */
index 12330fa..f1d440a 100644 (file)
@@ -10,7 +10,7 @@
  * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side,
  * USB on the other.
  *
- * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details
+ * Thanx to FTDI (http://www.ftdichip.com) for so kindly providing details
  * of the protocol required to talk to the device and ongoing assistence
  * during development.
  *
 #define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */
 #define FTDI_8U2232C_PID 0x6010 /* Dual channel device */
 #define FTDI_232RL_PID  0xFBFA  /* Product ID for FT232RL */
+#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */
 #define FTDI_RELAIS_PID        0xFA10  /* Relais device from Rudolf Gugler */
 #define FTDI_NF_RIC_VID        0x0DCD  /* Vendor Id */
 #define FTDI_NF_RIC_PID        0x0001  /* Product Id */
 #define FTDI_USBX_707_PID 0xF857       /* ADSTech IR Blaster USBX-707 */
 
+/* Larsen and Brusgaard AltiTrack/USBtrack  */
+#define LARSENBRUSGAARD_VID            0x0FD8
+#define LB_ALTITRACK_PID               0x0001
 
 /* www.canusb.com Lawicel CANUSB device */
 #define FTDI_CANUSB_PID 0xFFA8 /* Product Id */
 #define FTDI_SIO_SET_LATENCY_TIMER     9 /* Set the latency timer */
 #define FTDI_SIO_GET_LATENCY_TIMER     10 /* Get the latency timer */
 
+/* Interface indicies for FT2232, FT2232H and FT4232H devices*/
+#define INTERFACE_A            1
+#define INTERFACE_B            2
+#define INTERFACE_C            3
+#define INTERFACE_D            4
 
 /*
  * FIC / OpenMoko, Inc. http://wiki.openmoko.org/wiki/Neo1973_Debug_Board_v3
@@ -1036,6 +1045,8 @@ typedef enum {
        FT232BM = 3,
        FT2232C = 4,
        FT232RL = 5,
+       FT2232H = 6,
+       FT4232H = 7
 } ftdi_chip_type_t;
 
 typedef enum {
index ee25a3f..8839f1c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Garmin GPS driver
  *
- * Copyright (C) 2006,2007 Hermann Kneissel herkne@users.sourceforge.net
+ * Copyright (C) 2006-2009 Hermann Kneissel herkne@users.sourceforge.net
  *
  * The latest version of the driver can be found at
  * http://sourceforge.net/projects/garmin-gps/
@@ -51,7 +51,7 @@ static int debug;
  */
 
 #define VERSION_MAJOR  0
-#define VERSION_MINOR  31
+#define VERSION_MINOR  33
 
 #define _STR(s) #s
 #define _DRIVER_VERSION(a, b) "v" _STR(a) "." _STR(b)
@@ -129,7 +129,6 @@ struct garmin_data {
        __u8   state;
        __u16  flags;
        __u8   mode;
-       __u8   ignorePkts;
        __u8   count;
        __u8   pkt_id;
        __u32  serial_num;
@@ -141,8 +140,6 @@ struct garmin_data {
        __u8   inbuffer [GPS_IN_BUFSIZ];  /* tty -> usb */
        __u8   outbuffer[GPS_OUT_BUFSIZ]; /* usb -> tty */
        __u8   privpkt[4*6];
-       atomic_t req_count;
-       atomic_t resp_count;
        spinlock_t lock;
        struct list_head pktlist;
 };
@@ -170,6 +167,8 @@ struct garmin_data {
 #define FLAGS_BULK_IN_ACTIVE      0x0020
 #define FLAGS_BULK_IN_RESTART     0x0010
 #define FLAGS_THROTTLED           0x0008
+#define APP_REQ_SEEN              0x0004
+#define APP_RESP_SEEN             0x0002
 #define CLEAR_HALT_REQUIRED       0x0001
 
 #define FLAGS_QUEUING             0x0100
@@ -184,20 +183,16 @@ struct garmin_data {
 
 
 /* function prototypes */
-static void gsp_next_packet(struct garmin_data *garmin_data_p);
-static int  garmin_write_bulk(struct usb_serial_port *port,
+static int gsp_next_packet(struct garmin_data *garmin_data_p);
+static int garmin_write_bulk(struct usb_serial_port *port,
                             const unsigned char *buf, int count,
                             int dismiss_ack);
 
 /* some special packets to be send or received */
 static unsigned char const GARMIN_START_SESSION_REQ[]
        = { 0, 0, 0, 0,  5, 0, 0, 0, 0, 0, 0, 0 };
-static unsigned char const GARMIN_START_SESSION_REQ2[]
-       = { 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
 static unsigned char const GARMIN_START_SESSION_REPLY[]
        = { 0, 0, 0, 0,  6, 0, 0, 0, 4, 0, 0, 0 };
-static unsigned char const GARMIN_SESSION_ACTIVE_REPLY[]
-       = { 0, 0, 0, 0, 17, 0, 0, 0, 4, 0, 0, 0, 0, 16, 0, 0 };
 static unsigned char const GARMIN_BULK_IN_AVAIL_REPLY[]
        = { 0, 0, 0, 0,  2, 0, 0, 0, 0, 0, 0, 0 };
 static unsigned char const GARMIN_APP_LAYER_REPLY[]
@@ -233,13 +228,6 @@ static struct usb_driver garmin_driver = {
 };
 
 
-static inline int noResponseFromAppLayer(struct garmin_data *garmin_data_p)
-{
-       return atomic_read(&garmin_data_p->req_count) ==
-                               atomic_read(&garmin_data_p->resp_count);
-}
-
-
 static inline int getLayerId(const __u8 *usbPacket)
 {
        return __le32_to_cpup((__le32 *)(usbPacket));
@@ -325,8 +313,11 @@ static int pkt_add(struct garmin_data *garmin_data_p,
                state = garmin_data_p->state;
                spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
+               dbg("%s - added: pkt: %d - %d bytes",
+                       __func__, pkt->seq, data_length);
+
                /* in serial mode, if someone is waiting for data from
-                  the device, iconvert and send the next packet to tty. */
+                  the device, convert and send the next packet to tty. */
                if (result && (state == STATE_GSP_WAIT_DATA))
                        gsp_next_packet(garmin_data_p);
        }
@@ -411,7 +402,7 @@ static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id)
 /*
  * called for a complete packet received from tty layer
  *
- * the complete packet (pkzid ... cksum) is in garmin_data_p->inbuf starting
+ * the complete packet (pktid ... cksum) is in garmin_data_p->inbuf starting
  * at GSP_INITIAL_OFFSET.
  *
  * count - number of bytes in the input buffer including space reserved for
@@ -501,7 +492,6 @@ static int gsp_receive(struct garmin_data *garmin_data_p,
        unsigned long flags;
        int offs = 0;
        int ack_or_nak_seen = 0;
-       int i = 0;
        __u8 *dest;
        int size;
        /* dleSeen: set if last byte read was a DLE */
@@ -519,8 +509,8 @@ static int gsp_receive(struct garmin_data *garmin_data_p,
        skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
        spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
-       dbg("%s - dle=%d skip=%d size=%d count=%d",
-               __func__, dleSeen, skip, size, count);
+       /* dbg("%s - dle=%d skip=%d size=%d count=%d",
+               __func__, dleSeen, skip, size, count); */
 
        if (size == 0)
                size = GSP_INITIAL_OFFSET;
@@ -568,7 +558,6 @@ static int gsp_receive(struct garmin_data *garmin_data_p,
                } else if (!skip) {
 
                        if (dleSeen) {
-                               dbg("non-masked DLE at %d - restarting", i);
                                size = GSP_INITIAL_OFFSET;
                                dleSeen = 0;
                        }
@@ -599,19 +588,19 @@ static int gsp_receive(struct garmin_data *garmin_data_p,
        else
                garmin_data_p->flags &= ~FLAGS_GSP_DLESEEN;
 
-       if (ack_or_nak_seen)
-               garmin_data_p->state = STATE_GSP_WAIT_DATA;
-
        spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
-       if (ack_or_nak_seen)
-               gsp_next_packet(garmin_data_p);
+       if (ack_or_nak_seen) {
+               if (gsp_next_packet(garmin_data_p) > 0)
+                       garmin_data_p->state = STATE_ACTIVE;
+               else
+                       garmin_data_p->state = STATE_GSP_WAIT_DATA;
+       }
        return count;
 }
 
 
 
-
 /*
  * Sends a usb packet to the tty
  *
@@ -733,29 +722,28 @@ static int gsp_send(struct garmin_data *garmin_data_p,
 }
 
 
-
-
-
 /*
  * Process the next pending data packet - if there is one
  */
-static void gsp_next_packet(struct garmin_data *garmin_data_p)
+static int gsp_next_packet(struct garmin_data *garmin_data_p)
 {
+       int result = 0;
        struct garmin_packet *pkt = NULL;
 
        while ((pkt = pkt_pop(garmin_data_p)) != NULL) {
                dbg("%s - next pkt: %d", __func__, pkt->seq);
-               if (gsp_send(garmin_data_p, pkt->data, pkt->size) > 0) {
+               result = gsp_send(garmin_data_p, pkt->data, pkt->size);
+               if (result > 0) {
                        kfree(pkt);
-                       return;
+                       return result;
                }
                kfree(pkt);
        }
+       return result;
 }
 
 
 
-
 /******************************************************************************
  * garmin native mode
  ******************************************************************************/
@@ -888,14 +876,6 @@ static int garmin_clear(struct garmin_data *garmin_data_p)
        unsigned long flags;
        int status = 0;
 
-       struct usb_serial_port *port = garmin_data_p->port;
-
-       if (port != NULL && atomic_read(&garmin_data_p->resp_count)) {
-               /* send a terminate command */
-               status = garmin_write_bulk(port, GARMIN_STOP_TRANSFER_REQ,
-                                       sizeof(GARMIN_STOP_TRANSFER_REQ), 1);
-       }
-
        /* flush all queued data */
        pkt_clear(garmin_data_p);
 
@@ -908,16 +888,12 @@ static int garmin_clear(struct garmin_data *garmin_data_p)
 }
 
 
-
-
-
-
 static int garmin_init_session(struct usb_serial_port *port)
 {
-       unsigned long flags;
        struct usb_serial *serial = port->serial;
        struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
        int status = 0;
+       int i = 0;
 
        if (status == 0) {
                usb_kill_urb(port->interrupt_in_urb);
@@ -931,30 +907,25 @@ static int garmin_init_session(struct usb_serial_port *port)
                                                        __func__, status);
        }
 
+       /*
+        * using the initialization method from gpsbabel. See comments in
+        * gpsbabel/jeeps/gpslibusb.c gusb_reset_toggles()
+        */
        if (status == 0) {
                dbg("%s - starting session ...", __func__);
                garmin_data_p->state = STATE_ACTIVE;
-               status = garmin_write_bulk(port, GARMIN_START_SESSION_REQ,
-                                       sizeof(GARMIN_START_SESSION_REQ), 0);
-
-               if (status >= 0) {
-
-                       spin_lock_irqsave(&garmin_data_p->lock, flags);
-                       garmin_data_p->ignorePkts++;
-                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
-                       /* not needed, but the win32 driver does it too ... */
+               for (i = 0; i < 3; i++) {
                        status = garmin_write_bulk(port,
-                                       GARMIN_START_SESSION_REQ2,
-                                       sizeof(GARMIN_START_SESSION_REQ2), 0);
-                       if (status >= 0) {
-                               status = 0;
-                               spin_lock_irqsave(&garmin_data_p->lock, flags);
-                               garmin_data_p->ignorePkts++;
-                               spin_unlock_irqrestore(&garmin_data_p->lock,
-                                                                       flags);
-                       }
+                                       GARMIN_START_SESSION_REQ,
+                                       sizeof(GARMIN_START_SESSION_REQ), 0);
+
+                       if (status < 0)
+                               break;
                }
+
+               if (status > 0)
+                       status = 0;
        }
 
        return status;
@@ -962,8 +933,6 @@ static int garmin_init_session(struct usb_serial_port *port)
 
 
 
-
-
 static int garmin_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp)
 {
@@ -977,8 +946,6 @@ static int garmin_open(struct tty_struct *tty,
        garmin_data_p->mode  = initial_mode;
        garmin_data_p->count = 0;
        garmin_data_p->flags = 0;
-       atomic_set(&garmin_data_p->req_count, 0);
-       atomic_set(&garmin_data_p->resp_count, 0);
        spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
        /* shutdown any bulk reads that might be going on */
@@ -1006,6 +973,7 @@ static void garmin_close(struct usb_serial_port *port)
                return;
 
        mutex_lock(&port->serial->disc_mutex);
+
        if (!port->serial->disconnected)
                garmin_clear(garmin_data_p);
 
@@ -1013,25 +981,17 @@ static void garmin_close(struct usb_serial_port *port)
        usb_kill_urb(port->read_urb);
        usb_kill_urb(port->write_urb);
 
-       if (!port->serial->disconnected) {
-               if (noResponseFromAppLayer(garmin_data_p) ||
-                   ((garmin_data_p->flags & CLEAR_HALT_REQUIRED) != 0)) {
-                       process_resetdev_request(port);
-                       garmin_data_p->state = STATE_RESET;
-               } else {
-                       garmin_data_p->state = STATE_DISCONNECTED;
-               }
-       } else {
+       /* keep reset state so we know that we must start a new session */
+       if (garmin_data_p->state != STATE_RESET)
                garmin_data_p->state = STATE_DISCONNECTED;
-       }
+
        mutex_unlock(&port->serial->disc_mutex);
 }
 
+
 static void garmin_write_bulk_callback(struct urb *urb)
 {
-       unsigned long flags;
        struct usb_serial_port *port = urb->context;
-       int status = urb->status;
 
        if (port) {
                struct garmin_data *garmin_data_p =
@@ -1039,20 +999,13 @@ static void garmin_write_bulk_callback(struct urb *urb)
 
                dbg("%s - port %d", __func__, port->number);
 
-               if (GARMIN_LAYERID_APPL == getLayerId(urb->transfer_buffer)
-                   && (garmin_data_p->mode == MODE_GARMIN_SERIAL))  {
-                       gsp_send_ack(garmin_data_p,
-                                       ((__u8 *)urb->transfer_buffer)[4]);
-               }
+               if (GARMIN_LAYERID_APPL == getLayerId(urb->transfer_buffer)) {
 
-               if (status) {
-                       dbg("%s - nonzero write bulk status received: %d",
-                           __func__, status);
-                       spin_lock_irqsave(&garmin_data_p->lock, flags);
-                       garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
-                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+                       if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
+                               gsp_send_ack(garmin_data_p,
+                                       ((__u8 *)urb->transfer_buffer)[4]);
+                       }
                }
-
                usb_serial_port_softint(port);
        }
 
@@ -1108,7 +1061,11 @@ static int garmin_write_bulk(struct usb_serial_port *port,
        urb->transfer_flags |= URB_ZERO_PACKET;
 
        if (GARMIN_LAYERID_APPL == getLayerId(buffer)) {
-               atomic_inc(&garmin_data_p->req_count);
+
+               spin_lock_irqsave(&garmin_data_p->lock, flags);
+               garmin_data_p->flags |= APP_REQ_SEEN;
+               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
                if (garmin_data_p->mode == MODE_GARMIN_SERIAL)  {
                        pkt_clear(garmin_data_p);
                        garmin_data_p->state = STATE_GSP_WAIT_DATA;
@@ -1140,6 +1097,9 @@ static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
 
        usb_serial_debug_data(debug, &port->dev, __func__, count, buf);
 
+       if (garmin_data_p->state == STATE_RESET)
+               return -EIO;
+
        /* check for our private packets */
        if (count >= GARMIN_PKTHDR_LENGTH) {
                len = PRIVPKTSIZ;
@@ -1184,7 +1144,7 @@ static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
                                break;
 
                        case PRIV_PKTID_RESET_REQ:
-                               atomic_inc(&garmin_data_p->req_count);
+                               process_resetdev_request(port);
                                break;
 
                        case PRIV_PKTID_SET_DEF_MODE:
@@ -1200,8 +1160,6 @@ static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
                }
        }
 
-       garmin_data_p->ignorePkts = 0;
-
        if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
                return gsp_receive(garmin_data_p, buf, count);
        } else {        /* MODE_NATIVE */
@@ -1224,31 +1182,33 @@ static int garmin_write_room(struct tty_struct *tty)
 static void garmin_read_process(struct garmin_data *garmin_data_p,
                                 unsigned char *data, unsigned data_length)
 {
+       unsigned long flags;
+
        if (garmin_data_p->flags & FLAGS_DROP_DATA) {
                /* abort-transfer cmd is actice */
                dbg("%s - pkt dropped", __func__);
        } else if (garmin_data_p->state != STATE_DISCONNECTED &&
                garmin_data_p->state != STATE_RESET) {
 
-               /* remember any appl.layer packets, so we know
-                  if a reset is required or not when closing
-                  the device */
-               if (0 == memcmp(data, GARMIN_APP_LAYER_REPLY,
-                               sizeof(GARMIN_APP_LAYER_REPLY))) {
-                       atomic_inc(&garmin_data_p->resp_count);
-               }
-
                /* if throttling is active or postprecessing is required
                   put the received data in the input queue, otherwise
                   send it directly to the tty port */
                if (garmin_data_p->flags & FLAGS_QUEUING) {
                        pkt_add(garmin_data_p, data, data_length);
-               } else if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
-                       if (getLayerId(data) == GARMIN_LAYERID_APPL)
+               } else if (getLayerId(data) == GARMIN_LAYERID_APPL) {
+
+                       spin_lock_irqsave(&garmin_data_p->lock, flags);
+                       garmin_data_p->flags |= APP_RESP_SEEN;
+                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+                       if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
                                pkt_add(garmin_data_p, data, data_length);
-               } else {
-                       send_to_tty(garmin_data_p->port, data, data_length);
+                       } else {
+                               send_to_tty(garmin_data_p->port, data,
+                                               data_length);
+                       }
                }
+               /* ignore system layer packets ... */
        }
 }
 
@@ -1363,8 +1323,6 @@ static void garmin_read_int_callback(struct urb *urb)
                        } else {
                                spin_lock_irqsave(&garmin_data_p->lock, flags);
                                garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE;
-                               /* do not send this packet to the user */
-                               garmin_data_p->ignorePkts = 1;
                                spin_unlock_irqrestore(&garmin_data_p->lock,
                                                                        flags);
                        }
@@ -1391,17 +1349,7 @@ static void garmin_read_int_callback(struct urb *urb)
                        __func__, garmin_data_p->serial_num);
        }
 
-       if (garmin_data_p->ignorePkts) {
-               /* this reply belongs to a request generated by the driver,
-                  ignore it. */
-               dbg("%s - pkt ignored (%d)",
-                       __func__, garmin_data_p->ignorePkts);
-               spin_lock_irqsave(&garmin_data_p->lock, flags);
-               garmin_data_p->ignorePkts--;
-               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
-       } else {
-               garmin_read_process(garmin_data_p, data, urb->actual_length);
-       }
+       garmin_read_process(garmin_data_p, data, urb->actual_length);
 
        port->interrupt_in_urb->dev = port->serial->dev;
        retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -1527,7 +1475,7 @@ static int garmin_attach(struct usb_serial *serial)
 }
 
 
-static void garmin_shutdown(struct usb_serial *serial)
+static void garmin_disconnect(struct usb_serial *serial)
 {
        struct usb_serial_port *port = serial->port[0];
        struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
@@ -1536,8 +1484,17 @@ static void garmin_shutdown(struct usb_serial *serial)
 
        usb_kill_urb(port->interrupt_in_urb);
        del_timer_sync(&garmin_data_p->timer);
+}
+
+
+static void garmin_release(struct usb_serial *serial)
+{
+       struct usb_serial_port *port = serial->port[0];
+       struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+
+       dbg("%s", __func__);
+
        kfree(garmin_data_p);
-       usb_set_serial_port_data(port, NULL);
 }
 
 
@@ -1556,7 +1513,8 @@ static struct usb_serial_driver garmin_device = {
        .throttle            = garmin_throttle,
        .unthrottle          = garmin_unthrottle,
        .attach              = garmin_attach,
-       .shutdown            = garmin_shutdown,
+       .disconnect          = garmin_disconnect,
+       .release             = garmin_release,
        .write               = garmin_write,
        .write_room          = garmin_write_room,
        .write_bulk_callback = garmin_write_bulk_callback,
index be82ea9..932d624 100644 (file)
@@ -63,7 +63,8 @@ struct usb_serial_driver usb_serial_generic_device = {
        .id_table =             generic_device_ids,
        .usb_driver =           &generic_driver,
        .num_ports =            1,
-       .shutdown =             usb_serial_generic_shutdown,
+       .disconnect =           usb_serial_generic_disconnect,
+       .release =              usb_serial_generic_release,
        .throttle =             usb_serial_generic_throttle,
        .unthrottle =           usb_serial_generic_unthrottle,
        .resume =               usb_serial_generic_resume,
@@ -190,6 +191,88 @@ void usb_serial_generic_close(struct usb_serial_port *port)
        generic_cleanup(port);
 }
 
+static int usb_serial_multi_urb_write(struct tty_struct *tty,
+       struct usb_serial_port *port, const unsigned char *buf, int count)
+{
+       unsigned long flags;
+       struct urb *urb;
+       unsigned char *buffer;
+       int status;
+       int towrite;
+       int bwrite = 0;
+
+       dbg("%s - port %d", __func__, port->number);
+
+       if (count == 0)
+               dbg("%s - write request of 0 bytes", __func__);
+
+       while (count > 0) {
+               towrite = (count > port->bulk_out_size) ?
+                       port->bulk_out_size : count;
+               spin_lock_irqsave(&port->lock, flags);
+               if (port->urbs_in_flight >
+                   port->serial->type->max_in_flight_urbs) {
+                       spin_unlock_irqrestore(&port->lock, flags);
+                       dbg("%s - write limit hit\n", __func__);
+                       return bwrite;
+               }
+               port->tx_bytes_flight += towrite;
+               port->urbs_in_flight++;
+               spin_unlock_irqrestore(&port->lock, flags);
+
+               buffer = kmalloc(towrite, GFP_ATOMIC);
+               if (!buffer) {
+                       dev_err(&port->dev,
+                       "%s ran out of kernel memory for urb ...\n", __func__);
+                       goto error_no_buffer;
+               }
+
+               urb = usb_alloc_urb(0, GFP_ATOMIC);
+               if (!urb) {
+                       dev_err(&port->dev, "%s - no more free urbs\n",
+                               __func__);
+                       goto error_no_urb;
+               }
+
+               /* Copy data */
+               memcpy(buffer, buf + bwrite, towrite);
+               usb_serial_debug_data(debug, &port->dev, __func__,
+                                     towrite, buffer);
+               /* fill the buffer and send it */
+               usb_fill_bulk_urb(urb, port->serial->dev,
+                       usb_sndbulkpipe(port->serial->dev,
+                                       port->bulk_out_endpointAddress),
+                       buffer, towrite,
+                       usb_serial_generic_write_bulk_callback, port);
+
+               status = usb_submit_urb(urb, GFP_ATOMIC);
+               if (status) {
+                       dev_err(&port->dev,
+                               "%s - failed submitting write urb, error %d\n",
+                               __func__, status);
+                       goto error;
+               }
+
+               /* This urb is the responsibility of the host driver now */
+               usb_free_urb(urb);
+               dbg("%s write: %d", __func__, towrite);
+               count -= towrite;
+               bwrite += towrite;
+       }
+       return bwrite;
+
+error:
+       usb_free_urb(urb);
+error_no_urb:
+       kfree(buffer);
+error_no_buffer:
+       spin_lock_irqsave(&port->lock, flags);
+       port->urbs_in_flight--;
+       port->tx_bytes_flight -= towrite;
+       spin_unlock_irqrestore(&port->lock, flags);
+       return bwrite;
+}
+
 int usb_serial_generic_write(struct tty_struct *tty,
        struct usb_serial_port *port, const unsigned char *buf, int count)
 {
@@ -207,6 +290,11 @@ int usb_serial_generic_write(struct tty_struct *tty,
        /* only do something if we have a bulk out endpoint */
        if (serial->num_bulk_out) {
                unsigned long flags;
+
+               if (serial->type->max_in_flight_urbs)
+                       return usb_serial_multi_urb_write(tty, port,
+                                                         buf, count);
+
                spin_lock_irqsave(&port->lock, flags);
                if (port->write_urb_busy) {
                        spin_unlock_irqrestore(&port->lock, flags);
@@ -252,20 +340,26 @@ int usb_serial_generic_write(struct tty_struct *tty,
        /* no bulk out, so return 0 bytes written */
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_serial_generic_write);
 
 int usb_serial_generic_write_room(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
        struct usb_serial *serial = port->serial;
+       unsigned long flags;
        int room = 0;
 
        dbg("%s - port %d", __func__, port->number);
-
-       /* FIXME: Locking */
-       if (serial->num_bulk_out) {
-               if (!(port->write_urb_busy))
-                       room = port->bulk_out_size;
+       spin_lock_irqsave(&port->lock, flags);
+       if (serial->type->max_in_flight_urbs) {
+               if (port->urbs_in_flight < serial->type->max_in_flight_urbs)
+                       room = port->bulk_out_size *
+                               (serial->type->max_in_flight_urbs -
+                                port->urbs_in_flight);
+       } else if (serial->num_bulk_out && !(port->write_urb_busy)) {
+               room = port->bulk_out_size;
        }
+       spin_unlock_irqrestore(&port->lock, flags);
 
        dbg("%s - returns %d", __func__, room);
        return room;
@@ -276,11 +370,16 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
        struct usb_serial_port *port = tty->driver_data;
        struct usb_serial *serial = port->serial;
        int chars = 0;
+       unsigned long flags;
 
        dbg("%s - port %d", __func__, port->number);
 
-       /* FIXME: Locking */
-       if (serial->num_bulk_out) {
+       if (serial->type->max_in_flight_urbs) {
+               spin_lock_irqsave(&port->lock, flags);
+               chars = port->tx_bytes_flight;
+               spin_unlock_irqrestore(&port->lock, flags);
+       } else if (serial->num_bulk_out) {
+               /* FIXME: Locking */
                if (port->write_urb_busy)
                        chars = port->write_urb->transfer_buffer_length;
        }
@@ -290,7 +389,8 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
 }
 
 
-static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags)
+void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port,
+                       gfp_t mem_flags)
 {
        struct urb *urb = port->read_urb;
        struct usb_serial *serial = port->serial;
@@ -311,25 +411,28 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags)
                        "%s - failed resubmitting read urb, error %d\n",
                                                        __func__, result);
 }
+EXPORT_SYMBOL_GPL(usb_serial_generic_resubmit_read_urb);
 
 /* Push data to tty layer and resubmit the bulk read URB */
 static void flush_and_resubmit_read_urb(struct usb_serial_port *port)
 {
        struct urb *urb = port->read_urb;
        struct tty_struct *tty = tty_port_tty_get(&port->port);
-       int room;
+       char *ch = (char *)urb->transfer_buffer;
+       int i;
+
+       if (!tty)
+               goto done;
 
        /* Push data to tty */
-       if (tty && urb->actual_length) {
-               room = tty_buffer_request_room(tty, urb->actual_length);
-               if (room) {
-                       tty_insert_flip_string(tty, urb->transfer_buffer, room);
-                       tty_flip_buffer_push(tty);
-               }
+       for (i = 0; i < urb->actual_length; i++, ch++) {
+               if (!usb_serial_handle_sysrq_char(port, *ch))
+                       tty_insert_flip_char(tty, *ch, TTY_NORMAL);
        }
+       tty_flip_buffer_push(tty);
        tty_kref_put(tty);
-
-       resubmit_read_urb(port, GFP_ATOMIC);
+done:
+       usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC);
 }
 
 void usb_serial_generic_read_bulk_callback(struct urb *urb)
@@ -363,12 +466,24 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
 
 void usb_serial_generic_write_bulk_callback(struct urb *urb)
 {
+       unsigned long flags;
        struct usb_serial_port *port = urb->context;
        int status = urb->status;
 
        dbg("%s - port %d", __func__, port->number);
 
-       port->write_urb_busy = 0;
+       if (port->serial->type->max_in_flight_urbs) {
+               spin_lock_irqsave(&port->lock, flags);
+               --port->urbs_in_flight;
+               port->tx_bytes_flight -= urb->transfer_buffer_length;
+               if (port->urbs_in_flight < 0)
+                       port->urbs_in_flight = 0;
+               spin_unlock_irqrestore(&port->lock, flags);
+       } else {
+               /* Handle the case for single urb mode */
+               port->write_urb_busy = 0;
+       }
+
        if (status) {
                dbg("%s - nonzero write bulk status received: %d",
                    __func__, status);
@@ -408,11 +523,36 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty)
 
        if (was_throttled) {
                /* Resume reading from device */
-               resubmit_read_urb(port, GFP_KERNEL);
+               usb_serial_generic_resubmit_read_urb(port, GFP_KERNEL);
+       }
+}
+
+int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
+{
+       if (port->sysrq && port->console) {
+               if (ch && time_before(jiffies, port->sysrq)) {
+                       handle_sysrq(ch, tty_port_tty_get(&port->port));
+                       port->sysrq = 0;
+                       return 1;
+               }
+               port->sysrq = 0;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char);
+
+int usb_serial_handle_break(struct usb_serial_port *port)
+{
+       if (!port->sysrq) {
+               port->sysrq = jiffies + HZ*5;
+               return 1;
        }
+       port->sysrq = 0;
+       return 0;
 }
+EXPORT_SYMBOL_GPL(usb_serial_handle_break);
 
-void usb_serial_generic_shutdown(struct usb_serial *serial)
+void usb_serial_generic_disconnect(struct usb_serial *serial)
 {
        int i;
 
@@ -423,3 +563,7 @@ void usb_serial_generic_shutdown(struct usb_serial *serial)
                generic_cleanup(serial->port[i]);
 }
 
+void usb_serial_generic_release(struct usb_serial *serial)
+{
+       dbg("%s", __func__);
+}
index 53ef599..0191693 100644 (file)
@@ -224,7 +224,8 @@ static int  edge_tiocmget(struct tty_struct *tty, struct file *file);
 static int  edge_tiocmset(struct tty_struct *tty, struct file *file,
                                        unsigned int set, unsigned int clear);
 static int  edge_startup(struct usb_serial *serial);
-static void edge_shutdown(struct usb_serial *serial);
+static void edge_disconnect(struct usb_serial *serial);
+static void edge_release(struct usb_serial *serial);
 
 #include "io_tables.h" /* all of the devices that this driver supports */
 
@@ -3193,21 +3194,16 @@ static int edge_startup(struct usb_serial *serial)
 
 
 /****************************************************************************
- * edge_shutdown
+ * edge_disconnect
  *     This function is called whenever the device is removed from the usb bus.
  ****************************************************************************/
-static void edge_shutdown(struct usb_serial *serial)
+static void edge_disconnect(struct usb_serial *serial)
 {
        struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
-       int i;
 
        dbg("%s", __func__);
 
        /* stop reads and writes on all ports */
-       for (i = 0; i < serial->num_ports; ++i) {
-               kfree(usb_get_serial_port_data(serial->port[i]));
-               usb_set_serial_port_data(serial->port[i],  NULL);
-       }
        /* free up our endpoint stuff */
        if (edge_serial->is_epic) {
                usb_kill_urb(edge_serial->interrupt_read_urb);
@@ -3218,9 +3214,24 @@ static void edge_shutdown(struct usb_serial *serial)
                usb_free_urb(edge_serial->read_urb);
                kfree(edge_serial->bulk_in_buffer);
        }
+}
+
+
+/****************************************************************************
+ * edge_release
+ *     This function is called when the device structure is deallocated.
+ ****************************************************************************/
+static void edge_release(struct usb_serial *serial)
+{
+       struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+       int i;
+
+       dbg("%s", __func__);
+
+       for (i = 0; i < serial->num_ports; ++i)
+               kfree(usb_get_serial_port_data(serial->port[i]));
 
        kfree(edge_serial);
-       usb_set_serial_data(serial, NULL);
 }
 
 
index 7eb9d67..9241d31 100644 (file)
@@ -117,7 +117,8 @@ static struct usb_serial_driver edgeport_2port_device = {
        .throttle               = edge_throttle,
        .unthrottle             = edge_unthrottle,
        .attach                 = edge_startup,
-       .shutdown               = edge_shutdown,
+       .disconnect             = edge_disconnect,
+       .release                = edge_release,
        .ioctl                  = edge_ioctl,
        .set_termios            = edge_set_termios,
        .tiocmget               = edge_tiocmget,
@@ -145,7 +146,8 @@ static struct usb_serial_driver edgeport_4port_device = {
        .throttle               = edge_throttle,
        .unthrottle             = edge_unthrottle,
        .attach                 = edge_startup,
-       .shutdown               = edge_shutdown,
+       .disconnect             = edge_disconnect,
+       .release                = edge_release,
        .ioctl                  = edge_ioctl,
        .set_termios            = edge_set_termios,
        .tiocmget               = edge_tiocmget,
@@ -173,7 +175,8 @@ static struct usb_serial_driver edgeport_8port_device = {
        .throttle               = edge_throttle,
        .unthrottle             = edge_unthrottle,
        .attach                 = edge_startup,
-       .shutdown               = edge_shutdown,
+       .disconnect             = edge_disconnect,
+       .release                = edge_release,
        .ioctl                  = edge_ioctl,
        .set_termios            = edge_set_termios,
        .tiocmget               = edge_tiocmget,
@@ -200,7 +203,8 @@ static struct usb_serial_driver epic_device = {
        .throttle               = edge_throttle,
        .unthrottle             = edge_unthrottle,
        .attach                 = edge_startup,
-       .shutdown               = edge_shutdown,
+       .disconnect             = edge_disconnect,
+       .release                = edge_release,
        .ioctl                  = edge_ioctl,
        .set_termios            = edge_set_termios,
        .tiocmget               = edge_tiocmget,
index db964db..e8bc42f 100644 (file)
@@ -2663,7 +2663,7 @@ cleanup:
        return -ENOMEM;
 }
 
-static void edge_shutdown(struct usb_serial *serial)
+static void edge_disconnect(struct usb_serial *serial)
 {
        int i;
        struct edgeport_port *edge_port;
@@ -2673,12 +2673,22 @@ static void edge_shutdown(struct usb_serial *serial)
        for (i = 0; i < serial->num_ports; ++i) {
                edge_port = usb_get_serial_port_data(serial->port[i]);
                edge_remove_sysfs_attrs(edge_port->port);
+       }
+}
+
+static void edge_release(struct usb_serial *serial)
+{
+       int i;
+       struct edgeport_port *edge_port;
+
+       dbg("%s", __func__);
+
+       for (i = 0; i < serial->num_ports; ++i) {
+               edge_port = usb_get_serial_port_data(serial->port[i]);
                edge_buf_free(edge_port->ep_out_buf);
                kfree(edge_port);
-               usb_set_serial_port_data(serial->port[i], NULL);
        }
        kfree(usb_get_serial_data(serial));
-       usb_set_serial_data(serial, NULL);
 }
 
 
@@ -2915,7 +2925,8 @@ static struct usb_serial_driver edgeport_1port_device = {
        .throttle               = edge_throttle,
        .unthrottle             = edge_unthrottle,
        .attach                 = edge_startup,
-       .shutdown               = edge_shutdown,
+       .disconnect             = edge_disconnect,
+       .release                = edge_release,
        .port_probe             = edge_create_sysfs_attrs,
        .ioctl                  = edge_ioctl,
        .set_termios            = edge_set_termios,
@@ -2944,7 +2955,8 @@ static struct usb_serial_driver edgeport_2port_device = {
        .throttle               = edge_throttle,
        .unthrottle             = edge_unthrottle,
        .attach                 = edge_startup,
-       .shutdown               = edge_shutdown,
+       .disconnect             = edge_disconnect,
+       .release                = edge_release,
        .port_probe             = edge_create_sysfs_attrs,
        .ioctl                  = edge_ioctl,
        .set_termios            = edge_set_termios,
index c610a99..2545d45 100644 (file)
@@ -79,7 +79,6 @@ static int  ipaq_open(struct tty_struct *tty,
 static void ipaq_close(struct usb_serial_port *port);
 static int  ipaq_calc_num_ports(struct usb_serial *serial);
 static int  ipaq_startup(struct usb_serial *serial);
-static void ipaq_shutdown(struct usb_serial *serial);
 static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port,
                        const unsigned char *buf, int count);
 static int ipaq_write_bulk(struct usb_serial_port *port,
@@ -576,7 +575,6 @@ static struct usb_serial_driver ipaq_device = {
        .close =                ipaq_close,
        .attach =               ipaq_startup,
        .calc_num_ports =       ipaq_calc_num_ports,
-       .shutdown =             ipaq_shutdown,
        .write =                ipaq_write,
        .write_room =           ipaq_write_room,
        .chars_in_buffer =      ipaq_chars_in_buffer,
@@ -990,11 +988,6 @@ static int ipaq_startup(struct usb_serial *serial)
        return usb_reset_configuration(serial->dev);
 }
 
-static void ipaq_shutdown(struct usb_serial *serial)
-{
-       dbg("%s", __func__);
-}
-
 static int __init ipaq_init(void)
 {
        int retval;
index 76a3cc3..96873a7 100644 (file)
@@ -121,8 +121,8 @@ static int iuu_startup(struct usb_serial *serial)
        return 0;
 }
 
-/* Shutdown function */
-static void iuu_shutdown(struct usb_serial *serial)
+/* Release function */
+static void iuu_release(struct usb_serial *serial)
 {
        struct usb_serial_port *port = serial->port[0];
        struct iuu_private *priv = usb_get_serial_port_data(port);
@@ -1202,7 +1202,7 @@ static struct usb_serial_driver iuu_device = {
        .tiocmset = iuu_tiocmset,
        .set_termios = iuu_set_termios,
        .attach = iuu_startup,
-       .shutdown = iuu_shutdown,
+       .release = iuu_release,
 };
 
 static int __init iuu_init(void)
index f1195a9..2594b87 100644 (file)
@@ -2689,7 +2689,7 @@ static int keyspan_startup(struct usb_serial *serial)
        return 0;
 }
 
-static void keyspan_shutdown(struct usb_serial *serial)
+static void keyspan_disconnect(struct usb_serial *serial)
 {
        int                             i, j;
        struct usb_serial_port          *port;
@@ -2729,6 +2729,17 @@ static void keyspan_shutdown(struct usb_serial *serial)
                        usb_free_urb(p_priv->out_urbs[j]);
                }
        }
+}
+
+static void keyspan_release(struct usb_serial *serial)
+{
+       int                             i;
+       struct usb_serial_port          *port;
+       struct keyspan_serial_private   *s_priv;
+
+       dbg("%s", __func__);
+
+       s_priv = usb_get_serial_data(serial);
 
        /*  dbg("Freeing serial->private."); */
        kfree(s_priv);
index 0d4569b..3107ed1 100644 (file)
@@ -41,7 +41,8 @@ static int  keyspan_open              (struct tty_struct *tty,
 static void keyspan_close              (struct usb_serial_port *port);
 static void keyspan_dtr_rts            (struct usb_serial_port *port, int on);
 static int  keyspan_startup            (struct usb_serial *serial);
-static void keyspan_shutdown           (struct usb_serial *serial);
+static void keyspan_disconnect         (struct usb_serial *serial);
+static void keyspan_release            (struct usb_serial *serial);
 static int  keyspan_write_room         (struct tty_struct *tty);
 
 static int  keyspan_write              (struct tty_struct *tty,
@@ -569,7 +570,8 @@ static struct usb_serial_driver keyspan_1port_device = {
        .tiocmget               = keyspan_tiocmget,
        .tiocmset               = keyspan_tiocmset,
        .attach                 = keyspan_startup,
-       .shutdown               = keyspan_shutdown,
+       .disconnect             = keyspan_disconnect,
+       .release                = keyspan_release,
 };
 
 static struct usb_serial_driver keyspan_2port_device = {
@@ -590,7 +592,8 @@ static struct usb_serial_driver keyspan_2port_device = {
        .tiocmget               = keyspan_tiocmget,
        .tiocmset               = keyspan_tiocmset,
        .attach                 = keyspan_startup,
-       .shutdown               = keyspan_shutdown,
+       .disconnect             = keyspan_disconnect,
+       .release                = keyspan_release,
 };
 
 static struct usb_serial_driver keyspan_4port_device = {
@@ -611,7 +614,8 @@ static struct usb_serial_driver keyspan_4port_device = {
        .tiocmget               = keyspan_tiocmget,
        .tiocmset               = keyspan_tiocmset,
        .attach                 = keyspan_startup,
-       .shutdown               = keyspan_shutdown,
+       .disconnect             = keyspan_disconnect,
+       .release                = keyspan_release,
 };
 
 #endif
index ab769db..d0b12e4 100644 (file)
@@ -809,7 +809,7 @@ static int keyspan_pda_startup(struct usb_serial *serial)
        return 0;
 }
 
-static void keyspan_pda_shutdown(struct usb_serial *serial)
+static void keyspan_pda_release(struct usb_serial *serial)
 {
        dbg("%s", __func__);
 
@@ -869,7 +869,7 @@ static struct usb_serial_driver keyspan_pda_device = {
        .tiocmget =             keyspan_pda_tiocmget,
        .tiocmset =             keyspan_pda_tiocmset,
        .attach =               keyspan_pda_startup,
-       .shutdown =             keyspan_pda_shutdown,
+       .release =              keyspan_pda_release,
 };
 
 
index fa817c6..0f44bb8 100644 (file)
@@ -73,7 +73,8 @@ static int debug;
  * Function prototypes
  */
 static int  klsi_105_startup(struct usb_serial *serial);
-static void klsi_105_shutdown(struct usb_serial *serial);
+static void klsi_105_disconnect(struct usb_serial *serial);
+static void klsi_105_release(struct usb_serial *serial);
 static int  klsi_105_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp);
 static void klsi_105_close(struct usb_serial_port *port);
@@ -131,7 +132,8 @@ static struct usb_serial_driver kl5kusb105d_device = {
        .tiocmget =          klsi_105_tiocmget,
        .tiocmset =          klsi_105_tiocmset,
        .attach =            klsi_105_startup,
-       .shutdown =          klsi_105_shutdown,
+       .disconnect =        klsi_105_disconnect,
+       .release =           klsi_105_release,
        .throttle =          klsi_105_throttle,
        .unthrottle =        klsi_105_unthrottle,
 };
@@ -315,7 +317,7 @@ err_cleanup:
 } /* klsi_105_startup */
 
 
-static void klsi_105_shutdown(struct usb_serial *serial)
+static void klsi_105_disconnect(struct usb_serial *serial)
 {
        int i;
 
@@ -325,33 +327,36 @@ static void klsi_105_shutdown(struct usb_serial *serial)
        for (i = 0; i < serial->num_ports; ++i) {
                struct klsi_105_private *priv =
                                usb_get_serial_port_data(serial->port[i]);
-               unsigned long flags;
 
                if (priv) {
                        /* kill our write urb pool */
                        int j;
                        struct urb **write_urbs = priv->write_urb_pool;
-                       spin_lock_irqsave(&priv->lock, flags);
 
                        for (j = 0; j < NUM_URBS; j++) {
                                if (write_urbs[j]) {
-                                       /* FIXME - uncomment the following
-                                        * usb_kill_urb call when the host
-                                        * controllers get fixed to set
-                                        * urb->dev = NULL after the urb is
-                                        * finished.  Otherwise this call
-                                        * oopses. */
-                                       /* usb_kill_urb(write_urbs[j]); */
-                                       kfree(write_urbs[j]->transfer_buffer);
+                                       usb_kill_urb(write_urbs[j]);
                                        usb_free_urb(write_urbs[j]);
                                }
                        }
-                       spin_unlock_irqrestore(&priv->lock, flags);
-                       kfree(priv);
-                       usb_set_serial_port_data(serial->port[i], NULL);
                }
        }
-} /* klsi_105_shutdown */
+} /* klsi_105_disconnect */
+
+
+static void klsi_105_release(struct usb_serial *serial)
+{
+       int i;
+
+       dbg("%s", __func__);
+
+       for (i = 0; i < serial->num_ports; ++i) {
+               struct klsi_105_private *priv =
+                               usb_get_serial_port_data(serial->port[i]);
+
+               kfree(priv);
+       }
+} /* klsi_105_release */
 
 static int  klsi_105_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp)
index 6b57049..6db0e56 100644 (file)
@@ -69,7 +69,7 @@ static int debug;
 
 /* Function prototypes */
 static int  kobil_startup(struct usb_serial *serial);
-static void kobil_shutdown(struct usb_serial *serial);
+static void kobil_release(struct usb_serial *serial);
 static int  kobil_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp);
 static void kobil_close(struct usb_serial_port *port);
@@ -117,7 +117,7 @@ static struct usb_serial_driver kobil_device = {
        .id_table =             id_table,
        .num_ports =            1,
        .attach =               kobil_startup,
-       .shutdown =             kobil_shutdown,
+       .release =              kobil_release,
        .ioctl =                kobil_ioctl,
        .set_termios =          kobil_set_termios,
        .tiocmget =             kobil_tiocmget,
@@ -201,17 +201,13 @@ static int kobil_startup(struct usb_serial *serial)
 }
 
 
-static void kobil_shutdown(struct usb_serial *serial)
+static void kobil_release(struct usb_serial *serial)
 {
        int i;
        dbg("%s - port %d", __func__, serial->port[0]->number);
 
-       for (i = 0; i < serial->num_ports; ++i) {
-               while (serial->port[i]->port.count > 0)
-                       kobil_close(serial->port[i]);
+       for (i = 0; i < serial->num_ports; ++i)
                kfree(usb_get_serial_port_data(serial->port[i]));
-               usb_set_serial_port_data(serial->port[i], NULL);
-       }
 }
 
 
index 8737955..d8825e1 100644 (file)
@@ -92,7 +92,7 @@ static int debug;
  * Function prototypes
  */
 static int  mct_u232_startup(struct usb_serial *serial);
-static void mct_u232_shutdown(struct usb_serial *serial);
+static void mct_u232_release(struct usb_serial *serial);
 static int  mct_u232_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp);
 static void mct_u232_close(struct usb_serial_port *port);
@@ -149,7 +149,7 @@ static struct usb_serial_driver mct_u232_device = {
        .tiocmget =          mct_u232_tiocmget,
        .tiocmset =          mct_u232_tiocmset,
        .attach =            mct_u232_startup,
-       .shutdown =          mct_u232_shutdown,
+       .release =           mct_u232_release,
 };
 
 
@@ -407,7 +407,7 @@ static int mct_u232_startup(struct usb_serial *serial)
 } /* mct_u232_startup */
 
 
-static void mct_u232_shutdown(struct usb_serial *serial)
+static void mct_u232_release(struct usb_serial *serial)
 {
        struct mct_u232_private *priv;
        int i;
@@ -417,12 +417,9 @@ static void mct_u232_shutdown(struct usb_serial *serial)
        for (i = 0; i < serial->num_ports; ++i) {
                /* My special items, the standard routines free my urbs */
                priv = usb_get_serial_port_data(serial->port[i]);
-               if (priv) {
-                       usb_set_serial_port_data(serial->port[i], NULL);
-                       kfree(priv);
-               }
+               kfree(priv);
        }
-} /* mct_u232_shutdown */
+} /* mct_u232_release */
 
 static int  mct_u232_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp)
index 9e1a013..bfc5ce0 100644 (file)
@@ -1521,19 +1521,16 @@ static int mos7720_startup(struct usb_serial *serial)
        return 0;
 }
 
-static void mos7720_shutdown(struct usb_serial *serial)
+static void mos7720_release(struct usb_serial *serial)
 {
        int i;
 
        /* free private structure allocated for serial port */
-       for (i = 0; i < serial->num_ports; ++i) {
+       for (i = 0; i < serial->num_ports; ++i)
                kfree(usb_get_serial_port_data(serial->port[i]));
-               usb_set_serial_port_data(serial->port[i], NULL);
-       }
 
        /* free private structure allocated for serial device */
        kfree(usb_get_serial_data(serial));
-       usb_set_serial_data(serial, NULL);
 }
 
 static struct usb_driver usb_driver = {
@@ -1558,7 +1555,7 @@ static struct usb_serial_driver moschip7720_2port_driver = {
        .throttle               = mos7720_throttle,
        .unthrottle             = mos7720_unthrottle,
        .attach                 = mos7720_startup,
-       .shutdown               = mos7720_shutdown,
+       .release                = mos7720_release,
        .ioctl                  = mos7720_ioctl,
        .set_termios            = mos7720_set_termios,
        .write                  = mos7720_write,
index 10b78a3..c40f95c 100644 (file)
@@ -238,7 +238,7 @@ static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg,
 {
        struct usb_device *dev = port->serial->dev;
        val = val & 0x00ff;
-       dbg("mos7840_set_reg_sync offset is %x, value %x\n", reg, val);
+       dbg("mos7840_set_reg_sync offset is %x, value %x", reg, val);
 
        return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
                               MCS_WR_RTYPE, val, reg, NULL, 0,
@@ -260,7 +260,7 @@ static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
        ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
                              MCS_RD_RTYPE, 0, reg, val, VENDOR_READ_LENGTH,
                              MOS_WDR_TIMEOUT);
-       dbg("mos7840_get_reg_sync offset is %x, return val %x\n", reg, *val);
+       dbg("mos7840_get_reg_sync offset is %x, return val %x", reg, *val);
        *val = (*val) & 0x00ff;
        return ret;
 }
@@ -282,18 +282,18 @@ static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
        if (port->serial->num_ports == 4) {
                val |= (((__u16) port->number -
                                (__u16) (port->serial->minor)) + 1) << 8;
-               dbg("mos7840_set_uart_reg application number is %x\n", val);
+               dbg("mos7840_set_uart_reg application number is %x", val);
        } else {
                if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
                        val |= (((__u16) port->number -
                              (__u16) (port->serial->minor)) + 1) << 8;
-                       dbg("mos7840_set_uart_reg application number is %x\n",
+                       dbg("mos7840_set_uart_reg application number is %x",
                            val);
                } else {
                        val |=
                            (((__u16) port->number -
                              (__u16) (port->serial->minor)) + 2) << 8;
-                       dbg("mos7840_set_uart_reg application number is %x\n",
+                       dbg("mos7840_set_uart_reg application number is %x",
                            val);
                }
        }
@@ -315,24 +315,24 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
        int ret = 0;
        __u16 Wval;
 
-       /* dbg("application number is %4x \n",
+       /* dbg("application number is %4x",
            (((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); */
        /* Wval  is same as application number */
        if (port->serial->num_ports == 4) {
                Wval =
                    (((__u16) port->number - (__u16) (port->serial->minor)) +
                     1) << 8;
-               dbg("mos7840_get_uart_reg application number is %x\n", Wval);
+               dbg("mos7840_get_uart_reg application number is %x", Wval);
        } else {
                if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
                        Wval = (((__u16) port->number -
                              (__u16) (port->serial->minor)) + 1) << 8;
-                       dbg("mos7840_get_uart_reg application number is %x\n",
+                       dbg("mos7840_get_uart_reg application number is %x",
                            Wval);
                } else {
                        Wval = (((__u16) port->number -
                              (__u16) (port->serial->minor)) + 2) << 8;
-                       dbg("mos7840_get_uart_reg application number is %x\n",
+                       dbg("mos7840_get_uart_reg application number is %x",
                            Wval);
                }
        }
@@ -346,11 +346,11 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
 static void mos7840_dump_serial_port(struct moschip_port *mos7840_port)
 {
 
-       dbg("***************************************\n");
-       dbg("SpRegOffset is %2x\n", mos7840_port->SpRegOffset);
-       dbg("ControlRegOffset is %2x \n", mos7840_port->ControlRegOffset);
-       dbg("DCRRegOffset is %2x \n", mos7840_port->DcrRegOffset);
-       dbg("***************************************\n");
+       dbg("***************************************");
+       dbg("SpRegOffset is %2x", mos7840_port->SpRegOffset);
+       dbg("ControlRegOffset is %2x", mos7840_port->ControlRegOffset);
+       dbg("DCRRegOffset is %2x", mos7840_port->DcrRegOffset);
+       dbg("***************************************");
 
 }
 
@@ -474,12 +474,12 @@ static void mos7840_control_callback(struct urb *urb)
                goto exit;
        }
 
-       dbg("%s urb buffer size is %d\n", __func__, urb->actual_length);
-       dbg("%s mos7840_port->MsrLsr is %d port %d\n", __func__,
+       dbg("%s urb buffer size is %d", __func__, urb->actual_length);
+       dbg("%s mos7840_port->MsrLsr is %d port %d", __func__,
            mos7840_port->MsrLsr, mos7840_port->port_num);
        data = urb->transfer_buffer;
        regval = (__u8) data[0];
-       dbg("%s data is %x\n", __func__, regval);
+       dbg("%s data is %x", __func__, regval);
        if (mos7840_port->MsrLsr == 0)
                mos7840_handle_new_msr(mos7840_port, regval);
        else if (mos7840_port->MsrLsr == 1)
@@ -538,7 +538,7 @@ static void mos7840_interrupt_callback(struct urb *urb)
        __u16 wval, wreg = 0;
        int status = urb->status;
 
-       dbg("%s", " : Entering\n");
+       dbg("%s", " : Entering");
 
        switch (status) {
        case 0:
@@ -570,7 +570,7 @@ static void mos7840_interrupt_callback(struct urb *urb)
         * Byte 5 FIFO status for both */
 
        if (length && length > 5) {
-               dbg("%s \n", "Wrong data !!!");
+               dbg("%s", "Wrong data !!!");
                return;
        }
 
@@ -587,17 +587,17 @@ static void mos7840_interrupt_callback(struct urb *urb)
                      (__u16) (serial->minor)) + 1) << 8;
                if (mos7840_port->open) {
                        if (sp[i] & 0x01) {
-                               dbg("SP%d No Interrupt !!!\n", i);
+                               dbg("SP%d No Interrupt !!!", i);
                        } else {
                                switch (sp[i] & 0x0f) {
                                case SERIAL_IIR_RLS:
                                        dbg("Serial Port %d: Receiver status error or ", i);
-                                       dbg("address bit detected in 9-bit mode\n");
+                                       dbg("address bit detected in 9-bit mode");
                                        mos7840_port->MsrLsr = 1;
                                        wreg = LINE_STATUS_REGISTER;
                                        break;
                                case SERIAL_IIR_MS:
-                                       dbg("Serial Port %d: Modem status change\n", i);
+                                       dbg("Serial Port %d: Modem status change", i);
                                        mos7840_port->MsrLsr = 0;
                                        wreg = MODEM_STATUS_REGISTER;
                                        break;
@@ -689,7 +689,7 @@ static void mos7840_bulk_in_callback(struct urb *urb)
 
        mos7840_port = urb->context;
        if (!mos7840_port) {
-               dbg("%s", "NULL mos7840_port pointer \n");
+               dbg("%s", "NULL mos7840_port pointer");
                mos7840_port->read_urb_busy = false;
                return;
        }
@@ -702,41 +702,41 @@ static void mos7840_bulk_in_callback(struct urb *urb)
 
        port = (struct usb_serial_port *)mos7840_port->port;
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Port Paranoia failed \n");
+               dbg("%s", "Port Paranoia failed");
                mos7840_port->read_urb_busy = false;
                return;
        }
 
        serial = mos7840_get_usb_serial(port, __func__);
        if (!serial) {
-               dbg("%s\n", "Bad serial pointer ");
+               dbg("%s", "Bad serial pointer");
                mos7840_port->read_urb_busy = false;
                return;
        }
 
-       dbg("%s\n", "Entering... \n");
+       dbg("%s", "Entering... ");
 
        data = urb->transfer_buffer;
 
-       dbg("%s", "Entering ........... \n");
+       dbg("%s", "Entering ...........");
 
        if (urb->actual_length) {
                tty = tty_port_tty_get(&mos7840_port->port->port);
                if (tty) {
                        tty_buffer_request_room(tty, urb->actual_length);
                        tty_insert_flip_string(tty, data, urb->actual_length);
-                       dbg(" %s \n", data);
+                       dbg(" %s ", data);
                        tty_flip_buffer_push(tty);
                        tty_kref_put(tty);
                }
                mos7840_port->icount.rx += urb->actual_length;
                smp_wmb();
-               dbg("mos7840_port->icount.rx is %d:\n",
+               dbg("mos7840_port->icount.rx is %d:",
                    mos7840_port->icount.rx);
        }
 
        if (!mos7840_port->read_urb) {
-               dbg("%s", "URB KILLED !!!\n");
+               dbg("%s", "URB KILLED !!!");
                mos7840_port->read_urb_busy = false;
                return;
        }
@@ -777,16 +777,16 @@ static void mos7840_bulk_out_data_callback(struct urb *urb)
        spin_unlock(&mos7840_port->pool_lock);
 
        if (status) {
-               dbg("nonzero write bulk status received:%d\n", status);
+               dbg("nonzero write bulk status received:%d", status);
                return;
        }
 
        if (mos7840_port_paranoia_check(mos7840_port->port, __func__)) {
-               dbg("%s", "Port Paranoia failed \n");
+               dbg("%s", "Port Paranoia failed");
                return;
        }
 
-       dbg("%s \n", "Entering .........");
+       dbg("%s", "Entering .........");
 
        tty = tty_port_tty_get(&mos7840_port->port->port);
        if (tty && mos7840_port->open)
@@ -830,15 +830,17 @@ static int mos7840_open(struct tty_struct *tty,
        struct moschip_port *mos7840_port;
        struct moschip_port *port0;
 
+       dbg ("%s enter", __func__);
+
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Port Paranoia failed \n");
+               dbg("%s", "Port Paranoia failed");
                return -ENODEV;
        }
 
        serial = port->serial;
 
        if (mos7840_serial_paranoia_check(serial, __func__)) {
-               dbg("%s", "Serial Paranoia failed \n");
+               dbg("%s", "Serial Paranoia failed");
                return -ENODEV;
        }
 
@@ -891,20 +893,20 @@ static int mos7840_open(struct tty_struct *tty,
        Data = 0x0;
        status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
        if (status < 0) {
-               dbg("Reading Spreg failed\n");
+               dbg("Reading Spreg failed");
                return -1;
        }
        Data |= 0x80;
        status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
        if (status < 0) {
-               dbg("writing Spreg failed\n");
+               dbg("writing Spreg failed");
                return -1;
        }
 
        Data &= ~0x80;
        status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
        if (status < 0) {
-               dbg("writing Spreg failed\n");
+               dbg("writing Spreg failed");
                return -1;
        }
        /* End of block to be checked */
@@ -913,7 +915,7 @@ static int mos7840_open(struct tty_struct *tty,
        status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
                                                                        &Data);
        if (status < 0) {
-               dbg("Reading Controlreg failed\n");
+               dbg("Reading Controlreg failed");
                return -1;
        }
        Data |= 0x08;           /* Driver done bit */
@@ -921,7 +923,7 @@ static int mos7840_open(struct tty_struct *tty,
        status = mos7840_set_reg_sync(port,
                                mos7840_port->ControlRegOffset, Data);
        if (status < 0) {
-               dbg("writing Controlreg failed\n");
+               dbg("writing Controlreg failed");
                return -1;
        }
        /* do register settings here */
@@ -932,21 +934,21 @@ static int mos7840_open(struct tty_struct *tty,
        Data = 0x00;
        status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
        if (status < 0) {
-               dbg("disableing interrupts failed\n");
+               dbg("disabling interrupts failed");
                return -1;
        }
        /* Set FIFO_CONTROL_REGISTER to the default value */
        Data = 0x00;
        status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
        if (status < 0) {
-               dbg("Writing FIFO_CONTROL_REGISTER  failed\n");
+               dbg("Writing FIFO_CONTROL_REGISTER  failed");
                return -1;
        }
 
        Data = 0xcf;
        status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
        if (status < 0) {
-               dbg("Writing FIFO_CONTROL_REGISTER  failed\n");
+               dbg("Writing FIFO_CONTROL_REGISTER  failed");
                return -1;
        }
 
@@ -1043,12 +1045,12 @@ static int mos7840_open(struct tty_struct *tty,
         * (can't set it up in mos7840_startup as the  *
         * structures were not set up at that time.)   */
 
-       dbg("port number is %d \n", port->number);
-       dbg("serial number is %d \n", port->serial->minor);
-       dbg("Bulkin endpoint is %d \n", port->bulk_in_endpointAddress);
-       dbg("BulkOut endpoint is %d \n", port->bulk_out_endpointAddress);
-       dbg("Interrupt endpoint is %d \n", port->interrupt_in_endpointAddress);
-       dbg("port's number in the device is %d\n", mos7840_port->port_num);
+       dbg("port number is %d", port->number);
+       dbg("serial number is %d", port->serial->minor);
+       dbg("Bulkin endpoint is %d", port->bulk_in_endpointAddress);
+       dbg("BulkOut endpoint is %d", port->bulk_out_endpointAddress);
+       dbg("Interrupt endpoint is %d", port->interrupt_in_endpointAddress);
+       dbg("port's number in the device is %d", mos7840_port->port_num);
        mos7840_port->read_urb = port->read_urb;
 
        /* set up our bulk in urb */
@@ -1061,7 +1063,7 @@ static int mos7840_open(struct tty_struct *tty,
                          mos7840_port->read_urb->transfer_buffer_length,
                          mos7840_bulk_in_callback, mos7840_port);
 
-       dbg("mos7840_open: bulkin endpoint is %d\n",
+       dbg("mos7840_open: bulkin endpoint is %d",
            port->bulk_in_endpointAddress);
        mos7840_port->read_urb_busy = true;
        response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
@@ -1087,9 +1089,11 @@ static int mos7840_open(struct tty_struct *tty,
        mos7840_port->icount.tx = 0;
        mos7840_port->icount.rx = 0;
 
-       dbg("\n\nusb_serial serial:%p       mos7840_port:%p\n      usb_serial_port port:%p\n\n",
+       dbg("usb_serial serial:%p       mos7840_port:%p\n      usb_serial_port port:%p",
                                serial, mos7840_port, port);
 
+       dbg ("%s leave", __func__);
+
        return 0;
 
 }
@@ -1112,16 +1116,16 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty)
        unsigned long flags;
        struct moschip_port *mos7840_port;
 
-       dbg("%s \n", " mos7840_chars_in_buffer:entering ...........");
+       dbg("%s", " mos7840_chars_in_buffer:entering ...........");
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
+               dbg("%s", "Invalid port");
                return 0;
        }
 
        mos7840_port = mos7840_get_port_private(port);
        if (mos7840_port == NULL) {
-               dbg("%s \n", "mos7840_break:leaving ...........");
+               dbg("%s", "mos7840_break:leaving ...........");
                return 0;
        }
 
@@ -1148,16 +1152,16 @@ static void mos7840_close(struct usb_serial_port *port)
        int j;
        __u16 Data;
 
-       dbg("%s\n", "mos7840_close:entering...");
+       dbg("%s", "mos7840_close:entering...");
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Port Paranoia failed \n");
+               dbg("%s", "Port Paranoia failed");
                return;
        }
 
        serial = mos7840_get_usb_serial(port, __func__);
        if (!serial) {
-               dbg("%s", "Serial Paranoia failed \n");
+               dbg("%s", "Serial Paranoia failed");
                return;
        }
 
@@ -1185,27 +1189,27 @@ static void mos7840_close(struct usb_serial_port *port)
         * and interrupt read if they exists                  */
        if (serial->dev) {
                if (mos7840_port->write_urb) {
-                       dbg("%s", "Shutdown bulk write\n");
+                       dbg("%s", "Shutdown bulk write");
                        usb_kill_urb(mos7840_port->write_urb);
                }
                if (mos7840_port->read_urb) {
-                       dbg("%s", "Shutdown bulk read\n");
+                       dbg("%s", "Shutdown bulk read");
                        usb_kill_urb(mos7840_port->read_urb);
                        mos7840_port->read_urb_busy = false;
                }
                if ((&mos7840_port->control_urb)) {
-                       dbg("%s", "Shutdown control read\n");
+                       dbg("%s", "Shutdown control read");
                        /*/      usb_kill_urb (mos7840_port->control_urb); */
                }
        }
 /*      if(mos7840_port->ctrl_buf != NULL) */
 /*              kfree(mos7840_port->ctrl_buf); */
        port0->open_ports--;
-       dbg("mos7840_num_open_ports in close%d:in port%d\n",
+       dbg("mos7840_num_open_ports in close%d:in port%d",
            port0->open_ports, port->number);
        if (port0->open_ports == 0) {
                if (serial->port[0]->interrupt_in_urb) {
-                       dbg("%s", "Shutdown interrupt_in_urb\n");
+                       dbg("%s", "Shutdown interrupt_in_urb");
                        usb_kill_urb(serial->port[0]->interrupt_in_urb);
                }
        }
@@ -1225,7 +1229,7 @@ static void mos7840_close(struct usb_serial_port *port)
 
        mos7840_port->open = 0;
 
-       dbg("%s \n", "Leaving ............");
+       dbg("%s", "Leaving ............");
 }
 
 /************************************************************************
@@ -1280,17 +1284,17 @@ static void mos7840_break(struct tty_struct *tty, int break_state)
        struct usb_serial *serial;
        struct moschip_port *mos7840_port;
 
-       dbg("%s \n", "Entering ...........");
-       dbg("mos7840_break: Start\n");
+       dbg("%s", "Entering ...........");
+       dbg("mos7840_break: Start");
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Port Paranoia failed \n");
+               dbg("%s", "Port Paranoia failed");
                return;
        }
 
        serial = mos7840_get_usb_serial(port, __func__);
        if (!serial) {
-               dbg("%s", "Serial Paranoia failed \n");
+               dbg("%s", "Serial Paranoia failed");
                return;
        }
 
@@ -1310,7 +1314,7 @@ static void mos7840_break(struct tty_struct *tty, int break_state)
 
        /* FIXME: no locking on shadowLCR anywhere in driver */
        mos7840_port->shadowLCR = data;
-       dbg("mcs7840_break mos7840_port->shadowLCR is %x\n",
+       dbg("mcs7840_break mos7840_port->shadowLCR is %x",
            mos7840_port->shadowLCR);
        mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
                             mos7840_port->shadowLCR);
@@ -1334,17 +1338,17 @@ static int mos7840_write_room(struct tty_struct *tty)
        unsigned long flags;
        struct moschip_port *mos7840_port;
 
-       dbg("%s \n", " mos7840_write_room:entering ...........");
+       dbg("%s", " mos7840_write_room:entering ...........");
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
-               dbg("%s \n", " mos7840_write_room:leaving ...........");
+               dbg("%s", "Invalid port");
+               dbg("%s", " mos7840_write_room:leaving ...........");
                return -1;
        }
 
        mos7840_port = mos7840_get_port_private(port);
        if (mos7840_port == NULL) {
-               dbg("%s \n", "mos7840_break:leaving ...........");
+               dbg("%s", "mos7840_break:leaving ...........");
                return -1;
        }
 
@@ -1384,16 +1388,16 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
        /* __u16 Data; */
        const unsigned char *current_position = data;
        unsigned char *data1;
-       dbg("%s \n", "entering ...........");
-       /* dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+       dbg("%s", "entering ...........");
+       /* dbg("mos7840_write: mos7840_port->shadowLCR is %x",
                                        mos7840_port->shadowLCR); */
 
 #ifdef NOTMOS7840
        Data = 0x00;
        status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
        mos7840_port->shadowLCR = Data;
-       dbg("mos7840_write: LINE_CONTROL_REGISTER is %x\n", Data);
-       dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+       dbg("mos7840_write: LINE_CONTROL_REGISTER is %x", Data);
+       dbg("mos7840_write: mos7840_port->shadowLCR is %x",
            mos7840_port->shadowLCR);
 
        /* Data = 0x03; */
@@ -1407,32 +1411,32 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
        /* status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data); */
        Data = 0x00;
        status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data);
-       dbg("mos7840_write:DLL value is %x\n", Data);
+       dbg("mos7840_write:DLL value is %x", Data);
 
        Data = 0x0;
        status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data);
-       dbg("mos7840_write:DLM value is %x\n", Data);
+       dbg("mos7840_write:DLM value is %x", Data);
 
        Data = Data & ~SERIAL_LCR_DLAB;
-       dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+       dbg("mos7840_write: mos7840_port->shadowLCR is %x",
            mos7840_port->shadowLCR);
        status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
 #endif
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Port Paranoia failed \n");
+               dbg("%s", "Port Paranoia failed");
                return -1;
        }
 
        serial = port->serial;
        if (mos7840_serial_paranoia_check(serial, __func__)) {
-               dbg("%s", "Serial Paranoia failed \n");
+               dbg("%s", "Serial Paranoia failed");
                return -1;
        }
 
        mos7840_port = mos7840_get_port_private(port);
        if (mos7840_port == NULL) {
-               dbg("%s", "mos7840_port is NULL\n");
+               dbg("%s", "mos7840_port is NULL");
                return -1;
        }
 
@@ -1444,7 +1448,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
                if (!mos7840_port->busy[i]) {
                        mos7840_port->busy[i] = 1;
                        urb = mos7840_port->write_urb_pool[i];
-                       dbg("\nURB:%d", i);
+                       dbg("URB:%d", i);
                        break;
                }
        }
@@ -1479,7 +1483,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
                          mos7840_bulk_out_data_callback, mos7840_port);
 
        data1 = urb->transfer_buffer;
-       dbg("\nbulkout endpoint is %d", port->bulk_out_endpointAddress);
+       dbg("bulkout endpoint is %d", port->bulk_out_endpointAddress);
 
        /* send it down the pipe */
        status = usb_submit_urb(urb, GFP_ATOMIC);
@@ -1494,7 +1498,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
        bytes_sent = transfer_size;
        mos7840_port->icount.tx += transfer_size;
        smp_wmb();
-       dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx);
+       dbg("mos7840_port->icount.tx is %d:", mos7840_port->icount.tx);
 exit:
        return bytes_sent;
 
@@ -1513,11 +1517,11 @@ static void mos7840_throttle(struct tty_struct *tty)
        int status;
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
+               dbg("%s", "Invalid port");
                return;
        }
 
-       dbg("- port %d\n", port->number);
+       dbg("- port %d", port->number);
 
        mos7840_port = mos7840_get_port_private(port);
 
@@ -1525,11 +1529,11 @@ static void mos7840_throttle(struct tty_struct *tty)
                return;
 
        if (!mos7840_port->open) {
-               dbg("%s\n", "port not opened");
+               dbg("%s", "port not opened");
                return;
        }
 
-       dbg("%s", "Entering .......... \n");
+       dbg("%s", "Entering ..........");
 
        /* if we are implementing XON/XOFF, send the stop character */
        if (I_IXOFF(tty)) {
@@ -1563,7 +1567,7 @@ static void mos7840_unthrottle(struct tty_struct *tty)
        struct moschip_port *mos7840_port = mos7840_get_port_private(port);
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
+               dbg("%s", "Invalid port");
                return;
        }
 
@@ -1575,7 +1579,7 @@ static void mos7840_unthrottle(struct tty_struct *tty)
                return;
        }
 
-       dbg("%s", "Entering .......... \n");
+       dbg("%s", "Entering ..........");
 
        /* if we are implementing XON/XOFF, send the start character */
        if (I_IXOFF(tty)) {
@@ -1660,7 +1664,7 @@ static int mos7840_tiocmset(struct tty_struct *tty, struct file *file,
 
        status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr);
        if (status < 0) {
-               dbg("setting MODEM_CONTROL_REGISTER Failed\n");
+               dbg("setting MODEM_CONTROL_REGISTER Failed");
                return status;
        }
 
@@ -1729,11 +1733,11 @@ static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor,
                        custom++;
                *divisor = custom;
 
-               dbg(" Baud %d = %d\n", baudrate, custom);
+               dbg(" Baud %d = %d", baudrate, custom);
                return 0;
        }
 
-       dbg("%s\n", " Baud calculation Failed...");
+       dbg("%s", " Baud calculation Failed...");
        return -1;
 #endif
 }
@@ -1759,16 +1763,16 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
 
        port = (struct usb_serial_port *)mos7840_port->port;
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
+               dbg("%s", "Invalid port");
                return -1;
        }
 
        if (mos7840_serial_paranoia_check(port->serial, __func__)) {
-               dbg("%s", "Invalid Serial \n");
+               dbg("%s", "Invalid Serial");
                return -1;
        }
 
-       dbg("%s", "Entering .......... \n");
+       dbg("%s", "Entering ..........");
 
        number = mos7840_port->port->number - mos7840_port->port->serial->minor;
 
@@ -1784,7 +1788,7 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
                status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
                                                                        Data);
                if (status < 0) {
-                       dbg("Writing spreg failed in set_serial_baud\n");
+                       dbg("Writing spreg failed in set_serial_baud");
                        return -1;
                }
 #endif
@@ -1797,7 +1801,7 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
                status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
                                                                        Data);
                if (status < 0) {
-                       dbg("Writing spreg failed in set_serial_baud\n");
+                       dbg("Writing spreg failed in set_serial_baud");
                        return -1;
                }
 #endif
@@ -1812,14 +1816,14 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
                status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
                                                                 &Data);
                if (status < 0) {
-                       dbg("reading spreg failed in set_serial_baud\n");
+                       dbg("reading spreg failed in set_serial_baud");
                        return -1;
                }
                Data = (Data & 0x8f) | clk_sel_val;
                status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset,
                                                                Data);
                if (status < 0) {
-                       dbg("Writing spreg failed in set_serial_baud\n");
+                       dbg("Writing spreg failed in set_serial_baud");
                        return -1;
                }
                /* Calculate the Divisor */
@@ -1835,11 +1839,11 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
 
                /* Write the divisor */
                Data = (unsigned char)(divisor & 0xff);
-               dbg("set_serial_baud Value to write DLL is %x\n", Data);
+               dbg("set_serial_baud Value to write DLL is %x", Data);
                mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
 
                Data = (unsigned char)((divisor & 0xff00) >> 8);
-               dbg("set_serial_baud Value to write DLM is %x\n", Data);
+               dbg("set_serial_baud Value to write DLM is %x", Data);
                mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
 
                /* Disable access to divisor latch */
@@ -1877,12 +1881,12 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
        port = (struct usb_serial_port *)mos7840_port->port;
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
+               dbg("%s", "Invalid port");
                return;
        }
 
        if (mos7840_serial_paranoia_check(port->serial, __func__)) {
-               dbg("%s", "Invalid Serial \n");
+               dbg("%s", "Invalid Serial");
                return;
        }
 
@@ -1895,7 +1899,7 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
                return;
        }
 
-       dbg("%s", "Entering .......... \n");
+       dbg("%s", "Entering ..........");
 
        lData = LCR_BITS_8;
        lStop = LCR_STOP_1;
@@ -1955,7 +1959,7 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
            ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
        mos7840_port->shadowLCR |= (lData | lParity | lStop);
 
-       dbg("mos7840_change_port_settings mos7840_port->shadowLCR is %x\n",
+       dbg("mos7840_change_port_settings mos7840_port->shadowLCR is %x",
            mos7840_port->shadowLCR);
        /* Disable Interrupts */
        Data = 0x00;
@@ -1997,7 +2001,7 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
 
        if (!baud) {
                /* pick a default, any default... */
-               dbg("%s\n", "Picked default baud...");
+               dbg("%s", "Picked default baud...");
                baud = 9600;
        }
 
@@ -2020,7 +2024,7 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
        }
        wake_up(&mos7840_port->delta_msr_wait);
        mos7840_port->delta_msr_cond = 1;
-       dbg("mos7840_change_port_settings mos7840_port->shadowLCR is End %x\n",
+       dbg("mos7840_change_port_settings mos7840_port->shadowLCR is End %x",
            mos7840_port->shadowLCR);
 
        return;
@@ -2040,16 +2044,16 @@ static void mos7840_set_termios(struct tty_struct *tty,
        unsigned int cflag;
        struct usb_serial *serial;
        struct moschip_port *mos7840_port;
-       dbg("mos7840_set_termios: START\n");
+       dbg("mos7840_set_termios: START");
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
+               dbg("%s", "Invalid port");
                return;
        }
 
        serial = port->serial;
 
        if (mos7840_serial_paranoia_check(serial, __func__)) {
-               dbg("%s", "Invalid Serial \n");
+               dbg("%s", "Invalid Serial");
                return;
        }
 
@@ -2063,7 +2067,7 @@ static void mos7840_set_termios(struct tty_struct *tty,
                return;
        }
 
-       dbg("%s\n", "setting termios - ");
+       dbg("%s", "setting termios - ");
 
        cflag = tty->termios->c_cflag;
 
@@ -2078,7 +2082,7 @@ static void mos7840_set_termios(struct tty_struct *tty,
        mos7840_change_port_settings(tty, mos7840_port, old_termios);
 
        if (!mos7840_port->read_urb) {
-               dbg("%s", "URB KILLED !!!!!\n");
+               dbg("%s", "URB KILLED !!!!!");
                return;
        }
 
@@ -2144,7 +2148,7 @@ static int mos7840_set_modem_info(struct moschip_port *mos7840_port,
 
        port = (struct usb_serial_port *)mos7840_port->port;
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
+               dbg("%s", "Invalid port");
                return -1;
        }
 
@@ -2189,7 +2193,7 @@ static int mos7840_set_modem_info(struct moschip_port *mos7840_port,
        status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
        unlock_kernel();
        if (status < 0) {
-               dbg("setting MODEM_CONTROL_REGISTER Failed\n");
+               dbg("setting MODEM_CONTROL_REGISTER Failed");
                return -1;
        }
 
@@ -2274,7 +2278,7 @@ static int mos7840_ioctl(struct tty_struct *tty, struct file *file,
        int mosret = 0;
 
        if (mos7840_port_paranoia_check(port, __func__)) {
-               dbg("%s", "Invalid port \n");
+               dbg("%s", "Invalid port");
                return -1;
        }
 
@@ -2374,9 +2378,8 @@ static int mos7840_calc_num_ports(struct usb_serial *serial)
 {
        int mos7840_num_ports = 0;
 
-       dbg("numberofendpoints: %d \n",
-           (int)serial->interface->cur_altsetting->desc.bNumEndpoints);
-       dbg("numberofendpoints: %d \n",
+       dbg("numberofendpoints: cur %d, alt %d",
+           (int)serial->interface->cur_altsetting->desc.bNumEndpoints,
            (int)serial->interface->altsetting->desc.bNumEndpoints);
        if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) {
                mos7840_num_ports = serial->num_ports = 2;
@@ -2385,7 +2388,7 @@ static int mos7840_calc_num_ports(struct usb_serial *serial)
                serial->num_bulk_out = 4;
                mos7840_num_ports = serial->num_ports = 4;
        }
-
+       dbg ("mos7840_num_ports = %d", mos7840_num_ports);
        return mos7840_num_ports;
 }
 
@@ -2400,22 +2403,24 @@ static int mos7840_startup(struct usb_serial *serial)
        int i, status;
 
        __u16 Data;
-       dbg("%s \n", " mos7840_startup :entering..........");
+       dbg("%s", "mos7840_startup :Entering..........");
 
        if (!serial) {
-               dbg("%s\n", "Invalid Handler");
+               dbg("%s", "Invalid Handler");
                return -1;
        }
 
        dev = serial->dev;
 
-       dbg("%s\n", "Entering...");
+       dbg("%s", "Entering...");
+       dbg ("mos7840_startup: serial = %p", serial);
 
        /* we set up the pointers to the endpoints in the mos7840_open *
         * function, as the structures aren't created yet.             */
 
        /* set up port private structures */
        for (i = 0; i < serial->num_ports; ++i) {
+               dbg ("mos7840_startup: configuring port %d............", i);
                mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
                if (mos7840_port == NULL) {
                        dev_err(&dev->dev, "%s - Out of memory\n", __func__);
@@ -2473,10 +2478,10 @@ static int mos7840_startup(struct usb_serial *serial)
                status = mos7840_get_reg_sync(serial->port[i],
                                 mos7840_port->ControlRegOffset, &Data);
                if (status < 0) {
-                       dbg("Reading ControlReg failed status-0x%x\n", status);
+                       dbg("Reading ControlReg failed status-0x%x", status);
                        break;
                } else
-                       dbg("ControlReg Reading success val is %x, status%d\n",
+                       dbg("ControlReg Reading success val is %x, status%d",
                            Data, status);
                Data |= 0x08;   /* setting driver done bit */
                Data |= 0x04;   /* sp1_bit to have cts change reflect in
@@ -2486,10 +2491,10 @@ static int mos7840_startup(struct usb_serial *serial)
                status = mos7840_set_reg_sync(serial->port[i],
                                         mos7840_port->ControlRegOffset, Data);
                if (status < 0) {
-                       dbg("Writing ControlReg failed(rx_disable) status-0x%x\n", status);
+                       dbg("Writing ControlReg failed(rx_disable) status-0x%x", status);
                        break;
                } else
-                       dbg("ControlReg Writing success(rx_disable) status%d\n",
+                       dbg("ControlReg Writing success(rx_disable) status%d",
                            status);
 
                /* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2
@@ -2498,48 +2503,48 @@ static int mos7840_startup(struct usb_serial *serial)
                status = mos7840_set_reg_sync(serial->port[i],
                         (__u16) (mos7840_port->DcrRegOffset + 0), Data);
                if (status < 0) {
-                       dbg("Writing DCR0 failed status-0x%x\n", status);
+                       dbg("Writing DCR0 failed status-0x%x", status);
                        break;
                } else
-                       dbg("DCR0 Writing success status%d\n", status);
+                       dbg("DCR0 Writing success status%d", status);
 
                Data = 0x05;
                status = mos7840_set_reg_sync(serial->port[i],
                         (__u16) (mos7840_port->DcrRegOffset + 1), Data);
                if (status < 0) {
-                       dbg("Writing DCR1 failed status-0x%x\n", status);
+                       dbg("Writing DCR1 failed status-0x%x", status);
                        break;
                } else
-                       dbg("DCR1 Writing success status%d\n", status);
+                       dbg("DCR1 Writing success status%d", status);
 
                Data = 0x24;
                status = mos7840_set_reg_sync(serial->port[i],
                         (__u16) (mos7840_port->DcrRegOffset + 2), Data);
                if (status < 0) {
-                       dbg("Writing DCR2 failed status-0x%x\n", status);
+                       dbg("Writing DCR2 failed status-0x%x", status);
                        break;
                } else
-                       dbg("DCR2 Writing success status%d\n", status);
+                       dbg("DCR2 Writing success status%d", status);
 
                /* write values in clkstart0x0 and clkmulti 0x20 */
                Data = 0x0;
                status = mos7840_set_reg_sync(serial->port[i],
                                         CLK_START_VALUE_REGISTER, Data);
                if (status < 0) {
-                       dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
+                       dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x", status);
                        break;
                } else
-                       dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status);
+                       dbg("CLK_START_VALUE_REGISTER Writing success status%d", status);
 
                Data = 0x20;
                status = mos7840_set_reg_sync(serial->port[i],
                                        CLK_MULTI_REGISTER, Data);
                if (status < 0) {
-                       dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n",
+                       dbg("Writing CLK_MULTI_REGISTER failed status-0x%x",
                            status);
                        goto error;
                } else
-                       dbg("CLK_MULTI_REGISTER Writing success status%d\n",
+                       dbg("CLK_MULTI_REGISTER Writing success status%d",
                            status);
 
                /* write value 0x0 to scratchpad register */
@@ -2547,11 +2552,11 @@ static int mos7840_startup(struct usb_serial *serial)
                status = mos7840_set_uart_reg(serial->port[i],
                                                SCRATCH_PAD_REGISTER, Data);
                if (status < 0) {
-                       dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x\n",
+                       dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x",
                            status);
                        break;
                } else
-                       dbg("SCRATCH_PAD_REGISTER Writing success status%d\n",
+                       dbg("SCRATCH_PAD_REGISTER Writing success status%d",
                            status);
 
                /* Zero Length flag register */
@@ -2562,30 +2567,30 @@ static int mos7840_startup(struct usb_serial *serial)
                        status = mos7840_set_reg_sync(serial->port[i],
                                      (__u16) (ZLP_REG1 +
                                      ((__u16)mos7840_port->port_num)), Data);
-                       dbg("ZLIP offset%x\n",
+                       dbg("ZLIP offset %x",
                            (__u16) (ZLP_REG1 +
                                        ((__u16) mos7840_port->port_num)));
                        if (status < 0) {
-                               dbg("Writing ZLP_REG%d failed status-0x%x\n",
+                               dbg("Writing ZLP_REG%d failed status-0x%x",
                                    i + 2, status);
                                break;
                        } else
-                               dbg("ZLP_REG%d Writing success status%d\n",
+                               dbg("ZLP_REG%d Writing success status%d",
                                    i + 2, status);
                } else {
                        Data = 0xff;
                        status = mos7840_set_reg_sync(serial->port[i],
                              (__u16) (ZLP_REG1 +
                              ((__u16)mos7840_port->port_num) - 0x1), Data);
-                       dbg("ZLIP offset%x\n",
+                       dbg("ZLIP offset %x",
                            (__u16) (ZLP_REG1 +
                                     ((__u16) mos7840_port->port_num) - 0x1));
                        if (status < 0) {
-                               dbg("Writing ZLP_REG%d failed status-0x%x\n",
+                               dbg("Writing ZLP_REG%d failed status-0x%x",
                                    i + 1, status);
                                break;
                        } else
-                               dbg("ZLP_REG%d Writing success status%d\n",
+                               dbg("ZLP_REG%d Writing success status%d",
                                    i + 1, status);
 
                }
@@ -2599,15 +2604,16 @@ static int mos7840_startup(struct usb_serial *serial)
                        goto error;
                }
        }
+       dbg ("mos7840_startup: all ports configured...........");
 
        /* Zero Length flag enable */
        Data = 0x0f;
        status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
        if (status < 0) {
-               dbg("Writing ZLP_REG5 failed status-0x%x\n", status);
+               dbg("Writing ZLP_REG5 failed status-0x%x", status);
                goto error;
        } else
-               dbg("ZLP_REG5 Writing success status%d\n", status);
+               dbg("ZLP_REG5 Writing success status%d", status);
 
        /* setting configuration feature to one */
        usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
@@ -2627,19 +2633,19 @@ error:
 }
 
 /****************************************************************************
- * mos7840_shutdown
+ * mos7840_disconnect
  *     This function is called whenever the device is removed from the usb bus.
  ****************************************************************************/
 
-static void mos7840_shutdown(struct usb_serial *serial)
+static void mos7840_disconnect(struct usb_serial *serial)
 {
        int i;
        unsigned long flags;
        struct moschip_port *mos7840_port;
-       dbg("%s \n", " shutdown :entering..........");
+       dbg("%s", " disconnect :entering..........");
 
        if (!serial) {
-               dbg("%s", "Invalid Handler \n");
+               dbg("%s", "Invalid Handler");
                return;
        }
 
@@ -2656,14 +2662,45 @@ static void mos7840_shutdown(struct usb_serial *serial)
                        mos7840_port->zombie = 1;
                        spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
                        usb_kill_urb(mos7840_port->control_urb);
+               }
+       }
+
+       dbg("%s", "Thank u :: ");
+
+}
+
+/****************************************************************************
+ * mos7840_release
+ *     This function is called when the usb_serial structure is freed.
+ ****************************************************************************/
+
+static void mos7840_release(struct usb_serial *serial)
+{
+       int i;
+       struct moschip_port *mos7840_port;
+       dbg("%s", " release :entering..........");
+
+       if (!serial) {
+               dbg("%s", "Invalid Handler");
+               return;
+       }
+
+       /* check for the ports to be closed,close the ports and disconnect */
+
+       /* free private structure allocated for serial port  *
+        * stop reads and writes on all ports                */
+
+       for (i = 0; i < serial->num_ports; ++i) {
+               mos7840_port = mos7840_get_port_private(serial->port[i]);
+               dbg("mos7840_port %d = %p", i, mos7840_port);
+               if (mos7840_port) {
                        kfree(mos7840_port->ctrl_buf);
                        kfree(mos7840_port->dr);
                        kfree(mos7840_port);
                }
-               mos7840_set_port_private(serial->port[i], NULL);
        }
 
-       dbg("%s\n", "Thank u :: ");
+       dbg("%s", "Thank u :: ");
 
 }
 
@@ -2701,7 +2738,8 @@ static struct usb_serial_driver moschip7840_4port_device = {
        .tiocmget = mos7840_tiocmget,
        .tiocmset = mos7840_tiocmset,
        .attach = mos7840_startup,
-       .shutdown = mos7840_shutdown,
+       .disconnect = mos7840_disconnect,
+       .release = mos7840_release,
        .read_bulk_callback = mos7840_bulk_in_callback,
        .read_int_callback = mos7840_interrupt_callback,
 };
@@ -2714,7 +2752,7 @@ static int __init moschip7840_init(void)
 {
        int retval;
 
-       dbg("%s \n", " mos7840_init :entering..........");
+       dbg("%s", " mos7840_init :entering..........");
 
        /* Register with the usb serial */
        retval = usb_serial_register(&moschip7840_4port_device);
@@ -2722,14 +2760,14 @@ static int __init moschip7840_init(void)
        if (retval)
                goto failed_port_device_register;
 
-       dbg("%s\n", "Entring...");
+       dbg("%s", "Entering...");
        printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
               DRIVER_DESC "\n");
 
        /* Register with the usb */
        retval = usb_register(&io_driver);
        if (retval == 0) {
-               dbg("%s\n", "Leaving...");
+               dbg("%s", "Leaving...");
                return 0;
        }
        usb_serial_deregister(&moschip7840_4port_device);
@@ -2744,13 +2782,13 @@ failed_port_device_register:
 static void __exit moschip7840_exit(void)
 {
 
-       dbg("%s \n", " mos7840_exit :entering..........");
+       dbg("%s", " mos7840_exit :entering..........");
 
        usb_deregister(&io_driver);
 
        usb_serial_deregister(&moschip7840_4port_device);
 
-       dbg("%s\n", "Entring...");
+       dbg("%s", "Entering...");
 }
 
 module_init(moschip7840_init);
index 1104617..56857dd 100644 (file)
@@ -72,7 +72,8 @@ static void omninet_write_bulk_callback(struct urb *urb);
 static int  omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
                                const unsigned char *buf, int count);
 static int  omninet_write_room(struct tty_struct *tty);
-static void omninet_shutdown(struct usb_serial *serial);
+static void omninet_disconnect(struct usb_serial *serial);
+static void omninet_release(struct usb_serial *serial);
 static int omninet_attach(struct usb_serial *serial);
 
 static struct usb_device_id id_table[] = {
@@ -108,7 +109,8 @@ static struct usb_serial_driver zyxel_omninet_device = {
        .write_room =           omninet_write_room,
        .read_bulk_callback =   omninet_read_bulk_callback,
        .write_bulk_callback =  omninet_write_bulk_callback,
-       .shutdown =             omninet_shutdown,
+       .disconnect =           omninet_disconnect,
+       .release =              omninet_release,
 };
 
 
@@ -345,13 +347,22 @@ static void omninet_write_bulk_callback(struct urb *urb)
 }
 
 
-static void omninet_shutdown(struct usb_serial *serial)
+static void omninet_disconnect(struct usb_serial *serial)
 {
        struct usb_serial_port *wport = serial->port[1];
-       struct usb_serial_port *port = serial->port[0];
+
        dbg("%s", __func__);
 
        usb_kill_urb(wport->write_urb);
+}
+
+
+static void omninet_release(struct usb_serial *serial)
+{
+       struct usb_serial_port *port = serial->port[0];
+
+       dbg("%s", __func__);
+
        kfree(usb_get_serial_port_data(port));
 }
 
index c20480a..336bba7 100644 (file)
@@ -463,7 +463,7 @@ error:
        return retval;
 }
 
-static void opticon_shutdown(struct usb_serial *serial)
+static void opticon_disconnect(struct usb_serial *serial)
 {
        struct opticon_private *priv = usb_get_serial_data(serial);
 
@@ -471,9 +471,16 @@ static void opticon_shutdown(struct usb_serial *serial)
 
        usb_kill_urb(priv->bulk_read_urb);
        usb_free_urb(priv->bulk_read_urb);
+}
+
+static void opticon_release(struct usb_serial *serial)
+{
+       struct opticon_private *priv = usb_get_serial_data(serial);
+
+       dbg("%s", __func__);
+
        kfree(priv->bulk_in_buffer);
        kfree(priv);
-       usb_set_serial_data(serial, NULL);
 }
 
 static int opticon_suspend(struct usb_interface *intf, pm_message_t message)
@@ -524,7 +531,8 @@ static struct usb_serial_driver opticon_device = {
        .close =                opticon_close,
        .write =                opticon_write,
        .write_room =           opticon_write_room,
-       .shutdown =             opticon_shutdown,
+       .disconnect =           opticon_disconnect,
+       .release =              opticon_release,
        .throttle =             opticon_throttle,
        .unthrottle =           opticon_unthrottle,
        .ioctl =                opticon_ioctl,
index a16d69f..575816e 100644 (file)
 #include <linux/usb/serial.h>
 
 /* Function prototypes */
+static int  option_probe(struct usb_serial *serial,
+                       const struct usb_device_id *id);
 static int  option_open(struct tty_struct *tty, struct usb_serial_port *port,
                                                        struct file *filp);
 static void option_close(struct usb_serial_port *port);
 static void option_dtr_rts(struct usb_serial_port *port, int on);
 
 static int  option_startup(struct usb_serial *serial);
-static void option_shutdown(struct usb_serial *serial);
+static void option_disconnect(struct usb_serial *serial);
+static void option_release(struct usb_serial *serial);
 static int  option_write_room(struct tty_struct *tty);
 
 static void option_instat_callback(struct urb *urb);
@@ -202,9 +205,9 @@ static int  option_resume(struct usb_serial *serial);
 #define NOVATELWIRELESS_PRODUCT_MC727          0x4100
 #define NOVATELWIRELESS_PRODUCT_MC950D         0x4400
 #define NOVATELWIRELESS_PRODUCT_U727           0x5010
+#define NOVATELWIRELESS_PRODUCT_MC760          0x6000
 
 /* FUTURE NOVATEL PRODUCTS */
-#define NOVATELWIRELESS_PRODUCT_EVDO_FULLSPEED 0X6000
 #define NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED 0X6001
 #define NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED 0X7000
 #define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED 0X7001
@@ -305,6 +308,10 @@ static int  option_resume(struct usb_serial *serial);
 #define DLINK_PRODUCT_DWM_652                  0x3e04
 
 
+/* TOSHIBA PRODUCTS */
+#define TOSHIBA_VENDOR_ID                      0x0930
+#define TOSHIBA_PRODUCT_HSDPA_MINICARD         0x1302
+
 static struct usb_device_id option_ids[] = {
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -422,7 +429,7 @@ static struct usb_device_id option_ids[] = {
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC950D) }, /* Novatel MC930D/MC950D */
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) }, /* Novatel MC727/U727/USB727 */
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U727) }, /* Novatel MC727/U727/USB727 */
-       { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_FULLSPEED) }, /* Novatel EVDO product */
+       { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC760) }, /* Novatel MC760/U760/USB760 */
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED) }, /* Novatel HSPA product */
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, /* Novatel EVDO Embedded product */
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, /* Novatel HSPA Embedded product */
@@ -523,6 +530,7 @@ static struct usb_device_id option_ids[] = {
        { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) },
        { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) },
        { USB_DEVICE(0x1da5, 0x4515) }, /* BenQ H20 */
+       { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */
        { } /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, option_ids);
@@ -550,6 +558,7 @@ static struct usb_serial_driver option_1port_device = {
        .usb_driver        = &option_driver,
        .id_table          = option_ids,
        .num_ports         = 1,
+       .probe             = option_probe,
        .open              = option_open,
        .close             = option_close,
        .dtr_rts           = option_dtr_rts,
@@ -560,7 +569,8 @@ static struct usb_serial_driver option_1port_device = {
        .tiocmget          = option_tiocmget,
        .tiocmset          = option_tiocmset,
        .attach            = option_startup,
-       .shutdown          = option_shutdown,
+       .disconnect        = option_disconnect,
+       .release           = option_release,
        .read_int_callback = option_instat_callback,
        .suspend           = option_suspend,
        .resume            = option_resume,
@@ -626,6 +636,18 @@ static void __exit option_exit(void)
 module_init(option_init);
 module_exit(option_exit);
 
+static int option_probe(struct usb_serial *serial,
+                       const struct usb_device_id *id)
+{
+       /* 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)
+               return -ENODEV;
+
+       return 0;
+}
+
 static void option_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios)
 {
@@ -1129,7 +1151,14 @@ static void stop_read_write_urbs(struct usb_serial *serial)
        }
 }
 
-static void option_shutdown(struct usb_serial *serial)
+static void option_disconnect(struct usb_serial *serial)
+{
+       dbg("%s", __func__);
+
+       stop_read_write_urbs(serial);
+}
+
+static void option_release(struct usb_serial *serial)
 {
        int i, j;
        struct usb_serial_port *port;
@@ -1137,8 +1166,6 @@ static void option_shutdown(struct usb_serial *serial)
 
        dbg("%s", __func__);
 
-       stop_read_write_urbs(serial);
-
        /* Now free them */
        for (i = 0; i < serial->num_ports; ++i) {
                port = serial->port[i];
index 7de5478..3cece27 100644 (file)
@@ -159,7 +159,7 @@ static int oti6858_tiocmget(struct tty_struct *tty, struct file *file);
 static int oti6858_tiocmset(struct tty_struct *tty, struct file *file,
                                unsigned int set, unsigned int clear);
 static int oti6858_startup(struct usb_serial *serial);
-static void oti6858_shutdown(struct usb_serial *serial);
+static void oti6858_release(struct usb_serial *serial);
 
 /* functions operating on buffers */
 static struct oti6858_buf *oti6858_buf_alloc(unsigned int size);
@@ -194,7 +194,7 @@ static struct usb_serial_driver oti6858_device = {
        .write_room =           oti6858_write_room,
        .chars_in_buffer =      oti6858_chars_in_buffer,
        .attach =               oti6858_startup,
-       .shutdown =             oti6858_shutdown,
+       .release =              oti6858_release,
 };
 
 struct oti6858_private {
@@ -782,7 +782,7 @@ static int oti6858_ioctl(struct tty_struct *tty, struct file *file,
 }
 
 
-static void oti6858_shutdown(struct usb_serial *serial)
+static void oti6858_release(struct usb_serial *serial)
 {
        struct oti6858_private *priv;
        int i;
@@ -794,7 +794,6 @@ static void oti6858_shutdown(struct usb_serial *serial)
                if (priv) {
                        oti6858_buf_free(priv->buf);
                        kfree(priv);
-                       usb_set_serial_port_data(serial->port[i], NULL);
                }
        }
 }
index e02dc3d..ec6c132 100644 (file)
@@ -878,7 +878,7 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state)
                dbg("%s - error sending break = %d", __func__, result);
 }
 
-static void pl2303_shutdown(struct usb_serial *serial)
+static void pl2303_release(struct usb_serial *serial)
 {
        int i;
        struct pl2303_private *priv;
@@ -890,7 +890,6 @@ static void pl2303_shutdown(struct usb_serial *serial)
                if (priv) {
                        pl2303_buf_free(priv->buf);
                        kfree(priv);
-                       usb_set_serial_port_data(serial->port[i], NULL);
                }
        }
 }
@@ -927,6 +926,8 @@ static void pl2303_update_line_status(struct usb_serial_port *port,
        spin_lock_irqsave(&priv->lock, flags);
        priv->line_status = data[status_idx];
        spin_unlock_irqrestore(&priv->lock, flags);
+       if (priv->line_status & UART_BREAK_ERROR)
+               usb_serial_handle_break(port);
        wake_up_interruptible(&priv->delta_msr_wait);
 }
 
@@ -1037,7 +1038,8 @@ static void pl2303_read_bulk_callback(struct urb *urb)
                if (line_status & UART_OVERRUN_ERROR)
                        tty_insert_flip_char(tty, 0, TTY_OVERRUN);
                for (i = 0; i < urb->actual_length; ++i)
-                       tty_insert_flip_char(tty, data[i], tty_flag);
+                       if (!usb_serial_handle_sysrq_char(port, data[i]))
+                               tty_insert_flip_char(tty, data[i], tty_flag);
                tty_flip_buffer_push(tty);
        }
        tty_kref_put(tty);
@@ -1120,7 +1122,7 @@ static struct usb_serial_driver pl2303_device = {
        .write_room =           pl2303_write_room,
        .chars_in_buffer =      pl2303_chars_in_buffer,
        .attach =               pl2303_startup,
-       .shutdown =             pl2303_shutdown,
+       .release =              pl2303_release,
 };
 
 static int __init pl2303_init(void)
index 17ac34f..032f7ae 100644 (file)
@@ -1,7 +1,10 @@
 /*
   USB Driver for Sierra Wireless
 
-  Copyright (C) 2006, 2007, 2008  Kevin Lloyd <klloyd@sierrawireless.com>
+  Copyright (C) 2006, 2007, 2008  Kevin Lloyd <klloyd@sierrawireless.com>,
+
+  Copyright (C) 2008, 2009  Elina Pasheva, Matthew Safar, Rory Filer
+                       <linux@sierrawireless.com>
 
   IMPORTANT DISCLAIMER: This driver is not commercially supported by
   Sierra Wireless. Use at your own risk.
@@ -14,8 +17,8 @@
   Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org>
 */
 
-#define DRIVER_VERSION "v.1.3.3"
-#define DRIVER_AUTHOR "Kevin Lloyd <klloyd@sierrawireless.com>"
+#define DRIVER_VERSION "v.1.3.7"
+#define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer"
 #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
 
 #include <linux/kernel.h>
 #define SWIMS_USB_REQUEST_SetPower     0x00
 #define SWIMS_USB_REQUEST_SetNmea      0x07
 
-#define N_IN_URB       4
-#define N_OUT_URB      4
+#define N_IN_URB       8
+#define N_OUT_URB      64
 #define IN_BUFLEN      4096
 
+#define MAX_TRANSFER           (PAGE_SIZE - 512)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+   allocations > PAGE_SIZE and the number of packets in a page
+   is an integer 512 is the largest possible packet on EHCI */
+
 static int debug;
 static int nmea;
 
@@ -46,7 +54,7 @@ struct sierra_iface_info {
 static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
 {
        int result;
-       dev_dbg(&udev->dev, "%s", __func__);
+       dev_dbg(&udev->dev, "%s\n", __func__);
        result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                        SWIMS_USB_REQUEST_SetPower,     /* __u8 request      */
                        USB_TYPE_VENDOR,                /* __u8 request type */
@@ -61,7 +69,7 @@ static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
 static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable)
 {
        int result;
-       dev_dbg(&udev->dev, "%s", __func__);
+       dev_dbg(&udev->dev, "%s\n", __func__);
        result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                        SWIMS_USB_REQUEST_SetNmea,      /* __u8 request      */
                        USB_TYPE_VENDOR,                /* __u8 request type */
@@ -75,18 +83,22 @@ static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable)
 
 static int sierra_calc_num_ports(struct usb_serial *serial)
 {
-       int result;
-       int *num_ports = usb_get_serial_data(serial);
-       dev_dbg(&serial->dev->dev, "%s", __func__);
+       int num_ports = 0;
+       u8 ifnum, numendpoints;
 
-       result = *num_ports;
+       dev_dbg(&serial->dev->dev, "%s\n", __func__);
 
-       if (result) {
-               kfree(num_ports);
-               usb_set_serial_data(serial, NULL);
-       }
+       ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
+       numendpoints = serial->interface->cur_altsetting->desc.bNumEndpoints;
 
-       return result;
+       /* Dummy interface present on some SKUs should be ignored */
+       if (ifnum == 0x99)
+               num_ports = 0;
+       else if (numendpoints <= 3)
+               num_ports = 1;
+       else
+               num_ports = (numendpoints-1)/2;
+       return num_ports;
 }
 
 static int is_blacklisted(const u8 ifnum,
@@ -111,7 +123,7 @@ static int sierra_calc_interface(struct usb_serial *serial)
        int interface;
        struct usb_interface *p_interface;
        struct usb_host_interface *p_host_interface;
-       dev_dbg(&serial->dev->dev, "%s", __func__);
+       dev_dbg(&serial->dev->dev, "%s\n", __func__);
 
        /* Get the interface structure pointer from the serial struct */
        p_interface = serial->interface;
@@ -132,23 +144,12 @@ static int sierra_probe(struct usb_serial *serial,
 {
        int result = 0;
        struct usb_device *udev;
-       int *num_ports;
        u8 ifnum;
-       u8 numendpoints;
 
-       dev_dbg(&serial->dev->dev, "%s", __func__);
-
-       num_ports = kmalloc(sizeof(*num_ports), GFP_KERNEL);
-       if (!num_ports)
-               return -ENOMEM;
-
-       ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
-       numendpoints = serial->interface->cur_altsetting->desc.bNumEndpoints;
        udev = serial->dev;
+       dev_dbg(&udev->dev, "%s\n", __func__);
 
-       /* Figure out the interface number from the serial structure */
        ifnum = sierra_calc_interface(serial);
-
        /*
         * If this interface supports more than 1 alternate
         * select the 2nd one
@@ -160,20 +161,6 @@ static int sierra_probe(struct usb_serial *serial,
                usb_set_interface(udev, ifnum, 1);
        }
 
-       /* Dummy interface present on some SKUs should be ignored */
-       if (ifnum == 0x99)
-               *num_ports = 0;
-       else if (numendpoints <= 3)
-               *num_ports = 1;
-       else
-               *num_ports = (numendpoints-1)/2;
-
-       /*
-        * save off our num_ports info so that we can use it in the
-        * calc_num_ports callback
-        */
-       usb_set_serial_data(serial, (void *)num_ports);
-
        /* ifnum could have changed - by calling usb_set_interface */
        ifnum = sierra_calc_interface(serial);
 
@@ -289,7 +276,7 @@ static int sierra_send_setup(struct usb_serial_port *port)
        __u16 interface = 0;
        int val = 0;
 
-       dev_dbg(&port->dev, "%s", __func__);
+       dev_dbg(&port->dev, "%s\n", __func__);
 
        portdata = usb_get_serial_port_data(port);
 
@@ -332,7 +319,7 @@ static int sierra_send_setup(struct usb_serial_port *port)
 static void sierra_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios)
 {
-       dev_dbg(&port->dev, "%s", __func__);
+       dev_dbg(&port->dev, "%s\n", __func__);
        tty_termios_copy_hw(tty->termios, old_termios);
        sierra_send_setup(port);
 }
@@ -343,7 +330,7 @@ static int sierra_tiocmget(struct tty_struct *tty, struct file *file)
        unsigned int value;
        struct sierra_port_private *portdata;
 
-       dev_dbg(&port->dev, "%s", __func__);
+       dev_dbg(&port->dev, "%s\n", __func__);
        portdata = usb_get_serial_port_data(port);
 
        value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
@@ -394,14 +381,14 @@ static void sierra_outdat_callback(struct urb *urb)
        int status = urb->status;
        unsigned long flags;
 
-       dev_dbg(&port->dev, "%s - port %d", __func__, port->number);
+       dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number);
 
        /* free up the transfer buffer, as usb_free_urb() does not do this */
        kfree(urb->transfer_buffer);
 
        if (status)
                dev_dbg(&port->dev, "%s - nonzero write bulk status "
-                   "received: %d", __func__, status);
+                   "received: %d\n", __func__, status);
 
        spin_lock_irqsave(&portdata->lock, flags);
        --portdata->outstanding_urbs;
@@ -419,50 +406,61 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
        unsigned long flags;
        unsigned char *buffer;
        struct urb *urb;
-       int status;
+       size_t writesize = min((size_t)count, (size_t)MAX_TRANSFER);
+       int retval = 0;
+
+       /* verify that we actually have some data to write */
+       if (count == 0)
+               return 0;
 
        portdata = usb_get_serial_port_data(port);
 
-       dev_dbg(&port->dev, "%s: write (%d chars)", __func__, count);
+       dev_dbg(&port->dev, "%s: write (%zd bytes)\n", __func__, writesize);
 
        spin_lock_irqsave(&portdata->lock, flags);
+       dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__,
+               portdata->outstanding_urbs);
        if (portdata->outstanding_urbs > N_OUT_URB) {
                spin_unlock_irqrestore(&portdata->lock, flags);
                dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
                return 0;
        }
        portdata->outstanding_urbs++;
+       dev_dbg(&port->dev, "%s - 1, outstanding_urbs: %d\n", __func__,
+               portdata->outstanding_urbs);
        spin_unlock_irqrestore(&portdata->lock, flags);
 
-       buffer = kmalloc(count, GFP_ATOMIC);
+       buffer = kmalloc(writesize, GFP_ATOMIC);
        if (!buffer) {
                dev_err(&port->dev, "out of memory\n");
-               count = -ENOMEM;
+               retval = -ENOMEM;
                goto error_no_buffer;
        }
 
        urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!urb) {
                dev_err(&port->dev, "no more free urbs\n");
-               count = -ENOMEM;
+               retval = -ENOMEM;
                goto error_no_urb;
        }
 
-       memcpy(buffer, buf, count);
+       memcpy(buffer, buf, writesize);
 
-       usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
+       usb_serial_debug_data(debug, &port->dev, __func__, writesize, buffer);
 
        usb_fill_bulk_urb(urb, serial->dev,
                          usb_sndbulkpipe(serial->dev,
                                          port->bulk_out_endpointAddress),
-                         buffer, count, sierra_outdat_callback, port);
+                         buffer, writesize, sierra_outdat_callback, port);
+
+       /* Handle the need to send a zero length packet */
+       urb->transfer_flags |= URB_ZERO_PACKET;
 
        /* send it down the pipe */
-       status = usb_submit_urb(urb, GFP_ATOMIC);
-       if (status) {
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval) {
                dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed "
-                       "with status = %d\n", __func__, status);
-               count = status;
+                       "with status = %d\n", __func__, retval);
                goto error;
        }
 
@@ -470,7 +468,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
         * really free it when it is finished with it */
        usb_free_urb(urb);
 
-       return count;
+       return writesize;
 error:
        usb_free_urb(urb);
 error_no_urb:
@@ -478,8 +476,10 @@ error_no_urb:
 error_no_buffer:
        spin_lock_irqsave(&portdata->lock, flags);
        --portdata->outstanding_urbs;
+       dev_dbg(&port->dev, "%s - 2. outstanding_urbs: %d\n", __func__,
+               portdata->outstanding_urbs);
        spin_unlock_irqrestore(&portdata->lock, flags);
-       return count;
+       return retval;
 }
 
 static void sierra_indat_callback(struct urb *urb)
@@ -491,33 +491,39 @@ static void sierra_indat_callback(struct urb *urb)
        unsigned char *data = urb->transfer_buffer;
        int status = urb->status;
 
-       dbg("%s: %p", __func__, urb);
-
        endpoint = usb_pipeendpoint(urb->pipe);
-       port =  urb->context;
+       port = urb->context;
+
+       dev_dbg(&port->dev, "%s: %p\n", __func__, urb);
 
        if (status) {
                dev_dbg(&port->dev, "%s: nonzero status: %d on"
-                   " endpoint %02x.", __func__, status, endpoint);
+                       " endpoint %02x\n", __func__, status, endpoint);
        } else {
                if (urb->actual_length) {
                        tty = tty_port_tty_get(&port->port);
+
                        tty_buffer_request_room(tty, urb->actual_length);
                        tty_insert_flip_string(tty, data, urb->actual_length);
                        tty_flip_buffer_push(tty);
+
                        tty_kref_put(tty);
-               } else
+                       usb_serial_debug_data(debug, &port->dev, __func__,
+                               urb->actual_length, data);
+               } else {
                        dev_dbg(&port->dev, "%s: empty read urb"
-                               " received", __func__);
-
-               /* Resubmit urb so we continue receiving */
-               if (port->port.count && status != -ESHUTDOWN && status != -EPERM) {
-                       err = usb_submit_urb(urb, GFP_ATOMIC);
-                       if (err)
-                               dev_err(&port->dev, "resubmit read urb failed."
-                                       "(%d)\n", err);
+                               " received\n", __func__);
                }
        }
+
+       /* Resubmit urb so we continue receiving */
+       if (port->port.count && status != -ESHUTDOWN && status != -EPERM) {
+               err = usb_submit_urb(urb, GFP_ATOMIC);
+               if (err)
+                       dev_err(&port->dev, "resubmit read urb failed."
+                               "(%d)\n", err);
+       }
+
        return;
 }
 
@@ -529,8 +535,7 @@ static void sierra_instat_callback(struct urb *urb)
        struct sierra_port_private *portdata = usb_get_serial_port_data(port);
        struct usb_serial *serial = port->serial;
 
-       dev_dbg(&port->dev, "%s", __func__);
-       dev_dbg(&port->dev, "%s: urb %p port %p has data %p", __func__,
+       dev_dbg(&port->dev, "%s: urb %p port %p has data %p\n", __func__,
                urb, port, portdata);
 
        if (status == 0) {
@@ -550,7 +555,7 @@ static void sierra_instat_callback(struct urb *urb)
                                        sizeof(struct usb_ctrlrequest));
                        struct tty_struct *tty;
 
-                       dev_dbg(&port->dev, "%s: signal x%x", __func__,
+                       dev_dbg(&port->dev, "%s: signal x%x\n", __func__,
                                signals);
 
                        old_dcd_state = portdata->dcd_state;
@@ -565,20 +570,20 @@ static void sierra_instat_callback(struct urb *urb)
                                tty_hangup(tty);
                        tty_kref_put(tty);
                } else {
-                       dev_dbg(&port->dev, "%s: type %x req %x",
+                       dev_dbg(&port->dev, "%s: type %x req %x\n",
                                __func__, req_pkt->bRequestType,
                                req_pkt->bRequest);
                }
        } else
-               dev_dbg(&port->dev, "%s: error %d", __func__, status);
+               dev_dbg(&port->dev, "%s: error %d\n", __func__, status);
 
        /* Resubmit urb so we continue receiving IRQ data */
-       if (status != -ESHUTDOWN) {
+       if (port->port.count && status != -ESHUTDOWN && status != -ENOENT) {
                urb->dev = serial->dev;
                err = usb_submit_urb(urb, GFP_ATOMIC);
                if (err)
-                       dev_dbg(&port->dev, "%s: resubmit intr urb "
-                               "failed. (%d)", __func__, err);
+                       dev_err(&port->dev, "%s: resubmit intr urb "
+                               "failed. (%d)\n", __func__, err);
        }
 }
 
@@ -588,7 +593,7 @@ static int sierra_write_room(struct tty_struct *tty)
        struct sierra_port_private *portdata = usb_get_serial_port_data(port);
        unsigned long flags;
 
-       dev_dbg(&port->dev, "%s - port %d", __func__, port->number);
+       dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number);
 
        /* try to give a good number back based on if we have any free urbs at
         * this point in time */
@@ -729,7 +734,7 @@ static int sierra_open(struct tty_struct *tty,
 
        portdata = usb_get_serial_port_data(port);
 
-       dev_dbg(&port->dev, "%s", __func__);
+       dev_dbg(&port->dev, "%s\n", __func__);
 
        /* Set some sane defaults */
        portdata->rts_state = 1;
@@ -782,7 +787,7 @@ static int sierra_startup(struct usb_serial *serial)
        struct sierra_port_private *portdata;
        int i;
 
-       dev_dbg(&serial->dev->dev, "%s", __func__);
+       dev_dbg(&serial->dev->dev, "%s\n", __func__);
 
        /* Set Device mode to D0 */
        sierra_set_power_state(serial->dev, 0x0000);
@@ -797,7 +802,7 @@ static int sierra_startup(struct usb_serial *serial)
                portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
                if (!portdata) {
                        dev_dbg(&port->dev, "%s: kmalloc for "
-                               "sierra_port_private (%d) failed!.",
+                               "sierra_port_private (%d) failed!.\n",
                                __func__, i);
                        return -ENOMEM;
                }
@@ -809,13 +814,13 @@ static int sierra_startup(struct usb_serial *serial)
        return 0;
 }
 
-static void sierra_shutdown(struct usb_serial *serial)
+static void sierra_disconnect(struct usb_serial *serial)
 {
        int i;
        struct usb_serial_port *port;
        struct sierra_port_private *portdata;
 
-       dev_dbg(&serial->dev->dev, "%s", __func__);
+       dev_dbg(&serial->dev->dev, "%s\n", __func__);
 
        for (i = 0; i < serial->num_ports; ++i) {
                port = serial->port[i];
@@ -848,7 +853,7 @@ static struct usb_serial_driver sierra_device = {
        .tiocmget          = sierra_tiocmget,
        .tiocmset          = sierra_tiocmset,
        .attach            = sierra_startup,
-       .shutdown          = sierra_shutdown,
+       .disconnect        = sierra_disconnect,
        .read_int_callback = sierra_instat_callback,
 };
 
index 8f7ed8f..3c249d8 100644 (file)
@@ -356,7 +356,7 @@ cleanup:
 }
 
 /* call when the device plug out. free all the memory alloced by probe */
-static void spcp8x5_shutdown(struct usb_serial *serial)
+static void spcp8x5_release(struct usb_serial *serial)
 {
        int i;
        struct spcp8x5_private *priv;
@@ -366,7 +366,6 @@ static void spcp8x5_shutdown(struct usb_serial *serial)
                if (priv) {
                        free_ringbuf(priv->buf);
                        kfree(priv);
-                       usb_set_serial_port_data(serial->port[i] , NULL);
                }
        }
 }
@@ -1020,7 +1019,7 @@ static struct usb_serial_driver spcp8x5_device = {
        .write_bulk_callback    = spcp8x5_write_bulk_callback,
        .chars_in_buffer        = spcp8x5_chars_in_buffer,
        .attach                 = spcp8x5_startup,
-       .shutdown               = spcp8x5_shutdown,
+       .release                = spcp8x5_release,
 };
 
 static int __init spcp8x5_init(void)
index 8b07ebc..6157fac 100644 (file)
@@ -267,7 +267,7 @@ error:
        return retval;
 }
 
-static void symbol_shutdown(struct usb_serial *serial)
+static void symbol_disconnect(struct usb_serial *serial)
 {
        struct symbol_private *priv = usb_get_serial_data(serial);
 
@@ -275,9 +275,16 @@ static void symbol_shutdown(struct usb_serial *serial)
 
        usb_kill_urb(priv->int_urb);
        usb_free_urb(priv->int_urb);
+}
+
+static void symbol_release(struct usb_serial *serial)
+{
+       struct symbol_private *priv = usb_get_serial_data(serial);
+
+       dbg("%s", __func__);
+
        kfree(priv->int_buffer);
        kfree(priv);
-       usb_set_serial_data(serial, NULL);
 }
 
 static struct usb_driver symbol_driver = {
@@ -299,7 +306,8 @@ static struct usb_serial_driver symbol_device = {
        .attach =               symbol_startup,
        .open =                 symbol_open,
        .close =                symbol_close,
-       .shutdown =             symbol_shutdown,
+       .disconnect =           symbol_disconnect,
+       .release =              symbol_release,
        .throttle =             symbol_throttle,
        .unthrottle =           symbol_unthrottle,
 };
index 42cb04c..991d823 100644 (file)
@@ -97,7 +97,7 @@ struct ti_device {
 /* Function Declarations */
 
 static int ti_startup(struct usb_serial *serial);
-static void ti_shutdown(struct usb_serial *serial);
+static void ti_release(struct usb_serial *serial);
 static int ti_open(struct tty_struct *tty, struct usb_serial_port *port,
                struct file *file);
 static void ti_close(struct usb_serial_port *port);
@@ -230,7 +230,7 @@ static struct usb_serial_driver ti_1port_device = {
        .id_table               = ti_id_table_3410,
        .num_ports              = 1,
        .attach                 = ti_startup,
-       .shutdown               = ti_shutdown,
+       .release                = ti_release,
        .open                   = ti_open,
        .close                  = ti_close,
        .write                  = ti_write,
@@ -258,7 +258,7 @@ static struct usb_serial_driver ti_2port_device = {
        .id_table               = ti_id_table_5052,
        .num_ports              = 2,
        .attach                 = ti_startup,
-       .shutdown               = ti_shutdown,
+       .release                = ti_release,
        .open                   = ti_open,
        .close                  = ti_close,
        .write                  = ti_write,
@@ -473,7 +473,7 @@ free_tdev:
 }
 
 
-static void ti_shutdown(struct usb_serial *serial)
+static void ti_release(struct usb_serial *serial)
 {
        int i;
        struct ti_device *tdev = usb_get_serial_data(serial);
@@ -486,12 +486,10 @@ static void ti_shutdown(struct usb_serial *serial)
                if (tport) {
                        ti_buf_free(tport->tp_write_buf);
                        kfree(tport);
-                       usb_set_serial_port_data(serial->port[i], NULL);
                }
        }
 
        kfree(tdev);
-       usb_set_serial_data(serial, NULL);
 }
 
 
index 1967a7e..d595aa5 100644 (file)
@@ -141,6 +141,14 @@ static void destroy_serial(struct kref *kref)
        if (serial->minor != SERIAL_TTY_NO_MINOR)
                return_serial(serial);
 
+       serial->type->release(serial);
+
+       for (i = 0; i < serial->num_ports; ++i) {
+               port = serial->port[i];
+               if (port)
+                       put_device(&port->dev);
+       }
+
        /* If this is a "fake" port, we have to clean it up here, as it will
         * not get cleaned up in port_release() as it was never registered with
         * the driver core */
@@ -148,9 +156,8 @@ static void destroy_serial(struct kref *kref)
                for (i = serial->num_ports;
                                        i < serial->num_port_pointers; ++i) {
                        port = serial->port[i];
-                       if (!port)
-                               continue;
-                       port_free(port);
+                       if (port)
+                               port_free(port);
                }
        }
 
@@ -1046,10 +1053,15 @@ int usb_serial_probe(struct usb_interface *interface,
 
                dev_set_name(&port->dev, "ttyUSB%d", port->number);
                dbg ("%s - registering %s", __func__, dev_name(&port->dev));
+               port->dev_state = PORT_REGISTERING;
                retval = device_register(&port->dev);
-               if (retval)
+               if (retval) {
                        dev_err(&port->dev, "Error registering port device, "
                                "continuing\n");
+                       port->dev_state = PORT_UNREGISTERED;
+               } else {
+                       port->dev_state = PORT_REGISTERED;
+               }
        }
 
        usb_serial_console_init(debug, minor);
@@ -1113,10 +1125,6 @@ void usb_serial_disconnect(struct usb_interface *interface)
        serial->disconnected = 1;
        mutex_unlock(&serial->disc_mutex);
 
-       /* Unfortunately, many of the sub-drivers expect the port structures
-        * to exist when their shutdown method is called, so we have to go
-        * through this awkward two-step unregistration procedure.
-        */
        for (i = 0; i < serial->num_ports; ++i) {
                port = serial->port[i];
                if (port) {
@@ -1130,17 +1138,25 @@ void usb_serial_disconnect(struct usb_interface *interface)
                        }
                        kill_traffic(port);
                        cancel_work_sync(&port->work);
-                       device_del(&port->dev);
-               }
-       }
-       serial->type->shutdown(serial);
-       for (i = 0; i < serial->num_ports; ++i) {
-               port = serial->port[i];
-               if (port) {
-                       put_device(&port->dev);
-                       serial->port[i] = NULL;
+                       if (port->dev_state == PORT_REGISTERED) {
+
+                               /* Make sure the port is bound so that the
+                                * driver's port_remove method is called.
+                                */
+                               if (!port->dev.driver) {
+                                       int rc;
+
+                                       port->dev.driver =
+                                                       &serial->type->driver;
+                                       rc = device_bind_driver(&port->dev);
+                               }
+                               port->dev_state = PORT_UNREGISTERING;
+                               device_del(&port->dev);
+                               port->dev_state = PORT_UNREGISTERED;
+                       }
                }
        }
+       serial->type->disconnect(serial);
 
        /* let the last holder of this object
         * cause it to be cleaned up */
@@ -1318,7 +1334,8 @@ static void fixup_generic(struct usb_serial_driver *device)
        set_to_generic_if_null(device, chars_in_buffer);
        set_to_generic_if_null(device, read_bulk_callback);
        set_to_generic_if_null(device, write_bulk_callback);
-       set_to_generic_if_null(device, shutdown);
+       set_to_generic_if_null(device, disconnect);
+       set_to_generic_if_null(device, release);
 }
 
 int usb_serial_register(struct usb_serial_driver *driver)
index 6c9cbb5..6148009 100644 (file)
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
 
+#define URB_DEBUG_MAX_IN_FLIGHT_URBS   4000
 #define USB_DEBUG_MAX_PACKET_SIZE      8
+#define USB_DEBUG_BRK_SIZE             8
+static char USB_DEBUG_BRK[USB_DEBUG_BRK_SIZE] = {
+       0x00,
+       0xff,
+       0x01,
+       0xfe,
+       0x00,
+       0xfe,
+       0x01,
+       0xff,
+};
 
 static struct usb_device_id id_table [] = {
        { USB_DEVICE(0x0525, 0x127a) },
@@ -38,6 +50,32 @@ static int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port,
        return usb_serial_generic_open(tty, port, filp);
 }
 
+/* This HW really does not support a serial break, so one will be
+ * emulated when ever the break state is set to true.
+ */
+static void usb_debug_break_ctl(struct tty_struct *tty, int break_state)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       if (!break_state)
+               return;
+       usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE);
+}
+
+static void usb_debug_read_bulk_callback(struct urb *urb)
+{
+       struct usb_serial_port *port = urb->context;
+
+       if (urb->actual_length == USB_DEBUG_BRK_SIZE &&
+           memcmp(urb->transfer_buffer, USB_DEBUG_BRK,
+                  USB_DEBUG_BRK_SIZE) == 0) {
+               usb_serial_handle_break(port);
+               usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC);
+               return;
+       }
+
+       usb_serial_generic_read_bulk_callback(urb);
+}
+
 static struct usb_serial_driver debug_device = {
        .driver = {
                .owner =        THIS_MODULE,
@@ -46,6 +84,9 @@ static struct usb_serial_driver debug_device = {
        .id_table =             id_table,
        .num_ports =            1,
        .open =                 usb_debug_open,
+       .max_in_flight_urbs =   URB_DEBUG_MAX_IN_FLIGHT_URBS,
+       .break_ctl =            usb_debug_break_ctl,
+       .read_bulk_callback =   usb_debug_read_bulk_callback,
 };
 
 static int __init debug_init(void)
index b15f1c0..f5d0f64 100644 (file)
@@ -47,7 +47,7 @@ static void visor_unthrottle(struct tty_struct *tty);
 static int  visor_probe(struct usb_serial *serial,
                                        const struct usb_device_id *id);
 static int  visor_calc_num_ports(struct usb_serial *serial);
-static void visor_shutdown(struct usb_serial *serial);
+static void visor_release(struct usb_serial *serial);
 static void visor_write_bulk_callback(struct urb *urb);
 static void visor_read_bulk_callback(struct urb *urb);
 static void visor_read_int_callback(struct urb *urb);
@@ -202,7 +202,7 @@ static struct usb_serial_driver handspring_device = {
        .attach =               treo_attach,
        .probe =                visor_probe,
        .calc_num_ports =       visor_calc_num_ports,
-       .shutdown =             visor_shutdown,
+       .release =              visor_release,
        .write =                visor_write,
        .write_room =           visor_write_room,
        .write_bulk_callback =  visor_write_bulk_callback,
@@ -227,7 +227,7 @@ static struct usb_serial_driver clie_5_device = {
        .attach =               clie_5_attach,
        .probe =                visor_probe,
        .calc_num_ports =       visor_calc_num_ports,
-       .shutdown =             visor_shutdown,
+       .release =              visor_release,
        .write =                visor_write,
        .write_room =           visor_write_room,
        .write_bulk_callback =  visor_write_bulk_callback,
@@ -918,7 +918,7 @@ static int clie_5_attach(struct usb_serial *serial)
        return generic_startup(serial);
 }
 
-static void visor_shutdown(struct usb_serial *serial)
+static void visor_release(struct usb_serial *serial)
 {
        struct visor_private *priv;
        int i;
@@ -927,10 +927,7 @@ static void visor_shutdown(struct usb_serial *serial)
 
        for (i = 0; i < serial->num_ports; i++) {
                priv = usb_get_serial_port_data(serial->port[i]);
-               if (priv) {
-                       usb_set_serial_port_data(serial->port[i], NULL);
-                       kfree(priv);
-               }
+               kfree(priv);
        }
 }
 
index 7c7295d..8d126dd 100644 (file)
@@ -144,7 +144,7 @@ static int  whiteheat_firmware_attach(struct usb_serial *serial);
 
 /* function prototypes for the Connect Tech WhiteHEAT serial converter */
 static int  whiteheat_attach(struct usb_serial *serial);
-static void whiteheat_shutdown(struct usb_serial *serial);
+static void whiteheat_release(struct usb_serial *serial);
 static int  whiteheat_open(struct tty_struct *tty,
                        struct usb_serial_port *port, struct file *filp);
 static void whiteheat_close(struct usb_serial_port *port);
@@ -189,7 +189,7 @@ static struct usb_serial_driver whiteheat_device = {
        .id_table =             id_table_std,
        .num_ports =            4,
        .attach =               whiteheat_attach,
-       .shutdown =             whiteheat_shutdown,
+       .release =              whiteheat_release,
        .open =                 whiteheat_open,
        .close =                whiteheat_close,
        .write =                whiteheat_write,
@@ -617,7 +617,7 @@ no_command_buffer:
 }
 
 
-static void whiteheat_shutdown(struct usb_serial *serial)
+static void whiteheat_release(struct usb_serial *serial)
 {
        struct usb_serial_port *command_port;
        struct usb_serial_port *port;
index 2dd9bd4..ec17c96 100644 (file)
@@ -52,7 +52,7 @@ int usb_stor_euscsi_init(struct us_data *us)
        us->iobuf[0] = 0x1;
        result = usb_stor_control_msg(us, us->send_ctrl_pipe,
                        0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR,
-                       0x01, 0x0, us->iobuf, 0x1, 5*HZ);
+                       0x01, 0x0, us->iobuf, 0x1, 5000);
        US_DEBUGP("-- result is %d\n", result);
 
        return 0;
@@ -80,14 +80,16 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
 
        res = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb,
                        US_BULK_CB_WRAP_LEN, &partial);
-       if(res)
-               return res;
+       if (res)
+               return -EIO;
 
        US_DEBUGP("Getting status packet...\n");
        res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs,
                        US_BULK_CS_WRAP_LEN, &partial);
+       if (res)
+               return -EIO;
 
-       return (res ? -1 : 0);
+       return 0;
 }
 
 /* This places the HUAWEI E220 devices in multi-port mode */
@@ -99,6 +101,6 @@ int usb_stor_huawei_e220_init(struct us_data *us)
                                      USB_REQ_SET_FEATURE,
                                      USB_TYPE_STANDARD | USB_RECIP_DEVICE,
                                      0x01, 0x0, NULL, 0x0, 1000);
-       US_DEBUGP("usb_control_msg performing result is %d\n", result);
-       return (result ? 0 : -1);
+       US_DEBUGP("Huawei mode set result is %d\n", result);
+       return (result ? 0 : -ENODEV);
 }
index 353f922..d41cc0a 100644 (file)
@@ -37,7 +37,7 @@ MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default),"
 
 #define RESPONSE_LEN 1024
 
-static int option_rezero(struct us_data *us, int ep_in, int ep_out)
+static int option_rezero(struct us_data *us)
 {
        const unsigned char rezero_msg[] = {
          0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12,
@@ -54,10 +54,10 @@ static int option_rezero(struct us_data *us, int ep_in, int ep_out)
        if (buffer == NULL)
                return USB_STOR_TRANSPORT_ERROR;
 
-       memcpy(buffer, rezero_msg, sizeof (rezero_msg));
+       memcpy(buffer, rezero_msg, sizeof(rezero_msg));
        result = usb_stor_bulk_transfer_buf(us,
-                       usb_sndbulkpipe(us->pusb_dev, ep_out),
-                       buffer, sizeof (rezero_msg), NULL);
+                       us->send_bulk_pipe,
+                       buffer, sizeof(rezero_msg), NULL);
        if (result != USB_STOR_XFER_GOOD) {
                result = USB_STOR_XFER_ERROR;
                goto out;
@@ -66,9 +66,15 @@ static int option_rezero(struct us_data *us, int ep_in, int ep_out)
        /* Some of the devices need to be asked for a response, but we don't
         * care what that response is.
         */
-       result = usb_stor_bulk_transfer_buf(us,
-                       usb_sndbulkpipe(us->pusb_dev, ep_out),
+       usb_stor_bulk_transfer_buf(us,
+                       us->recv_bulk_pipe,
                        buffer, RESPONSE_LEN, NULL);
+
+       /* Read the CSW */
+       usb_stor_bulk_transfer_buf(us,
+                       us->recv_bulk_pipe,
+                       buffer, 13, NULL);
+
        result = USB_STOR_XFER_GOOD;
 
 out:
@@ -76,63 +82,75 @@ out:
        return result;
 }
 
-int option_ms_init(struct us_data *us)
+static int option_inquiry(struct us_data *us)
 {
-       struct usb_device *udev;
-       struct usb_interface *intf;
-       struct usb_host_interface *iface_desc;
-       struct usb_endpoint_descriptor *endpoint = NULL;
-       u8 ep_in = 0, ep_out = 0;
-       int ep_in_size = 0, ep_out_size = 0;
-       int i, result;
-
-       udev = us->pusb_dev;
-       intf = us->pusb_intf;
-
-       /* Ensure it's really a ZeroCD device; devices that are already
-        * in modem mode return 0xFF for class, subclass, and protocol.
-        */
-       if (udev->descriptor.bDeviceClass != 0 ||
-           udev->descriptor.bDeviceSubClass != 0 ||
-           udev->descriptor.bDeviceProtocol != 0)
-               return USB_STOR_TRANSPORT_GOOD;
+       const unsigned char inquiry_msg[] = {
+         0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
+         0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12,
+         0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       char *buffer;
+       int result;
 
-       US_DEBUGP("Option MS: option_ms_init called\n");
+       US_DEBUGP("Option MS: %s", "device inquiry for vendor name\n");
 
-       /* Find the right mass storage interface */
-       iface_desc = intf->cur_altsetting;
-       if (iface_desc->desc.bInterfaceClass != 0x8 ||
-           iface_desc->desc.bInterfaceSubClass != 0x6 ||
-           iface_desc->desc.bInterfaceProtocol != 0x50) {
-               US_DEBUGP("Option MS: mass storage interface not found, no action "
-                         "required\n");
-               return USB_STOR_TRANSPORT_GOOD;
-       }
+       buffer = kzalloc(0x24, GFP_KERNEL);
+       if (buffer == NULL)
+               return USB_STOR_TRANSPORT_ERROR;
 
-       /* Find the mass storage bulk endpoints */
-       for (i = 0; i < iface_desc->desc.bNumEndpoints && (!ep_in_size || !ep_out_size); ++i) {
-               endpoint = &iface_desc->endpoint[i].desc;
-
-               if (usb_endpoint_is_bulk_in(endpoint)) {
-                       ep_in = usb_endpoint_num(endpoint);
-                       ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
-               } else if (usb_endpoint_is_bulk_out(endpoint)) {
-                       ep_out = usb_endpoint_num(endpoint);
-                       ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize);
-               }
+       memcpy(buffer, inquiry_msg, sizeof(inquiry_msg));
+       result = usb_stor_bulk_transfer_buf(us,
+                       us->send_bulk_pipe,
+                       buffer, sizeof(inquiry_msg), NULL);
+       if (result != USB_STOR_XFER_GOOD) {
+               result = USB_STOR_XFER_ERROR;
+               goto out;
        }
 
-       /* Can't find the mass storage endpoints */
-       if (!ep_in_size || !ep_out_size) {
-               US_DEBUGP("Option MS: mass storage endpoints not found, no action "
-                         "required\n");
-               return USB_STOR_TRANSPORT_GOOD;
+       result = usb_stor_bulk_transfer_buf(us,
+                       us->recv_bulk_pipe,
+                       buffer, 0x24, NULL);
+       if (result != USB_STOR_XFER_GOOD) {
+               result = USB_STOR_XFER_ERROR;
+               goto out;
        }
 
+       result = memcmp(buffer+8, "Option", 6);
+
+       /* Read the CSW */
+       usb_stor_bulk_transfer_buf(us,
+                       us->recv_bulk_pipe,
+                       buffer, 13, NULL);
+
+out:
+       kfree(buffer);
+       return result;
+}
+
+
+int option_ms_init(struct us_data *us)
+{
+       int result;
+
+       US_DEBUGP("Option MS: option_ms_init called\n");
+
+       /* Additional test for vendor information via INQUIRY,
+        * because some vendor/product IDs are ambiguous
+        */
+       result = option_inquiry(us);
+       if (result != 0) {
+               US_DEBUGP("Option MS: vendor is not Option or not determinable,"
+                         " no action taken\n");
+               return 0;
+       } else
+               US_DEBUGP("Option MS: this is a genuine Option device,"
+                         " proceeding\n");
+
        /* Force Modem mode */
        if (option_zero_cd == ZCD_FORCE_MODEM) {
                US_DEBUGP("Option MS: %s", "Forcing Modem Mode\n");
-               result = option_rezero(us, ep_in, ep_out);
+               result = option_rezero(us);
                if (result != USB_STOR_XFER_GOOD)
                        US_DEBUGP("Option MS: Failed to switch to modem mode.\n");
                return -EIO;
@@ -142,6 +160,6 @@ int option_ms_init(struct us_data *us)
                          " requests it\n");
        }
 
-       return USB_STOR_TRANSPORT_GOOD;
+       return 0;
 }
 
index 4359a2c..4395c41 100644 (file)
@@ -202,6 +202,6 @@ int sierra_ms_init(struct us_data *us)
 complete:
        result = device_create_file(&us->pusb_intf->dev, &dev_attr_truinst);
 
-       return USB_STOR_TRANSPORT_GOOD;
+       return 0;
 }
 
index 4b8b690..1b9c5dd 100644 (file)
@@ -1385,7 +1385,7 @@ UNUSUAL_DEV(  0x10d6, 0x2200, 0x0100, 0x0100,
 UNUSUAL_DEV(  0x1186, 0x3e04, 0x0000, 0x0000,
            "D-Link",
            "USB Mass Storage",
-           US_SC_DEVICE, US_PR_DEVICE, option_ms_init, 0),
+           US_SC_DEVICE, US_PR_DEVICE, option_ms_init, US_FL_IGNORE_DEVICE),
 
 /* Reported by Kevin Lloyd <linux@sierrawireless.com>
  * Entry is needed for the initializer function override,
index 9367b62..89cd2de 100644 (file)
@@ -513,7 +513,7 @@ befs_utf2nls(struct super_block *sb, const char *in,
 {
        struct nls_table *nls = BEFS_SB(sb)->nls;
        int i, o;
-       wchar_t uni;
+       unicode_t uni;
        int unilen, utflen;
        char *result;
        /* The utf8->nls conversion won't make the final nls string bigger
@@ -539,16 +539,16 @@ befs_utf2nls(struct super_block *sb, const char *in,
        for (i = o = 0; i < in_len; i += utflen, o += unilen) {
 
                /* convert from UTF-8 to Unicode */
-               utflen = utf8_mbtowc(&uni, &in[i], in_len - i);
-               if (utflen < 0) {
+               utflen = utf8_to_utf32(&in[i], in_len - i, &uni);
+               if (utflen < 0)
                        goto conv_err;
-               }
 
                /* convert from Unicode to nls */
+               if (uni > MAX_WCHAR_T)
+                       goto conv_err;
                unilen = nls->uni2char(uni, &result[o], in_len - o);
-               if (unilen < 0) {
+               if (unilen < 0)
                        goto conv_err;
-               }
        }
        result[o] = '\0';
        *out_len = o;
@@ -619,15 +619,13 @@ befs_nls2utf(struct super_block *sb, const char *in,
 
                /* convert from nls to unicode */
                unilen = nls->char2uni(&in[i], in_len - i, &uni);
-               if (unilen < 0) {
+               if (unilen < 0)
                        goto conv_err;
-               }
 
                /* convert from unicode to UTF-8 */
-               utflen = utf8_wctomb(&result[o], uni, 3);
-               if (utflen <= 0) {
+               utflen = utf32_to_utf8(uni, &result[o], 3);
+               if (utflen <= 0)
                        goto conv_err;
-               }
        }
 
        result[o] = '\0';
index 3b8e71b..38ff75a 100644 (file)
 #include <asm/uaccess.h>
 #include "fat.h"
 
+/*
+ * Maximum buffer size of short name.
+ * [(MSDOS_NAME + '.') * max one char + nul]
+ * For msdos style, ['.' (hidden) + MSDOS_NAME + '.' + nul]
+ */
+#define FAT_MAX_SHORT_SIZE     ((MSDOS_NAME + 1) * NLS_MAX_CHARSET_SIZE + 1)
+/*
+ * Maximum buffer size of unicode chars from slots.
+ * [(max longname slots * 13 (size in a slot) + nul) * sizeof(wchar_t)]
+ */
+#define FAT_MAX_UNI_CHARS      ((MSDOS_SLOTS - 1) * 13 + 1)
+#define FAT_MAX_UNI_SIZE       (FAT_MAX_UNI_CHARS * sizeof(wchar_t))
+
 static inline loff_t fat_make_i_pos(struct super_block *sb,
                                    struct buffer_head *bh,
                                    struct msdos_dir_entry *de)
@@ -171,7 +184,8 @@ static inline int fat_uni_to_x8(struct msdos_sb_info *sbi, const wchar_t *uni,
                                unsigned char *buf, int size)
 {
        if (sbi->options.utf8)
-               return utf8_wcstombs(buf, uni, size);
+               return utf16s_to_utf8s(uni, FAT_MAX_UNI_CHARS,
+                               UTF16_HOST_ENDIAN, buf, size);
        else
                return uni16_to_x8(buf, uni, size, sbi->options.unicode_xlate,
                                   sbi->nls_io);
@@ -325,19 +339,6 @@ parse_long:
 }
 
 /*
- * Maximum buffer size of short name.
- * [(MSDOS_NAME + '.') * max one char + nul]
- * For msdos style, ['.' (hidden) + MSDOS_NAME + '.' + nul]
- */
-#define FAT_MAX_SHORT_SIZE     ((MSDOS_NAME + 1) * NLS_MAX_CHARSET_SIZE + 1)
-/*
- * Maximum buffer size of unicode chars from slots.
- * [(max longname slots * 13 (size in a slot) + nul) * sizeof(wchar_t)]
- */
-#define FAT_MAX_UNI_CHARS      ((MSDOS_SLOTS - 1) * 13 + 1)
-#define FAT_MAX_UNI_SIZE       (FAT_MAX_UNI_CHARS * sizeof(wchar_t))
-
-/*
  * Return values: negative -> error, 0 -> not found, positive -> found,
  * value is the total amount of slots, including the shortname entry.
  */
index 8d6fdcf..73471b7 100644 (file)
@@ -502,11 +502,11 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,
        if (utf8) {
                int name_len = strlen(name);
 
-               *outlen = utf8_mbstowcs((wchar_t *)outname, name, PATH_MAX);
+               *outlen = utf8s_to_utf16s(name, PATH_MAX, (wchar_t *) outname);
 
                /*
                 * We stripped '.'s before and set len appropriately,
-                * but utf8_mbstowcs doesn't care about len
+                * but utf8s_to_utf16s doesn't care about len
                 */
                *outlen -= (name_len - len);
 
index 92c14b8..a048de8 100644 (file)
@@ -37,37 +37,6 @@ uni16_to_x8(unsigned char *ascii, __be16 *uni, int len, struct nls_table *nls)
        return (op - ascii);
 }
 
-/* Convert big endian wide character string to utf8 */
-static int
-wcsntombs_be(__u8 *s, const __u8 *pwcs, int inlen, int maxlen)
-{
-       const __u8 *ip;
-       __u8 *op;
-       int size;
-       __u16 c;
-
-       op = s;
-       ip = pwcs;
-       while ((*ip || ip[1]) && (maxlen > 0) && (inlen > 0)) {
-               c = (*ip << 8) | ip[1];
-               if (c > 0x7f) {
-                       size = utf8_wctomb(op, c, maxlen);
-                       if (size == -1) {
-                               /* Ignore character and move on */
-                               maxlen--;
-                       } else {
-                               op += size;
-                               maxlen -= size;
-                       }
-               } else {
-                       *op++ = (__u8) c;
-               }
-               ip += 2;
-               inlen--;
-       }
-       return (op - s);
-}
-
 int
 get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, struct inode * inode)
 {
@@ -79,8 +48,9 @@ get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, st
        nls = ISOFS_SB(inode->i_sb)->s_nls_iocharset;
 
        if (utf8) {
-               len = wcsntombs_be(outname, de->name,
-                               de->name_len[0] >> 1, PAGE_SIZE);
+               len = utf16s_to_utf8s((const wchar_t *) de->name,
+                               de->name_len[0] >> 1, UTF16_BIG_ENDIAN,
+                               outname, PAGE_SIZE);
        } else {
                len = uni16_to_x8(outname, (__be16 *) de->name,
                                de->name_len[0] >> 1, nls);
index 97645f1..0ec6237 100644 (file)
@@ -1113,11 +1113,13 @@ ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen,
 
                if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
                        int k;
+                       unicode_t u;
 
-                       k = utf8_mbtowc(&ec, iname, iname_end - iname);
-                       if (k < 0)
+                       k = utf8_to_utf32(iname, iname_end - iname, &u);
+                       if (k < 0 || u > MAX_WCHAR_T)
                                return -EINVAL;
                        iname += k;
+                       ec = u;
                } else {
                        if (*iname == NCP_ESC) {
                                int k;
@@ -1214,7 +1216,7 @@ ncp__vol2io(struct ncp_server *server, unsigned char *iname, unsigned int *ilen,
                if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
                        int k;
 
-                       k = utf8_wctomb(iname, ec, iname_end - iname);
+                       k = utf32_to_utf8(ec, iname, iname_end - iname);
                        if (k < 0) {
                                err = -ENAMETOOLONG;
                                goto quit;
index 9b0efda..477d37d 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/kmod.h>
 #include <linux/spinlock.h>
+#include <asm/byteorder.h>
 
 static struct nls_table default_table;
 static struct nls_table *tables = &default_table;
@@ -43,10 +44,17 @@ static const struct utf8_table utf8_table[] =
     {0,                                                       /* end of table    */}
 };
 
-int
-utf8_mbtowc(wchar_t *p, const __u8 *s, int n)
+#define UNICODE_MAX    0x0010ffff
+#define PLANE_SIZE     0x00010000
+
+#define SURROGATE_MASK 0xfffff800
+#define SURROGATE_PAIR 0x0000d800
+#define SURROGATE_LOW  0x00000400
+#define SURROGATE_BITS 0x000003ff
+
+int utf8_to_utf32(const u8 *s, int len, unicode_t *pu)
 {
-       long l;
+       unsigned long l;
        int c0, c, nc;
        const struct utf8_table *t;
   
@@ -57,12 +65,13 @@ utf8_mbtowc(wchar_t *p, const __u8 *s, int n)
                nc++;
                if ((c0 & t->cmask) == t->cval) {
                        l &= t->lmask;
-                       if (l < t->lval)
+                       if (l < t->lval || l > UNICODE_MAX ||
+                                       (l & SURROGATE_MASK) == SURROGATE_PAIR)
                                return -1;
-                       *p = l;
+                       *pu = (unicode_t) l;
                        return nc;
                }
-               if (n <= nc)
+               if (len <= nc)
                        return -1;
                s++;
                c = (*s ^ 0x80) & 0xFF;
@@ -72,90 +81,133 @@ utf8_mbtowc(wchar_t *p, const __u8 *s, int n)
        }
        return -1;
 }
+EXPORT_SYMBOL(utf8_to_utf32);
 
-int
-utf8_mbstowcs(wchar_t *pwcs, const __u8 *s, int n)
+int utf32_to_utf8(unicode_t u, u8 *s, int maxlen)
 {
-       __u16 *op;
-       const __u8 *ip;
-       int size;
-
-       op = pwcs;
-       ip = s;
-       while (*ip && n > 0) {
-               if (*ip & 0x80) {
-                       size = utf8_mbtowc(op, ip, n);
-                       if (size == -1) {
-                               /* Ignore character and move on */
-                               ip++;
-                               n--;
-                       } else {
-                               op++;
-                               ip += size;
-                               n -= size;
-                       }
-               } else {
-                       *op++ = *ip++;
-                       n--;
-               }
-       }
-       return (op - pwcs);
-}
-
-int
-utf8_wctomb(__u8 *s, wchar_t wc, int maxlen)
-{
-       long l;
+       unsigned long l;
        int c, nc;
        const struct utf8_table *t;
-  
+
        if (!s)
                return 0;
-  
-       l = wc;
+
+       l = u;
+       if (l > UNICODE_MAX || (l & SURROGATE_MASK) == SURROGATE_PAIR)
+               return -1;
+
        nc = 0;
        for (t = utf8_table; t->cmask && maxlen; t++, maxlen--) {
                nc++;
                if (l <= t->lmask) {
                        c = t->shift;
-                       *s = t->cval | (l >> c);
+                       *s = (u8) (t->cval | (l >> c));
                        while (c > 0) {
                                c -= 6;
                                s++;
-                               *s = 0x80 | ((l >> c) & 0x3F);
+                               *s = (u8) (0x80 | ((l >> c) & 0x3F));
                        }
                        return nc;
                }
        }
        return -1;
 }
+EXPORT_SYMBOL(utf32_to_utf8);
 
-int
-utf8_wcstombs(__u8 *s, const wchar_t *pwcs, int maxlen)
+int utf8s_to_utf16s(const u8 *s, int len, wchar_t *pwcs)
 {
-       const __u16 *ip;
-       __u8 *op;
+       u16 *op;
        int size;
+       unicode_t u;
+
+       op = pwcs;
+       while (*s && len > 0) {
+               if (*s & 0x80) {
+                       size = utf8_to_utf32(s, len, &u);
+                       if (size < 0) {
+                               /* Ignore character and move on */
+                               size = 1;
+                       } else if (u >= PLANE_SIZE) {
+                               u -= PLANE_SIZE;
+                               *op++ = (wchar_t) (SURROGATE_PAIR |
+                                               ((u >> 10) & SURROGATE_BITS));
+                               *op++ = (wchar_t) (SURROGATE_PAIR |
+                                               SURROGATE_LOW |
+                                               (u & SURROGATE_BITS));
+                       } else {
+                               *op++ = (wchar_t) u;
+                       }
+                       s += size;
+                       len -= size;
+               } else {
+                       *op++ = *s++;
+                       len--;
+               }
+       }
+       return op - pwcs;
+}
+EXPORT_SYMBOL(utf8s_to_utf16s);
+
+static inline unsigned long get_utf16(unsigned c, enum utf16_endian endian)
+{
+       switch (endian) {
+       default:
+               return c;
+       case UTF16_LITTLE_ENDIAN:
+               return __le16_to_cpu(c);
+       case UTF16_BIG_ENDIAN:
+               return __be16_to_cpu(c);
+       }
+}
+
+int utf16s_to_utf8s(const wchar_t *pwcs, int len, enum utf16_endian endian,
+               u8 *s, int maxlen)
+{
+       u8 *op;
+       int size;
+       unsigned long u, v;
 
        op = s;
-       ip = pwcs;
-       while (*ip && maxlen > 0) {
-               if (*ip > 0x7f) {
-                       size = utf8_wctomb(op, *ip, maxlen);
+       while (len > 0 && maxlen > 0) {
+               u = get_utf16(*pwcs, endian);
+               if (!u)
+                       break;
+               pwcs++;
+               len--;
+               if (u > 0x7f) {
+                       if ((u & SURROGATE_MASK) == SURROGATE_PAIR) {
+                               if (u & SURROGATE_LOW) {
+                                       /* Ignore character and move on */
+                                       continue;
+                               }
+                               if (len <= 0)
+                                       break;
+                               v = get_utf16(*pwcs, endian);
+                               if ((v & SURROGATE_MASK) != SURROGATE_PAIR ||
+                                               !(v & SURROGATE_LOW)) {
+                                       /* Ignore character and move on */
+                                       continue;
+                               }
+                               u = PLANE_SIZE + ((u & SURROGATE_BITS) << 10)
+                                               + (v & SURROGATE_BITS);
+                               pwcs++;
+                               len--;
+                       }
+                       size = utf32_to_utf8(u, op, maxlen);
                        if (size == -1) {
                                /* Ignore character and move on */
-                               maxlen--;
                        } else {
                                op += size;
                                maxlen -= size;
                        }
                } else {
-                       *op++ = (__u8) *ip;
+                       *op++ = (u8) u;
+                       maxlen--;
                }
-               ip++;
        }
-       return (op - s);
+       return op - s;
 }
+EXPORT_SYMBOL(utf16s_to_utf8s);
 
 int register_nls(struct nls_table * nls)
 {
@@ -467,9 +519,5 @@ EXPORT_SYMBOL(unregister_nls);
 EXPORT_SYMBOL(unload_nls);
 EXPORT_SYMBOL(load_nls);
 EXPORT_SYMBOL(load_nls_default);
-EXPORT_SYMBOL(utf8_mbtowc);
-EXPORT_SYMBOL(utf8_mbstowcs);
-EXPORT_SYMBOL(utf8_wctomb);
-EXPORT_SYMBOL(utf8_wcstombs);
 
 MODULE_LICENSE("Dual BSD/GPL");
index aa2c42f..0d60a44 100644 (file)
@@ -15,7 +15,11 @@ static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
 {
        int n;
 
-       if ( (n = utf8_wctomb(out, uni, boundlen)) == -1) {
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       n = utf32_to_utf8(uni, out, boundlen);
+       if (n < 0) {
                *out = '?';
                return -EINVAL;
        }
@@ -25,11 +29,14 @@ static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
 static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
 {
        int n;
+       unicode_t u;
 
-       if ( (n = utf8_mbtowc(uni, rawstring, boundlen)) == -1) {
+       n = utf8_to_utf32(rawstring, boundlen, &u);
+       if (n < 0 || u > MAX_WCHAR_T) {
                *uni = 0x003f;  /* ? */
-               n = -EINVAL;
+               return -EINVAL;
        }
+       *uni = (wchar_t) u;
        return n;
 }
 
index 52b1a76..d47beef 100644 (file)
@@ -3,8 +3,23 @@
 
 #include <linux/init.h>
 
-/* unicode character */
-typedef __u16 wchar_t;
+/* Unicode has changed over the years.  Unicode code points no longer
+ * fit into 16 bits; as of Unicode 5 valid code points range from 0
+ * to 0x10ffff (17 planes, where each plane holds 65536 code points).
+ *
+ * The original decision to represent Unicode characters as 16-bit
+ * wchar_t values is now outdated.  But plane 0 still includes the
+ * most commonly used characters, so we will retain it.  The newer
+ * 32-bit unicode_t type can be used when it is necessary to
+ * represent the full Unicode character set.
+ */
+
+/* Plane-0 Unicode character */
+typedef u16 wchar_t;
+#define MAX_WCHAR_T    0xffff
+
+/* Arbitrary Unicode character */
+typedef u32 unicode_t;
 
 struct nls_table {
        const char *charset;
@@ -21,6 +36,13 @@ struct nls_table {
 /* this value hold the maximum octet of charset */
 #define NLS_MAX_CHARSET_SIZE 6 /* for UTF-8 */
 
+/* Byte order for UTF-16 strings */
+enum utf16_endian {
+       UTF16_HOST_ENDIAN,
+       UTF16_LITTLE_ENDIAN,
+       UTF16_BIG_ENDIAN
+};
+
 /* nls.c */
 extern int register_nls(struct nls_table *);
 extern int unregister_nls(struct nls_table *);
@@ -28,10 +50,11 @@ extern struct nls_table *load_nls(char *);
 extern void unload_nls(struct nls_table *);
 extern struct nls_table *load_nls_default(void);
 
-extern int utf8_mbtowc(wchar_t *, const __u8 *, int);
-extern int utf8_mbstowcs(wchar_t *, const __u8 *, int);
-extern int utf8_wctomb(__u8 *, wchar_t, int);
-extern int utf8_wcstombs(__u8 *, const wchar_t *, int);
+extern int utf8_to_utf32(const u8 *s, int len, unicode_t *pu);
+extern int utf32_to_utf8(unicode_t u, u8 *s, int maxlen);
+extern int utf8s_to_utf16s(const u8 *s, int len, wchar_t *pwcs);
+extern int utf16s_to_utf8s(const wchar_t *pwcs, int len,
+               enum utf16_endian endian, u8 *s, int maxlen);
 
 static inline unsigned char nls_tolower(struct nls_table *t, unsigned char c)
 {
index 72698d8..8e366bb 100644 (file)
@@ -124,6 +124,14 @@ typedef int __bitwise pci_power_t;
 #define PCI_UNKNOWN    ((pci_power_t __force) 5)
 #define PCI_POWER_ERROR        ((pci_power_t __force) -1)
 
+/* Remember to update this when the list above changes! */
+extern const char *pci_power_names[];
+
+static inline const char *pci_power_name(pci_power_t state)
+{
+       return pci_power_names[1 + (int) state];
+}
+
 #define PCI_PM_D2_DELAY        200
 #define PCI_PM_D3_WAIT 10
 #define PCI_PM_BUS_WAIT        50
index aa01d38..a3b0003 100644 (file)
 #define PCI_CLASS_SERIAL_USB_UHCI      0x0c0300
 #define PCI_CLASS_SERIAL_USB_OHCI      0x0c0310
 #define PCI_CLASS_SERIAL_USB_EHCI      0x0c0320
+#define PCI_CLASS_SERIAL_USB_XHCI      0x0c0330
 #define PCI_CLASS_SERIAL_FIBER         0x0c04
 #define PCI_CLASS_SERIAL_SMBUS         0x0c05
 
index 34cdfca..84929e9 100644 (file)
@@ -36,6 +36,7 @@ struct wusb_dev;
  *  - configs have one (often) or more interfaces;
  *  - interfaces have one (usually) or more settings;
  *  - each interface setting has zero or (usually) more endpoints.
+ *  - a SuperSpeed endpoint has a companion descriptor
  *
  * And there might be other descriptors mixed in with those.
  *
@@ -44,6 +45,19 @@ struct wusb_dev;
 
 struct ep_device;
 
+/* For SS devices */
+/**
+ * struct usb_host_ss_ep_comp - Valid for SuperSpeed devices only
+ * @desc: endpoint companion descriptor, wMaxPacketSize in native byteorder
+ * @extra: descriptors following this endpoint companion descriptor
+ * @extralen: how many bytes of "extra" are valid
+ */
+struct usb_host_ss_ep_comp {
+       struct usb_ss_ep_comp_descriptor        desc;
+       unsigned char                           *extra;   /* Extra descriptors */
+       int                                     extralen;
+};
+
 /**
  * struct usb_host_endpoint - host-side endpoint descriptor and queue
  * @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder
@@ -51,6 +65,7 @@ struct ep_device;
  * @hcpriv: for use by HCD; typically holds hardware dma queue head (QH)
  *     with one or more transfer descriptors (TDs) per urb
  * @ep_dev: ep_device for sysfs info
+ * @ss_ep_comp: companion descriptor information for this endpoint
  * @extra: descriptors following this endpoint in the configuration
  * @extralen: how many bytes of "extra" are valid
  * @enabled: URBs may be submitted to this endpoint
@@ -63,6 +78,7 @@ struct usb_host_endpoint {
        struct list_head                urb_list;
        void                            *hcpriv;
        struct ep_device                *ep_dev;        /* For sysfs info */
+       struct usb_host_ss_ep_comp      *ss_ep_comp;    /* For SS devices */
 
        unsigned char *extra;   /* Extra descriptors */
        int extralen;
@@ -336,7 +352,6 @@ struct usb_bus {
 #ifdef CONFIG_USB_DEVICEFS
        struct dentry *usbfs_dentry;    /* usbfs dentry entry for the bus */
 #endif
-       struct device *dev;             /* device for this bus */
 
 #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
        struct mon_bus *mon_bus;        /* non-null when associated */
@@ -363,6 +378,7 @@ struct usb_tt;
  * struct usb_device - kernel's representation of a USB device
  * @devnum: device number; address on a USB bus
  * @devpath: device ID string for use in messages (e.g., /port/...)
+ * @route: tree topology hex string for use with xHCI
  * @state: device state: configured, not attached, etc.
  * @speed: device speed: high/full/low (or error)
  * @tt: Transaction Translator info; used with low/full speed dev, highspeed hub
@@ -420,6 +436,7 @@ struct usb_tt;
  * @skip_sys_resume: skip the next system resume
  * @wusb_dev: if this is a Wireless USB device, link to the WUSB
  *     specific data for the device.
+ * @slot_id: Slot ID assigned by xHCI
  *
  * Notes:
  * Usbcore drivers should not set usbdev->state directly.  Instead use
@@ -428,6 +445,7 @@ struct usb_tt;
 struct usb_device {
        int             devnum;
        char            devpath [16];
+       u32             route;
        enum usb_device_state   state;
        enum usb_device_speed   speed;
 
@@ -503,6 +521,7 @@ struct usb_device {
        unsigned skip_sys_resume:1;
 #endif
        struct wusb_dev *wusb_dev;
+       int slot_id;
 };
 #define        to_usb_device(d) container_of(d, struct usb_device, dev)
 
@@ -1044,7 +1063,9 @@ typedef void (*usb_complete_t)(struct urb *);
  * @setup_dma: For control transfers with URB_NO_SETUP_DMA_MAP set, the
  *     device driver has provided this DMA address for the setup packet.
  *     The host controller driver should use this in preference to
- *     setup_packet.
+ *     setup_packet, but the HCD may chose to ignore the address if it must
+ *     copy the setup packet into internal structures.  Therefore, setup_packet
+ *     must always point to a valid buffer.
  * @start_frame: Returns the initial frame for isochronous transfers.
  * @number_of_packets: Lists the number of ISO transfer buffers.
  * @interval: Specifies the polling interval for interrupt or isochronous
@@ -1180,6 +1201,8 @@ struct urb {
        unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/
        void *transfer_buffer;          /* (in) associated data buffer */
        dma_addr_t transfer_dma;        /* (in) dma addr for transfer_buffer */
+       struct usb_sg_request *sg;      /* (in) scatter gather buffer list */
+       int num_sgs;                    /* (in) number of entries in the sg list */
        u32 transfer_buffer_length;     /* (in) data buffer length */
        u32 actual_length;              /* (return) actual transfer length */
        unsigned char *setup_packet;    /* (in) setup packet (control only) */
@@ -1425,8 +1448,8 @@ struct usb_sg_request {
        int                     status;
        size_t                  bytes;
 
-       /*
-        * members below are private: to usbcore,
+       /* private:
+        * members below are private to usbcore,
         * and are not provided for driver access!
         */
        spinlock_t              lock;
@@ -1561,6 +1584,9 @@ extern void usb_unregister_notify(struct notifier_block *nb);
 #define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \
        format "\n" , ## arg)
 
+/* debugfs stuff */
+extern struct dentry *usb_debug_root;
+
 #endif  /* __KERNEL__ */
 
 #endif
index 8cb025f..b5744bc 100644 (file)
 #define USB_SUBCLASS_AUDIOCONTROL      0x01
 #define USB_SUBCLASS_AUDIOSTREAMING    0x02
 #define USB_SUBCLASS_MIDISTREAMING     0x03
+#define USB_SUBCLASS_VENDOR_SPEC       0xff
 
+/* A.5 Audio Class-Specific AC interface Descriptor Subtypes*/
+#define HEADER                         0x01
+#define INPUT_TERMINAL                 0x02
+#define OUTPUT_TERMINAL                        0x03
+#define MIXER_UNIT                     0x04
+#define SELECTOR_UNIT                  0x05
+#define FEATURE_UNIT                   0x06
+#define PROCESSING_UNIT                        0x07
+#define EXTENSION_UNIT                 0x08
+
+#define AS_GENERAL                     0x01
+#define FORMAT_TYPE                    0x02
+#define FORMAT_SPECIFIC                        0x03
+
+#define EP_GENERAL                     0x01
+
+#define MS_GENERAL                     0x01
+#define MIDI_IN_JACK                   0x02
+#define MIDI_OUT_JACK                  0x03
+
+/* endpoint attributes */
+#define EP_ATTR_MASK                   0x0c
+#define EP_ATTR_ASYNC                  0x04
+#define EP_ATTR_ADAPTIVE               0x08
+#define EP_ATTR_SYNC                   0x0c
+
+/* cs endpoint attributes */
+#define EP_CS_ATTR_SAMPLE_RATE         0x01
+#define EP_CS_ATTR_PITCH_CONTROL       0x02
+#define EP_CS_ATTR_FILL_MAX            0x80
+
+/* Audio Class specific Request Codes */
+#define USB_AUDIO_SET_INTF             0x21
+#define USB_AUDIO_SET_ENDPOINT         0x22
+#define USB_AUDIO_GET_INTF             0xa1
+#define USB_AUDIO_GET_ENDPOINT         0xa2
+
+#define SET_   0x00
+#define GET_   0x80
+
+#define _CUR   0x1
+#define _MIN   0x2
+#define _MAX   0x3
+#define _RES   0x4
+#define _MEM   0x5
+
+#define SET_CUR                (SET_ | _CUR)
+#define GET_CUR                (GET_ | _CUR)
+#define SET_MIN                (SET_ | _MIN)
+#define GET_MIN                (GET_ | _MIN)
+#define SET_MAX                (SET_ | _MAX)
+#define GET_MAX                (GET_ | _MAX)
+#define SET_RES                (SET_ | _RES)
+#define GET_RES                (GET_ | _RES)
+#define SET_MEM                (SET_ | _MEM)
+#define GET_MEM                (GET_ | _MEM)
+
+#define GET_STAT       0xff
+
+#define USB_AC_TERMINAL_UNDEFINED      0x100
+#define USB_AC_TERMINAL_STREAMING      0x101
+#define USB_AC_TERMINAL_VENDOR_SPEC    0x1FF
+
+/* Terminal Control Selectors */
 /* 4.3.2  Class-Specific AC Interface Descriptor */
 struct usb_ac_header_descriptor {
-       __u8  bLength;                  /* 8+n */
+       __u8  bLength;                  /* 8 + n */
        __u8  bDescriptorType;          /* USB_DT_CS_INTERFACE */
        __u8  bDescriptorSubtype;       /* USB_MS_HEADER */
        __le16 bcdADC;                  /* 0x0100 */
@@ -36,7 +101,7 @@ struct usb_ac_header_descriptor {
        __u8  baInterfaceNr[];          /* [n] */
 } __attribute__ ((packed));
 
-#define USB_DT_AC_HEADER_SIZE(n)       (8+(n))
+#define USB_DT_AC_HEADER_SIZE(n)       (8 + (n))
 
 /* As above, but more useful for defining your own descriptors: */
 #define DECLARE_USB_AC_HEADER_DESCRIPTOR(n)                    \
@@ -50,4 +115,200 @@ struct usb_ac_header_descriptor_##n {                              \
        __u8  baInterfaceNr[n];                                 \
 } __attribute__ ((packed))
 
+/* 4.3.2.1 Input Terminal Descriptor */
+struct usb_input_terminal_descriptor {
+       __u8  bLength;                  /* in bytes: 12 */
+       __u8  bDescriptorType;          /* CS_INTERFACE descriptor type */
+       __u8  bDescriptorSubtype;       /* INPUT_TERMINAL descriptor subtype */
+       __u8  bTerminalID;              /* Constant uniquely terminal ID */
+       __le16 wTerminalType;           /* USB Audio Terminal Types */
+       __u8  bAssocTerminal;           /* ID of the Output Terminal associated */
+       __u8  bNrChannels;              /* Number of logical output channels */
+       __le16 wChannelConfig;
+       __u8  iChannelNames;
+       __u8  iTerminal;
+} __attribute__ ((packed));
+
+#define USB_DT_AC_INPUT_TERMINAL_SIZE                  12
+
+#define USB_AC_INPUT_TERMINAL_UNDEFINED                        0x200
+#define USB_AC_INPUT_TERMINAL_MICROPHONE               0x201
+#define USB_AC_INPUT_TERMINAL_DESKTOP_MICROPHONE       0x202
+#define USB_AC_INPUT_TERMINAL_PERSONAL_MICROPHONE      0x203
+#define USB_AC_INPUT_TERMINAL_OMNI_DIR_MICROPHONE      0x204
+#define USB_AC_INPUT_TERMINAL_MICROPHONE_ARRAY         0x205
+#define USB_AC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY    0x206
+
+/* 4.3.2.2 Output Terminal Descriptor */
+struct usb_output_terminal_descriptor {
+       __u8  bLength;                  /* in bytes: 9 */
+       __u8  bDescriptorType;          /* CS_INTERFACE descriptor type */
+       __u8  bDescriptorSubtype;       /* OUTPUT_TERMINAL descriptor subtype */
+       __u8  bTerminalID;              /* Constant uniquely terminal ID */
+       __le16 wTerminalType;           /* USB Audio Terminal Types */
+       __u8  bAssocTerminal;           /* ID of the Input Terminal associated */
+       __u8  bSourceID;                /* ID of the connected Unit or Terminal*/
+       __u8  iTerminal;
+} __attribute__ ((packed));
+
+#define USB_DT_AC_OUTPUT_TERMINAL_SIZE                         9
+
+#define USB_AC_OUTPUT_TERMINAL_UNDEFINED                       0x300
+#define USB_AC_OUTPUT_TERMINAL_SPEAKER                         0x301
+#define USB_AC_OUTPUT_TERMINAL_HEADPHONES                      0x302
+#define USB_AC_OUTPUT_TERMINAL_HEAD_MOUNTED_DISPLAY_AUDIO      0x303
+#define USB_AC_OUTPUT_TERMINAL_DESKTOP_SPEAKER                 0x304
+#define USB_AC_OUTPUT_TERMINAL_ROOM_SPEAKER                    0x305
+#define USB_AC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER           0x306
+#define USB_AC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER                0x307
+
+/* Set bControlSize = 2 as default setting */
+#define USB_DT_AC_FEATURE_UNIT_SIZE(ch)                (7 + ((ch) + 1) * 2)
+
+/* As above, but more useful for defining your own descriptors: */
+#define DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(ch)             \
+struct usb_ac_feature_unit_descriptor_##ch {                   \
+       __u8  bLength;                                          \
+       __u8  bDescriptorType;                                  \
+       __u8  bDescriptorSubtype;                               \
+       __u8  bUnitID;                                          \
+       __u8  bSourceID;                                        \
+       __u8  bControlSize;                                     \
+       __le16 bmaControls[ch + 1];                             \
+       __u8  iFeature;                                         \
+} __attribute__ ((packed))
+
+/* 4.5.2 Class-Specific AS Interface Descriptor */
+struct usb_as_header_descriptor {
+       __u8  bLength;                  /* in bytes: 7 */
+       __u8  bDescriptorType;          /* USB_DT_CS_INTERFACE */
+       __u8  bDescriptorSubtype;       /* AS_GENERAL */
+       __u8  bTerminalLink;            /* Terminal ID of connected Terminal */
+       __u8  bDelay;                   /* Delay introduced by the data path */
+       __le16 wFormatTag;              /* The Audio Data Format */
+} __attribute__ ((packed));
+
+#define USB_DT_AS_HEADER_SIZE          7
+
+#define USB_AS_AUDIO_FORMAT_TYPE_I_UNDEFINED   0x0
+#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM         0x1
+#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM8                0x2
+#define USB_AS_AUDIO_FORMAT_TYPE_I_IEEE_FLOAT  0x3
+#define USB_AS_AUDIO_FORMAT_TYPE_I_ALAW                0x4
+#define USB_AS_AUDIO_FORMAT_TYPE_I_MULAW       0x5
+
+struct usb_as_format_type_i_continuous_descriptor {
+       __u8  bLength;                  /* in bytes: 8 + (ns * 3) */
+       __u8  bDescriptorType;          /* USB_DT_CS_INTERFACE */
+       __u8  bDescriptorSubtype;       /* FORMAT_TYPE */
+       __u8  bFormatType;              /* FORMAT_TYPE_1 */
+       __u8  bNrChannels;              /* physical channels in the stream */
+       __u8  bSubframeSize;            /* */
+       __u8  bBitResolution;
+       __u8  bSamFreqType;
+       __u8  tLowerSamFreq[3];
+       __u8  tUpperSamFreq[3];
+} __attribute__ ((packed));
+
+#define USB_AS_FORMAT_TYPE_I_CONTINUOUS_DESC_SIZE      14
+
+struct usb_as_formate_type_i_discrete_descriptor {
+       __u8  bLength;                  /* in bytes: 8 + (ns * 3) */
+       __u8  bDescriptorType;          /* USB_DT_CS_INTERFACE */
+       __u8  bDescriptorSubtype;       /* FORMAT_TYPE */
+       __u8  bFormatType;              /* FORMAT_TYPE_1 */
+       __u8  bNrChannels;              /* physical channels in the stream */
+       __u8  bSubframeSize;            /* */
+       __u8  bBitResolution;
+       __u8  bSamFreqType;
+       __u8  tSamFreq[][3];
+} __attribute__ ((packed));
+
+#define DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(n)          \
+struct usb_as_formate_type_i_discrete_descriptor_##n {         \
+       __u8  bLength;                                          \
+       __u8  bDescriptorType;                                  \
+       __u8  bDescriptorSubtype;                               \
+       __u8  bFormatType;                                      \
+       __u8  bNrChannels;                                      \
+       __u8  bSubframeSize;                                    \
+       __u8  bBitResolution;                                   \
+       __u8  bSamFreqType;                                     \
+       __u8  tSamFreq[n][3];                                   \
+} __attribute__ ((packed))
+
+#define USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n)     (8 + (n * 3))
+
+#define USB_AS_FORMAT_TYPE_UNDEFINED   0x0
+#define USB_AS_FORMAT_TYPE_I           0x1
+#define USB_AS_FORMAT_TYPE_II          0x2
+#define USB_AS_FORMAT_TYPE_III         0x3
+
+#define USB_AS_ENDPOINT_ASYNC          (1 << 2)
+#define USB_AS_ENDPOINT_ADAPTIVE       (2 << 2)
+#define USB_AS_ENDPOINT_SYNC           (3 << 2)
+
+struct usb_as_iso_endpoint_descriptor {
+       __u8  bLength;                  /* in bytes: 7 */
+       __u8  bDescriptorType;          /* USB_DT_CS_ENDPOINT */
+       __u8  bDescriptorSubtype;       /* EP_GENERAL */
+       __u8  bmAttributes;
+       __u8  bLockDelayUnits;
+       __le16 wLockDelay;
+};
+#define USB_AS_ISO_ENDPOINT_DESC_SIZE  7
+
+#define FU_CONTROL_UNDEFINED           0x00
+#define MUTE_CONTROL                   0x01
+#define VOLUME_CONTROL                 0x02
+#define BASS_CONTROL                   0x03
+#define MID_CONTROL                    0x04
+#define TREBLE_CONTROL                 0x05
+#define GRAPHIC_EQUALIZER_CONTROL      0x06
+#define AUTOMATIC_GAIN_CONTROL         0x07
+#define DELAY_CONTROL                  0x08
+#define BASS_BOOST_CONTROL             0x09
+#define LOUDNESS_CONTROL               0x0a
+
+#define FU_MUTE                (1 << (MUTE_CONTROL - 1))
+#define FU_VOLUME      (1 << (VOLUME_CONTROL - 1))
+#define FU_BASS                (1 << (BASS_CONTROL - 1))
+#define FU_MID         (1 << (MID_CONTROL - 1))
+#define FU_TREBLE      (1 << (TREBLE_CONTROL - 1))
+#define FU_GRAPHIC_EQ  (1 << (GRAPHIC_EQUALIZER_CONTROL - 1))
+#define FU_AUTO_GAIN   (1 << (AUTOMATIC_GAIN_CONTROL - 1))
+#define FU_DELAY       (1 << (DELAY_CONTROL - 1))
+#define FU_BASS_BOOST  (1 << (BASS_BOOST_CONTROL - 1))
+#define FU_LOUDNESS    (1 << (LOUDNESS_CONTROL - 1))
+
+struct usb_audio_control {
+       struct list_head list;
+       const char *name;
+       u8 type;
+       int data[5];
+       int (*set)(struct usb_audio_control *con, u8 cmd, int value);
+       int (*get)(struct usb_audio_control *con, u8 cmd);
+};
+
+static inline int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
+{
+       con->data[cmd] = value;
+
+       return 0;
+}
+
+static inline int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
+{
+       return con->data[cmd];
+}
+
+struct usb_audio_control_selector {
+       struct list_head list;
+       struct list_head control;
+       u8 id;
+       const char *name;
+       u8 type;
+       struct usb_descriptor_header *desc;
+};
+
 #endif /* __LINUX_USB_AUDIO_H */
index b145119..9322363 100644 (file)
@@ -191,6 +191,8 @@ struct usb_ctrlrequest {
 #define USB_DT_WIRE_ADAPTER            0x21
 #define USB_DT_RPIPE                   0x22
 #define USB_DT_CS_RADIO_CONTROL                0x23
+/* From the USB 3.0 spec */
+#define        USB_DT_SS_ENDPOINT_COMP         0x30
 
 /* Conventional codes for class-specific descriptors.  The convention is
  * defined in the USB "Common Class" Spec (3.11).  Individual class specs
@@ -535,6 +537,20 @@ static inline int usb_endpoint_is_isoc_out(
 
 /*-------------------------------------------------------------------------*/
 
+/* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */
+struct usb_ss_ep_comp_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+
+       __u8  bMaxBurst;
+       __u8  bmAttributes;
+       __u16 wBytesPerInterval;
+} __attribute__ ((packed));
+
+#define USB_DT_SS_EP_COMP_SIZE         6
+
+/*-------------------------------------------------------------------------*/
+
 /* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */
 struct usb_qualifier_descriptor {
        __u8  bLength;
@@ -752,6 +768,7 @@ enum usb_device_speed {
        USB_SPEED_LOW, USB_SPEED_FULL,          /* usb 1.1 */
        USB_SPEED_HIGH,                         /* usb 2.0 */
        USB_SPEED_VARIABLE,                     /* wireless (usb 2.5) */
+       USB_SPEED_SUPER,                        /* usb 3.0 */
 };
 
 enum usb_device_state {
index acd7b0f..4f6bb3d 100644 (file)
@@ -124,6 +124,7 @@ struct usb_function {
        void                    (*suspend)(struct usb_function *);
        void                    (*resume)(struct usb_function *);
 
+       /* private: */
        /* internals */
        struct list_head                list;
 };
@@ -219,6 +220,7 @@ struct usb_configuration {
 
        struct usb_composite_dev        *cdev;
 
+       /* private: */
        /* internals */
        struct list_head        list;
        struct list_head        functions;
@@ -321,6 +323,7 @@ struct usb_composite_dev {
 
        struct usb_configuration        *config;
 
+       /* private: */
        /* internals */
        struct usb_device_descriptor    desc;
        struct list_head                configs;
diff --git a/include/linux/usb/langwell_otg.h b/include/linux/usb/langwell_otg.h
new file mode 100644 (file)
index 0000000..e115ae6
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Intel Langwell USB OTG transceiver driver
+ * Copyright (C) 2008, Intel Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef __LANGWELL_OTG_H__
+#define __LANGWELL_OTG_H__
+
+/* notify transceiver driver about OTG events */
+extern void langwell_update_transceiver(void);
+/* HCD register bus driver */
+extern int langwell_register_host(struct pci_driver *host_driver);
+/* HCD unregister bus driver */
+extern void langwell_unregister_host(struct pci_driver *host_driver);
+/* DCD register bus driver */
+extern int langwell_register_peripheral(struct pci_driver *client_driver);
+/* DCD unregister bus driver */
+extern void langwell_unregister_peripheral(struct pci_driver *client_driver);
+/* No silent failure, output warning message */
+extern void langwell_otg_nsf_msg(unsigned long message);
+
+#define CI_USBCMD              0x30
+#      define USBCMD_RST               BIT(1)
+#      define USBCMD_RS                BIT(0)
+#define CI_USBSTS              0x34
+#      define USBSTS_SLI               BIT(8)
+#      define USBSTS_URI               BIT(6)
+#      define USBSTS_PCI               BIT(2)
+#define CI_PORTSC1             0x74
+#      define PORTSC_PP                BIT(12)
+#      define PORTSC_LS                (BIT(11) | BIT(10))
+#      define PORTSC_SUSP              BIT(7)
+#      define PORTSC_CCS               BIT(0)
+#define CI_HOSTPC1             0xb4
+#      define HOSTPC1_PHCD             BIT(22)
+#define CI_OTGSC               0xf4
+#      define OTGSC_DPIE               BIT(30)
+#      define OTGSC_1MSE               BIT(29)
+#      define OTGSC_BSEIE              BIT(28)
+#      define OTGSC_BSVIE              BIT(27)
+#      define OTGSC_ASVIE              BIT(26)
+#      define OTGSC_AVVIE              BIT(25)
+#      define OTGSC_IDIE               BIT(24)
+#      define OTGSC_DPIS               BIT(22)
+#      define OTGSC_1MSS               BIT(21)
+#      define OTGSC_BSEIS              BIT(20)
+#      define OTGSC_BSVIS              BIT(19)
+#      define OTGSC_ASVIS              BIT(18)
+#      define OTGSC_AVVIS              BIT(17)
+#      define OTGSC_IDIS               BIT(16)
+#      define OTGSC_DPS                BIT(14)
+#      define OTGSC_1MST               BIT(13)
+#      define OTGSC_BSE                BIT(12)
+#      define OTGSC_BSV                BIT(11)
+#      define OTGSC_ASV                BIT(10)
+#      define OTGSC_AVV                BIT(9)
+#      define OTGSC_ID                 BIT(8)
+#      define OTGSC_HABA               BIT(7)
+#      define OTGSC_HADP               BIT(6)
+#      define OTGSC_IDPU               BIT(5)
+#      define OTGSC_DP                 BIT(4)
+#      define OTGSC_OT                 BIT(3)
+#      define OTGSC_HAAR               BIT(2)
+#      define OTGSC_VC                 BIT(1)
+#      define OTGSC_VD                 BIT(0)
+#      define OTGSC_INTEN_MASK         (0x7f << 24)
+#      define OTGSC_INTSTS_MASK        (0x7f << 16)
+#define CI_USBMODE             0xf8
+#      define USBMODE_CM               (BIT(1) | BIT(0))
+#      define USBMODE_IDLE             0
+#      define USBMODE_DEVICE           0x2
+#      define USBMODE_HOST             0x3
+
+#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI)
+
+struct otg_hsm {
+       /* Input */
+       int a_bus_resume;
+       int a_bus_suspend;
+       int a_conn;
+       int a_sess_vld;
+       int a_srp_det;
+       int a_vbus_vld;
+       int b_bus_resume;
+       int b_bus_suspend;
+       int b_conn;
+       int b_se0_srp;
+       int b_sess_end;
+       int b_sess_vld;
+       int id;
+
+       /* Internal variables */
+       int a_set_b_hnp_en;
+       int b_srp_done;
+       int b_hnp_enable;
+
+       /* Timeout indicator for timers */
+       int a_wait_vrise_tmout;
+       int a_wait_bcon_tmout;
+       int a_aidl_bdis_tmout;
+       int b_ase0_brst_tmout;
+       int b_bus_suspend_tmout;
+       int b_srp_res_tmout;
+
+       /* Informative variables */
+       int a_bus_drop;
+       int a_bus_req;
+       int a_clr_err;
+       int a_suspend_req;
+       int b_bus_req;
+
+       /* Output */
+       int drv_vbus;
+       int loc_conn;
+       int loc_sof;
+
+       /* Others */
+       int b_bus_suspend_vld;
+};
+
+#define TA_WAIT_VRISE  100
+#define TA_WAIT_BCON   30000
+#define TA_AIDL_BDIS   15000
+#define TB_ASE0_BRST   5000
+#define TB_SE0_SRP     2
+#define TB_SRP_RES     100
+#define TB_BUS_SUSPEND 500
+
+struct langwell_otg_timer {
+       unsigned long expires;  /* Number of count increase to timeout */
+       unsigned long count;    /* Tick counter */
+       void (*function)(unsigned long);        /* Timeout function */
+       unsigned long data;     /* Data passed to function */
+       struct list_head list;
+};
+
+struct langwell_otg {
+       struct otg_transceiver  otg;
+       struct otg_hsm          hsm;
+       void __iomem            *regs;
+       unsigned                region;
+       struct pci_driver       *host_ops;
+       struct pci_driver       *client_ops;
+       struct pci_dev          *pdev;
+       struct work_struct      work;
+       struct workqueue_struct *qwork;
+       spinlock_t              lock;
+       spinlock_t              wq_lock;
+};
+
+static inline struct langwell_otg *otg_to_langwell(struct otg_transceiver *otg)
+{
+       return container_of(otg, struct langwell_otg, otg);
+}
+
+#ifdef DEBUG
+#define otg_dbg(fmt, args...) \
+       printk(KERN_DEBUG fmt , ## args)
+#else
+#define otg_dbg(fmt, args...) \
+       do { } while (0)
+#endif /* DEBUG */
+#endif /* __LANGWELL_OTG_H__ */
diff --git a/include/linux/usb/langwell_udc.h b/include/linux/usb/langwell_udc.h
new file mode 100644 (file)
index 0000000..c949178
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Intel Langwell USB Device Controller driver
+ * Copyright (C) 2008-2009, Intel Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef __LANGWELL_UDC_H
+#define __LANGWELL_UDC_H
+
+
+/* MACRO defines */
+#define        CAP_REG_OFFSET          0x0
+#define        OP_REG_OFFSET           0x28
+
+#define        DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
+#define        DQH_ALIGNMENT           2048
+#define        DTD_ALIGNMENT           64
+#define        DMA_BOUNDARY            4096
+
+#define        EP0_MAX_PKT_SIZE        64
+#define EP_DIR_IN              1
+#define EP_DIR_OUT             0
+
+#define FLUSH_TIMEOUT          1000
+#define RESET_TIMEOUT          1000
+#define SETUPSTAT_TIMEOUT      100
+#define PRIME_TIMEOUT          100
+
+
+/* device memory space registers */
+
+/* Capability Registers, BAR0 + CAP_REG_OFFSET */
+struct langwell_cap_regs {
+       /* offset: 0x0 */
+       u8      caplength;      /* offset of Operational Register */
+       u8      _reserved3;
+       u16     hciversion;     /* H: BCD encoding of host version */
+       u32     hcsparams;      /* H: host port steering logic capability */
+       u32     hccparams;      /* H: host multiple mode control capability */
+#define        HCC_LEN BIT(17)         /* Link power management (LPM) capability */
+       u8      _reserved4[0x20-0xc];
+       /* offset: 0x20 */
+       u16     dciversion;     /* BCD encoding of device version */
+       u8      _reserved5[0x24-0x22];
+       u32     dccparams;      /* overall device controller capability */
+#define        HOSTCAP BIT(8)          /* host capable */
+#define        DEVCAP  BIT(7)          /* device capable */
+#define DEN(d) \
+       (((d)>>0)&0x1f)         /* bits 4:0, device endpoint number */
+} __attribute__ ((packed));
+
+
+/* Operational Registers, BAR0 + OP_REG_OFFSET */
+struct langwell_op_regs {
+       /* offset: 0x28 */
+       u32     extsts;
+#define        EXTS_TI1        BIT(4)  /* general purpose timer interrupt 1 */
+#define        EXTS_TI1TI0     BIT(3)  /* general purpose timer interrupt 0 */
+#define        EXTS_TI1UPI     BIT(2)  /* USB host periodic interrupt */
+#define        EXTS_TI1UAI     BIT(1)  /* USB host asynchronous interrupt */
+#define        EXTS_TI1NAKI    BIT(0)  /* NAK interrupt */
+       u32     extintr;
+#define        EXTI_TIE1       BIT(4)  /* general purpose timer interrupt enable 1 */
+#define        EXTI_TIE0       BIT(3)  /* general purpose timer interrupt enable 0 */
+#define        EXTI_UPIE       BIT(2)  /* USB host periodic interrupt enable */
+#define        EXTI_UAIE       BIT(1)  /* USB host asynchronous interrupt enable */
+#define        EXTI_NAKE       BIT(0)  /* NAK interrupt enable */
+       /* offset: 0x30 */
+       u32     usbcmd;
+#define        CMD_HIRD(u)     \
+       (((u)>>24)&0xf)         /* bits 27:24, host init resume duration */
+#define        CMD_ITC(u)      \
+       (((u)>>16)&0xff)        /* bits 23:16, interrupt threshold control */
+#define        CMD_PPE         BIT(15) /* per-port change events enable */
+#define        CMD_ATDTW       BIT(14) /* add dTD tripwire */
+#define        CMD_SUTW        BIT(13) /* setup tripwire */
+#define        CMD_ASPE        BIT(11) /* asynchronous schedule park mode enable */
+#define        CMD_FS2         BIT(10) /* frame list size */
+#define        CMD_ASP1        BIT(9)  /* asynchronous schedule park mode count */
+#define        CMD_ASP0        BIT(8)
+#define        CMD_LR          BIT(7)  /* light host/device controller reset */
+#define        CMD_IAA         BIT(6)  /* interrupt on async advance doorbell */
+#define        CMD_ASE         BIT(5)  /* asynchronous schedule enable */
+#define        CMD_PSE         BIT(4)  /* periodic schedule enable */
+#define        CMD_FS1         BIT(3)
+#define        CMD_FS0         BIT(2)
+#define        CMD_RST         BIT(1)  /* controller reset */
+#define        CMD_RUNSTOP     BIT(0)  /* run/stop */
+       u32     usbsts;
+#define        STS_PPCI(u)     \
+       (((u)>>16)&0xffff)      /* bits 31:16, port-n change detect */
+#define        STS_AS          BIT(15) /* asynchronous schedule status */
+#define        STS_PS          BIT(14) /* periodic schedule status */
+#define        STS_RCL         BIT(13) /* reclamation */
+#define        STS_HCH         BIT(12) /* HC halted */
+#define        STS_ULPII       BIT(10) /* ULPI interrupt */
+#define        STS_SLI         BIT(8)  /* DC suspend */
+#define        STS_SRI         BIT(7)  /* SOF received */
+#define        STS_URI         BIT(6)  /* USB reset received */
+#define        STS_AAI         BIT(5)  /* interrupt on async advance */
+#define        STS_SEI         BIT(4)  /* system error */
+#define        STS_FRI         BIT(3)  /* frame list rollover */
+#define        STS_PCI         BIT(2)  /* port change detect */
+#define        STS_UEI         BIT(1)  /* USB error interrupt */
+#define        STS_UI          BIT(0)  /* USB interrupt */
+       u32     usbintr;
+/* bits 31:16, per-port interrupt enable */
+#define        INTR_PPCE(u)    (((u)>>16)&0xffff)
+#define        INTR_ULPIE      BIT(10) /* ULPI enable */
+#define        INTR_SLE        BIT(8)  /* DC sleep/suspend enable */
+#define        INTR_SRE        BIT(7)  /* SOF received enable */
+#define        INTR_URE        BIT(6)  /* USB reset enable */
+#define        INTR_AAE        BIT(5)  /* interrupt on async advance enable */
+#define        INTR_SEE        BIT(4)  /* system error enable */
+#define        INTR_FRE        BIT(3)  /* frame list rollover enable */
+#define        INTR_PCE        BIT(2)  /* port change detect enable */
+#define        INTR_UEE        BIT(1)  /* USB error interrupt enable */
+#define        INTR_UE         BIT(0)  /* USB interrupt enable */
+       u32     frindex;        /* frame index */
+#define        FRINDEX_MASK    (0x3fff << 0)
+       u32     ctrldssegment;  /* not used */
+       u32     deviceaddr;
+#define USBADR_SHIFT   25
+#define        USBADR(d)       \
+       (((d)>>25)&0x7f)        /* bits 31:25, device address */
+#define USBADR_MASK    (0x7f << 25)
+#define        USBADRA         BIT(24) /* device address advance */
+       u32     endpointlistaddr;/* endpoint list top memory address */
+/* bits 31:11, endpoint list pointer */
+#define        EPBASE(d)       (((d)>>11)&0x1fffff)
+#define        ENDPOINTLISTADDR_MASK   (0x1fffff << 11)
+       u32     ttctrl;         /* H: TT operatin, not used */
+       /* offset: 0x50 */
+       u32     burstsize;      /* burst size of data movement */
+#define        TXPBURST(b)     \
+       (((b)>>8)&0xff)         /* bits 15:8, TX burst length */
+#define        RXPBURST(b)     \
+       (((b)>>0)&0xff)         /* bits 7:0, RX burst length */
+       u32     txfilltuning;   /* TX tuning */
+       u32     txttfilltuning; /* H: TX TT tuning */
+       u32     ic_usb;         /* control the IC_USB FS/LS transceiver */
+       /* offset: 0x60 */
+       u32     ulpi_viewport;  /* indirect access to ULPI PHY */
+#define        ULPIWU          BIT(31) /* ULPI wakeup */
+#define        ULPIRUN         BIT(30) /* ULPI read/write run */
+#define        ULPIRW          BIT(29) /* ULPI read/write control */
+#define        ULPISS          BIT(27) /* ULPI sync state */
+#define        ULPIPORT(u)     \
+       (((u)>>24)&7)           /* bits 26:24, ULPI port number */
+#define        ULPIADDR(u)     \
+       (((u)>>16)&0xff)        /* bits 23:16, ULPI data address */
+#define        ULPIDATRD(u)    \
+       (((u)>>8)&0xff)         /* bits 15:8, ULPI data read */
+#define        ULPIDATWR(u)    \
+       (((u)>>0)&0xff)         /* bits 7:0, ULPI date write */
+       u8      _reserved6[0x70-0x64];
+       /* offset: 0x70 */
+       u32     configflag;     /* H: not used */
+       u32     portsc1;        /* port status */
+#define        DA(p)   \
+       (((p)>>25)&0x7f)        /* bits 31:25, device address */
+#define        PORTS_SSTS      (BIT(24) | BIT(23))     /* suspend status */
+#define        PORTS_WKOC      BIT(22) /* wake on over-current enable */
+#define        PORTS_WKDS      BIT(21) /* wake on disconnect enable */
+#define        PORTS_WKCN      BIT(20) /* wake on connect enable */
+#define        PORTS_PTC(p)    (((p)>>16)&0xf) /* bits 19:16, port test control */
+#define        PORTS_PIC       (BIT(15) | BIT(14))     /* port indicator control */
+#define        PORTS_PO        BIT(13) /* port owner */
+#define        PORTS_PP        BIT(12) /* port power */
+#define        PORTS_LS        (BIT(11) | BIT(10))     /* line status */
+#define        PORTS_SLP       BIT(9)  /* suspend using L1 */
+#define        PORTS_PR        BIT(8)  /* port reset */
+#define        PORTS_SUSP      BIT(7)  /* suspend */
+#define        PORTS_FPR       BIT(6)  /* force port resume */
+#define        PORTS_OCC       BIT(5)  /* over-current change */
+#define        PORTS_OCA       BIT(4)  /* over-current active */
+#define        PORTS_PEC       BIT(3)  /* port enable/disable change */
+#define        PORTS_PE        BIT(2)  /* port enable/disable */
+#define        PORTS_CSC       BIT(1)  /* connect status change */
+#define        PORTS_CCS       BIT(0)  /* current connect status */
+       u8      _reserved7[0xb4-0x78];
+       /* offset: 0xb4 */
+       u32     devlc;          /* control LPM and each USB port behavior */
+/* bits 31:29, parallel transceiver select */
+#define        LPM_PTS(d)      (((d)>>29)&7)
+#define        LPM_STS         BIT(28) /* serial transceiver select */
+#define        LPM_PTW         BIT(27) /* parallel transceiver width */
+#define        LPM_PSPD(d)     (((d)>>25)&3)   /* bits 26:25, port speed */
+#define LPM_PSPD_MASK  (BIT(26) | BIT(25))
+#define LPM_SPEED_FULL 0
+#define LPM_SPEED_LOW  1
+#define LPM_SPEED_HIGH 2
+#define        LPM_SRT         BIT(24) /* shorten reset time */
+#define        LPM_PFSC        BIT(23) /* port force full speed connect */
+#define        LPM_PHCD        BIT(22) /* PHY low power suspend clock disable */
+#define        LPM_STL         BIT(16) /* STALL reply to LPM token */
+#define        LPM_BA(d)       \
+       (((d)>>1)&0x7ff)        /* bits 11:1, BmAttributes */
+#define        LPM_NYT_ACK     BIT(0)  /* NYET/ACK reply to LPM token */
+       u8      _reserved8[0xf4-0xb8];
+       /* offset: 0xf4 */
+       u32     otgsc;          /* On-The-Go status and control */
+#define        OTGSC_DPIE      BIT(30) /* data pulse interrupt enable */
+#define        OTGSC_MSE       BIT(29) /* 1 ms timer interrupt enable */
+#define        OTGSC_BSEIE     BIT(28) /* B session end interrupt enable */
+#define        OTGSC_BSVIE     BIT(27) /* B session valid interrupt enable */
+#define        OTGSC_ASVIE     BIT(26) /* A session valid interrupt enable */
+#define        OTGSC_AVVIE     BIT(25) /* A VBUS valid interrupt enable */
+#define        OTGSC_IDIE      BIT(24) /* USB ID interrupt enable */
+#define        OTGSC_DPIS      BIT(22) /* data pulse interrupt status */
+#define        OTGSC_MSS       BIT(21) /* 1 ms timer interrupt status */
+#define        OTGSC_BSEIS     BIT(20) /* B session end interrupt status */
+#define        OTGSC_BSVIS     BIT(19) /* B session valid interrupt status */
+#define        OTGSC_ASVIS     BIT(18) /* A session valid interrupt status */
+#define        OTGSC_AVVIS     BIT(17) /* A VBUS valid interrupt status */
+#define        OTGSC_IDIS      BIT(16) /* USB ID interrupt status */
+#define        OTGSC_DPS       BIT(14) /* data bus pulsing status */
+#define        OTGSC_MST       BIT(13) /* 1 ms timer toggle */
+#define        OTGSC_BSE       BIT(12) /* B session end */
+#define        OTGSC_BSV       BIT(11) /* B session valid */
+#define        OTGSC_ASV       BIT(10) /* A session valid */
+#define        OTGSC_AVV       BIT(9)  /* A VBUS valid */
+#define        OTGSC_USBID     BIT(8)  /* USB ID */
+#define        OTGSC_HABA      BIT(7)  /* hw assist B-disconnect to A-connect */
+#define        OTGSC_HADP      BIT(6)  /* hw assist data pulse */
+#define        OTGSC_IDPU      BIT(5)  /* ID pullup */
+#define        OTGSC_DP        BIT(4)  /* data pulsing */
+#define        OTGSC_OT        BIT(3)  /* OTG termination */
+#define        OTGSC_HAAR      BIT(2)  /* hw assist auto reset */
+#define        OTGSC_VC        BIT(1)  /* VBUS charge */
+#define        OTGSC_VD        BIT(0)  /* VBUS discharge */
+       u32     usbmode;
+#define        MODE_VBPS       BIT(5)  /* R/W VBUS power select */
+#define        MODE_SDIS       BIT(4)  /* R/W stream disable mode */
+#define        MODE_SLOM       BIT(3)  /* R/W setup lockout mode */
+#define        MODE_ENSE       BIT(2)  /* endian select */
+#define        MODE_CM(u)      (((u)>>0)&3)    /* bits 1:0, controller mode */
+#define        MODE_IDLE       0
+#define        MODE_DEVICE     2
+#define        MODE_HOST       3
+       u8      _reserved9[0x100-0xfc];
+       /* offset: 0x100 */
+       u32     endptnak;
+#define        EPTN(e)         \
+       (((e)>>16)&0xffff)      /* bits 31:16, TX endpoint NAK */
+#define        EPRN(e)         \
+       (((e)>>0)&0xffff)       /* bits 15:0, RX endpoint NAK */
+       u32     endptnaken;
+#define        EPTNE(e)        \
+       (((e)>>16)&0xffff)      /* bits 31:16, TX endpoint NAK enable */
+#define        EPRNE(e)        \
+       (((e)>>0)&0xffff)       /* bits 15:0, RX endpoint NAK enable */
+       u32     endptsetupstat;
+#define        SETUPSTAT_MASK          (0xffff << 0)   /* bits 15:0 */
+#define EP0SETUPSTAT_MASK      1
+       u32     endptprime;
+/* bits 31:16, prime endpoint transmit buffer */
+#define        PETB(e)         (((e)>>16)&0xffff)
+/* bits 15:0, prime endpoint receive buffer */
+#define        PERB(e)         (((e)>>0)&0xffff)
+       /* offset: 0x110 */
+       u32     endptflush;
+/* bits 31:16, flush endpoint transmit buffer */
+#define        FETB(e)         (((e)>>16)&0xffff)
+/* bits 15:0, flush endpoint receive buffer */
+#define        FERB(e)         (((e)>>0)&0xffff)
+       u32     endptstat;
+/* bits 31:16, endpoint transmit buffer ready */
+#define        ETBR(e)         (((e)>>16)&0xffff)
+/* bits 15:0, endpoint receive buffer ready */
+#define        ERBR(e)         (((e)>>0)&0xffff)
+       u32     endptcomplete;
+/* bits 31:16, endpoint transmit complete event */
+#define        ETCE(e)         (((e)>>16)&0xffff)
+/* bits 15:0, endpoint receive complete event */
+#define        ERCE(e)         (((e)>>0)&0xffff)
+       /* offset: 0x11c */
+       u32     endptctrl[16];
+#define        EPCTRL_TXE      BIT(23) /* TX endpoint enable */
+#define        EPCTRL_TXR      BIT(22) /* TX data toggle reset */
+#define        EPCTRL_TXI      BIT(21) /* TX data toggle inhibit */
+#define        EPCTRL_TXT(e)   (((e)>>18)&3)   /* bits 19:18, TX endpoint type */
+#define        EPCTRL_TXT_SHIFT        18
+#define        EPCTRL_TXD      BIT(17) /* TX endpoint data source */
+#define        EPCTRL_TXS      BIT(16) /* TX endpoint STALL */
+#define        EPCTRL_RXE      BIT(7)  /* RX endpoint enable */
+#define        EPCTRL_RXR      BIT(6)  /* RX data toggle reset */
+#define        EPCTRL_RXI      BIT(5)  /* RX data toggle inhibit */
+#define        EPCTRL_RXT(e)   (((e)>>2)&3)    /* bits 3:2, RX endpoint type */
+#define        EPCTRL_RXT_SHIFT        2       /* bits 19:18, TX endpoint type */
+#define        EPCTRL_RXD      BIT(1)  /* RX endpoint data sink */
+#define        EPCTRL_RXS      BIT(0)  /* RX endpoint STALL */
+} __attribute__ ((packed));
+
+#endif /* __LANGWELL_UDC_H */
+
index 1aaa826..2443c0e 100644 (file)
@@ -80,10 +80,10 @@ struct otg_transceiver {
 
 /* for board-specific init logic */
 extern int otg_set_transceiver(struct otg_transceiver *);
-#ifdef CONFIG_NOP_USB_XCEIV
+
+/* sometimes transceivers are accessed only through e.g. ULPI */
 extern void usb_nop_xceiv_register(void);
 extern void usb_nop_xceiv_unregister(void);
-#endif
 
 
 /* for usb host and peripheral controller drivers */
diff --git a/include/linux/usb/r8a66597.h b/include/linux/usb/r8a66597.h
new file mode 100644 (file)
index 0000000..e9f0384
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * R8A66597 driver platform data
+ *
+ * Copyright (C) 2009  Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __LINUX_USB_R8A66597_H
+#define __LINUX_USB_R8A66597_H
+
+#define R8A66597_PLATDATA_XTAL_12MHZ   0x01
+#define R8A66597_PLATDATA_XTAL_24MHZ   0x02
+#define R8A66597_PLATDATA_XTAL_48MHZ   0x03
+
+struct r8a66597_platdata {
+       /* This ops can controll port power instead of DVSTCTR register. */
+       void (*port_power)(int port, int power);
+
+       /* (external controller only) set R8A66597_PLATDATA_XTAL_nnMHZ */
+       unsigned        xtal:2;
+
+       /* set one = 3.3V, set zero = 1.5V */
+       unsigned        vif:1;
+
+       /* set one = big endian, set zero = little endian */
+       unsigned        endian:1;
+};
+#endif
+
index 8cdfed7..44801d2 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/kref.h>
 #include <linux/mutex.h>
+#include <linux/sysrq.h>
 
 #define SERIAL_TTY_MAJOR       188     /* Nice legal number now */
 #define SERIAL_TTY_MINORS      254     /* loads of devices :) */
 /* parity check flag */
 #define RELEVANT_IFLAG(iflag)  (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
 
+enum port_dev_state {
+       PORT_UNREGISTERED,
+       PORT_REGISTERING,
+       PORT_REGISTERED,
+       PORT_UNREGISTERING,
+};
+
 /**
  * usb_serial_port: structure for the specific ports of a device.
  * @serial: pointer back to the struct usb_serial owner of this port.
@@ -91,12 +99,17 @@ struct usb_serial_port {
        int                     write_urb_busy;
        __u8                    bulk_out_endpointAddress;
 
+       int                     tx_bytes_flight;
+       int                     urbs_in_flight;
+
        wait_queue_head_t       write_wait;
        struct work_struct      work;
        char                    throttled;
        char                    throttle_req;
        char                    console;
+       unsigned long           sysrq; /* sysrq timeout */
        struct device           dev;
+       enum port_dev_state     dev_state;
 };
 #define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev)
 
@@ -181,8 +194,10 @@ static inline void usb_set_serial_data(struct usb_serial *serial, void *data)
  *     This will be called when the struct usb_serial structure is fully set
  *     set up.  Do any local initialization of the device, or any private
  *     memory structure allocation at this point in time.
- * @shutdown: pointer to the driver's shutdown function.  This will be
- *     called when the device is removed from the system.
+ * @disconnect: pointer to the driver's disconnect function.  This will be
+ *     called when the device is unplugged or unbound from the driver.
+ * @release: pointer to the driver's release function.  This will be called
+ *     when the usb_serial data structure is about to be destroyed.
  * @usb_driver: pointer to the struct usb_driver that controls this
  *     device.  This is necessary to allow dynamic ids to be added to
  *     the driver from sysfs.
@@ -207,12 +222,14 @@ struct usb_serial_driver {
        struct device_driver    driver;
        struct usb_driver       *usb_driver;
        struct usb_dynids       dynids;
+       int                     max_in_flight_urbs;
 
        int (*probe)(struct usb_serial *serial, const struct usb_device_id *id);
        int (*attach)(struct usb_serial *serial);
        int (*calc_num_ports) (struct usb_serial *serial);
 
-       void (*shutdown)(struct usb_serial *serial);
+       void (*disconnect)(struct usb_serial *serial);
+       void (*release)(struct usb_serial *serial);
 
        int (*port_probe)(struct usb_serial_port *port);
        int (*port_remove)(struct usb_serial_port *port);
@@ -294,9 +311,16 @@ extern void usb_serial_generic_read_bulk_callback(struct urb *urb);
 extern void usb_serial_generic_write_bulk_callback(struct urb *urb);
 extern void usb_serial_generic_throttle(struct tty_struct *tty);
 extern void usb_serial_generic_unthrottle(struct tty_struct *tty);
-extern void usb_serial_generic_shutdown(struct usb_serial *serial);
+extern void usb_serial_generic_disconnect(struct usb_serial *serial);
+extern void usb_serial_generic_release(struct usb_serial *serial);
 extern int usb_serial_generic_register(int debug);
 extern void usb_serial_generic_deregister(void);
+extern void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port,
+                                                gfp_t mem_flags);
+extern int usb_serial_handle_sysrq_char(struct usb_serial_port *port,
+                                       unsigned int ch);
+extern int usb_serial_handle_break(struct usb_serial_port *port);
+
 
 extern int usb_serial_bus_register(struct usb_serial_driver *device);
 extern void usb_serial_bus_deregister(struct usb_serial_driver *device);